From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source4/torture/auth/ntlmssp.c | 163 + source4/torture/auth/pac.c | 741 ++ source4/torture/auth/smbencrypt.c | 70 + source4/torture/basic/aliases.c | 397 + source4/torture/basic/attr.c | 437 + source4/torture/basic/base.c | 2090 ++++ source4/torture/basic/charset.c | 209 + source4/torture/basic/cxd_known.h | 8670 ++++++++++++++ source4/torture/basic/delaywrite.c | 3095 +++++ source4/torture/basic/delete.c | 2624 +++++ source4/torture/basic/denytest.c | 2819 +++++ source4/torture/basic/dir.c | 171 + source4/torture/basic/disconnect.c | 182 + source4/torture/basic/locking.c | 811 ++ source4/torture/basic/mangle_test.c | 208 + source4/torture/basic/misc.c | 1003 ++ source4/torture/basic/properties.c | 118 + source4/torture/basic/rename.c | 98 + source4/torture/basic/scanner.c | 623 + source4/torture/basic/secleak.c | 77 + source4/torture/basic/unlink.c | 91 + source4/torture/basic/utable.c | 202 + source4/torture/dfs/common.c | 71 + source4/torture/dfs/domaindfs.c | 540 + source4/torture/dns/dlz_bind9.c | 2514 ++++ source4/torture/dns/internal_dns.c | 189 + source4/torture/dns/wscript_build | 19 + source4/torture/drs/drs_init.c | 80 + source4/torture/drs/drs_util.c | 167 + source4/torture/drs/python/cracknames.py | 204 + source4/torture/drs/python/delete_object.py | 376 + source4/torture/drs/python/drs_base.py | 632 + source4/torture/drs/python/fsmo.py | 152 + source4/torture/drs/python/getnc_exop.py | 1304 +++ source4/torture/drs/python/getnc_schema.py | 304 + source4/torture/drs/python/getnc_unpriv.py | 306 + source4/torture/drs/python/getncchanges.py | 1427 +++ source4/torture/drs/python/link_conflicts.py | 763 ++ .../torture/drs/python/linked_attributes_drs.py | 162 + source4/torture/drs/python/repl_move.py | 2608 +++++ source4/torture/drs/python/repl_rodc.py | 735 ++ source4/torture/drs/python/repl_schema.py | 444 + source4/torture/drs/python/repl_secdesc.py | 400 + source4/torture/drs/python/replica_sync.py | 747 ++ source4/torture/drs/python/replica_sync_rodc.py | 155 + source4/torture/drs/python/ridalloc_exop.py | 802 ++ source4/torture/drs/python/samba_tool_drs.py | 410 + .../torture/drs/python/samba_tool_drs_critical.py | 98 + .../torture/drs/python/samba_tool_drs_no_dns.py | 174 + .../torture/drs/python/samba_tool_drs_showrepl.py | 377 + source4/torture/drs/rpc/dssync.c | 1072 ++ source4/torture/drs/rpc/msds_intid.c | 792 ++ source4/torture/drs/unit/prefixmap_tests.c | 900 ++ source4/torture/drs/unit/schemainfo_tests.c | 740 ++ source4/torture/drs/wscript_build | 12 + source4/torture/gentest.c | 3463 ++++++ source4/torture/gpo/apply.c | 390 + source4/torture/gpo/gpo.c | 35 + source4/torture/gpo/wscript_build | 13 + source4/torture/krb5/kdc-canon-heimdal.c | 1091 ++ source4/torture/krb5/kdc-heimdal.c | 1065 ++ source4/torture/krb5/kdc-mit.c | 795 ++ source4/torture/krb5/wscript_build | 19 + source4/torture/ldap/basic.c | 1004 ++ source4/torture/ldap/cldap.c | 179 + source4/torture/ldap/cldapbench.c | 233 + source4/torture/ldap/common.c | 135 + source4/torture/ldap/ldap_sort.c | 158 + source4/torture/ldap/nested_search.c | 206 + source4/torture/ldap/netlogon.c | 668 ++ source4/torture/ldap/schema.c | 408 + source4/torture/ldap/session_expiry.c | 122 + source4/torture/ldap/uptodatevector.c | 173 + source4/torture/ldb/ldb.c | 1794 +++ source4/torture/libnet/domain.c | 117 + source4/torture/libnet/groupinfo.c | 128 + source4/torture/libnet/groupman.c | 97 + source4/torture/libnet/grouptest.h | 20 + source4/torture/libnet/libnet.c | 70 + source4/torture/libnet/libnet_BecomeDC.c | 191 + source4/torture/libnet/libnet_domain.c | 440 + source4/torture/libnet/libnet_group.c | 210 + source4/torture/libnet/libnet_lookup.c | 191 + source4/torture/libnet/libnet_rpc.c | 230 + source4/torture/libnet/libnet_share.c | 285 + source4/torture/libnet/libnet_user.c | 520 + source4/torture/libnet/python/samr-test.py | 59 + source4/torture/libnet/userinfo.c | 192 + source4/torture/libnet/userman.c | 473 + source4/torture/libnet/usertest.h | 42 + source4/torture/libnet/utils.c | 556 + source4/torture/libnetapi/libnetapi.c | 94 + source4/torture/libnetapi/libnetapi_group.c | 522 + source4/torture/libnetapi/libnetapi_server.c | 76 + source4/torture/libnetapi/libnetapi_user.c | 487 + source4/torture/libnetapi/wscript_build | 11 + source4/torture/libsmbclient/libsmbclient.c | 1617 +++ source4/torture/libsmbclient/wscript_build | 14 + source4/torture/local/dbspeed.c | 268 + source4/torture/local/fsrvp_state.c | 492 + source4/torture/local/local.c | 105 + source4/torture/local/mdspkt.c | 104 + source4/torture/local/nss_tests.c | 1061 ++ source4/torture/local/smbtorture_fullname.c | 31 + source4/torture/local/torture.c | 85 + source4/torture/local/verif_trailer.c | 99 + source4/torture/local/wscript_build | 45 + source4/torture/locktest.c | 700 ++ source4/torture/man/gentest.1.xml | 162 + source4/torture/man/locktest.1.xml | 160 + source4/torture/man/masktest.1.xml | 142 + source4/torture/man/smbtorture.1.xml | 253 + source4/torture/masktest.c | 418 + source4/torture/nbench/nbench.c | 309 + source4/torture/nbench/nbio.c | 994 ++ source4/torture/nbt/dgram.c | 699 ++ source4/torture/nbt/nbt.c | 69 + source4/torture/nbt/query.c | 115 + source4/torture/nbt/register.c | 176 + source4/torture/nbt/wins.c | 545 + source4/torture/nbt/winsbench.c | 300 + source4/torture/nbt/winsreplication.c | 9884 ++++++++++++++++ source4/torture/ndr/README | 21 + source4/torture/ndr/atsvc.c | 215 + source4/torture/ndr/backupkey.c | 163 + source4/torture/ndr/cabinet.c | 4335 +++++++ source4/torture/ndr/charset.c | 91 + source4/torture/ndr/clusapi.c | 390 + source4/torture/ndr/dcerpc.c | 148 + source4/torture/ndr/dfs.c | 115 + source4/torture/ndr/dfsblob.c | 85 + source4/torture/ndr/dnsp.c | 389 + source4/torture/ndr/drsblobs.c | 558 + source4/torture/ndr/drsuapi.c | 309 + source4/torture/ndr/epmap.c | 80 + source4/torture/ndr/krb5pac.c | 705 ++ source4/torture/ndr/lsa.c | 2229 ++++ source4/torture/ndr/nbt.c | 253 + source4/torture/ndr/ndr.c | 831 ++ source4/torture/ndr/ndr.h | 249 + source4/torture/ndr/negoex.c | 100 + source4/torture/ndr/netlogon.c | 825 ++ source4/torture/ndr/ntlmssp.c | 296 + source4/torture/ndr/ntprinting.c | 655 ++ source4/torture/ndr/odj.c | 210 + source4/torture/ndr/samr.c | 355 + source4/torture/ndr/spoolss.c | 2064 ++++ source4/torture/ndr/string.c | 223 + source4/torture/ndr/svcctl.c | 88 + source4/torture/ndr/winreg.c | 620 + source4/torture/ndr/winspool.c | 173 + source4/torture/ndr/witness.c | 411 + source4/torture/ntp/ntp_signd.c | 306 + source4/torture/rap/printing.c | 711 ++ source4/torture/rap/rap.c | 275 + source4/torture/rap/rpc.c | 100 + source4/torture/rap/sam.c | 376 + source4/torture/raw/acls.c | 2556 ++++ source4/torture/raw/chkpath.c | 390 + source4/torture/raw/close.c | 178 + source4/torture/raw/composite.c | 417 + source4/torture/raw/context.c | 893 ++ source4/torture/raw/eas.c | 594 + source4/torture/raw/ioctl.c | 191 + source4/torture/raw/lock.c | 3586 ++++++ source4/torture/raw/lockbench.c | 447 + source4/torture/raw/lookuprate.c | 318 + source4/torture/raw/missing.txt | 160 + source4/torture/raw/mkdir.c | 171 + source4/torture/raw/mux.c | 342 + source4/torture/raw/notify.c | 2297 ++++ source4/torture/raw/offline.c | 514 + source4/torture/raw/open.c | 2253 ++++ source4/torture/raw/openbench.c | 502 + source4/torture/raw/oplock.c | 4672 ++++++++ source4/torture/raw/pingpong.c | 248 + source4/torture/raw/qfileinfo.c | 1084 ++ source4/torture/raw/qfsinfo.c | 340 + source4/torture/raw/raw.c | 87 + source4/torture/raw/read.c | 1039 ++ source4/torture/raw/rename.c | 692 ++ source4/torture/raw/samba3hide.c | 326 + source4/torture/raw/samba3misc.c | 1137 ++ source4/torture/raw/search.c | 1653 +++ source4/torture/raw/seek.c | 242 + source4/torture/raw/session.c | 446 + source4/torture/raw/setfileinfo.c | 1152 ++ source4/torture/raw/streams.c | 2091 ++++ source4/torture/raw/tconrate.c | 208 + source4/torture/raw/unlink.c | 470 + source4/torture/raw/write.c | 799 ++ source4/torture/rpc/alter_context.c | 112 + source4/torture/rpc/async_bind.c | 86 + source4/torture/rpc/atsvc.c | 138 + source4/torture/rpc/backupkey.c | 2431 ++++ source4/torture/rpc/bench.c | 152 + source4/torture/rpc/bind.c | 245 + source4/torture/rpc/browser.c | 124 + source4/torture/rpc/clusapi.c | 4185 +++++++ source4/torture/rpc/countcalls.c | 131 + source4/torture/rpc/dfs.c | 651 ++ source4/torture/rpc/drsuapi.c | 1049 ++ source4/torture/rpc/drsuapi.h | 94 + source4/torture/rpc/drsuapi_cracknames.c | 1087 ++ source4/torture/rpc/drsuapi_w2k8.c | 334 + source4/torture/rpc/dsgetinfo.c | 452 + source4/torture/rpc/dssetup.c | 64 + source4/torture/rpc/echo.c | 474 + source4/torture/rpc/epmapper.c | 679 ++ source4/torture/rpc/eventlog.c | 501 + source4/torture/rpc/forest_trust.c | 914 ++ source4/torture/rpc/frsapi.c | 276 + source4/torture/rpc/fsrvp.c | 973 ++ source4/torture/rpc/handles.c | 622 + source4/torture/rpc/initshutdown.c | 116 + source4/torture/rpc/iremotewinspool.c | 1090 ++ source4/torture/rpc/iremotewinspool_common.c | 269 + source4/torture/rpc/iremotewinspool_common.h | 110 + source4/torture/rpc/iremotewinspool_driver.c | 840 ++ source4/torture/rpc/join.c | 86 + source4/torture/rpc/lsa.c | 5645 +++++++++ source4/torture/rpc/lsa_lookup.c | 428 + source4/torture/rpc/mdssvc.c | 1056 ++ source4/torture/rpc/mgmt.c | 328 + source4/torture/rpc/netlogon.c | 6038 ++++++++++ source4/torture/rpc/netlogon.h | 37 + source4/torture/rpc/netlogon_crypto.c | 274 + source4/torture/rpc/ntsvcs.c | 189 + source4/torture/rpc/object_uuid.c | 85 + source4/torture/rpc/remote_pac.c | 1425 +++ source4/torture/rpc/rpc.c | 661 ++ source4/torture/rpc/samba3rpc.c | 4729 ++++++++ source4/torture/rpc/samlogon.c | 2121 ++++ source4/torture/rpc/samr.c | 9466 +++++++++++++++ source4/torture/rpc/samr_accessmask.c | 1197 ++ source4/torture/rpc/samr_handletype.c | 217 + source4/torture/rpc/samr_priv.c | 580 + source4/torture/rpc/samsync.c | 1798 +++ source4/torture/rpc/scanner.c | 187 + source4/torture/rpc/schannel.c | 1338 +++ source4/torture/rpc/session_key.c | 250 + source4/torture/rpc/spoolss.c | 11705 +++++++++++++++++++ source4/torture/rpc/spoolss_access.c | 905 ++ source4/torture/rpc/spoolss_notify.c | 636 + source4/torture/rpc/spoolss_win.c | 612 + source4/torture/rpc/srvsvc.c | 1206 ++ source4/torture/rpc/svcctl.c | 828 ++ source4/torture/rpc/testjoin.c | 915 ++ source4/torture/rpc/torture_rpc.h | 126 + source4/torture/rpc/unixinfo.c | 149 + source4/torture/rpc/winreg.c | 3335 ++++++ source4/torture/rpc/witness.c | 911 ++ source4/torture/rpc/wkssvc.c | 1458 +++ source4/torture/shell.c | 326 + source4/torture/smb2/acls.c | 3340 ++++++ source4/torture/smb2/attr.c | 710 ++ source4/torture/smb2/bench.c | 1376 +++ source4/torture/smb2/block.c | 446 + source4/torture/smb2/block.h | 43 + source4/torture/smb2/charset.c | 235 + source4/torture/smb2/compound.c | 2595 ++++ source4/torture/smb2/connect.c | 257 + source4/torture/smb2/create.c | 3629 ++++++ source4/torture/smb2/credits.c | 268 + source4/torture/smb2/delete-on-close.c | 762 ++ source4/torture/smb2/deny.c | 526 + source4/torture/smb2/dir.c | 1606 +++ source4/torture/smb2/dosmode.c | 254 + source4/torture/smb2/durable_open.c | 2872 +++++ source4/torture/smb2/durable_v2_open.c | 2371 ++++ source4/torture/smb2/ea.c | 152 + source4/torture/smb2/getinfo.c | 951 ++ source4/torture/smb2/ioctl.c | 7552 ++++++++++++ source4/torture/smb2/lease.c | 4819 ++++++++ source4/torture/smb2/lease_break_handler.c | 161 + source4/torture/smb2/lease_break_handler.h | 134 + source4/torture/smb2/lock.c | 3513 ++++++ source4/torture/smb2/mangle.c | 341 + source4/torture/smb2/max_allowed.c | 248 + source4/torture/smb2/maxfid.c | 149 + source4/torture/smb2/maxwrite.c | 137 + source4/torture/smb2/mkdir.c | 105 + source4/torture/smb2/multichannel.c | 2743 +++++ source4/torture/smb2/notify.c | 2786 +++++ source4/torture/smb2/notify_disabled.c | 120 + source4/torture/smb2/oplock.c | 5405 +++++++++ source4/torture/smb2/oplock_break_handler.c | 169 + source4/torture/smb2/oplock_break_handler.h | 57 + source4/torture/smb2/read.c | 573 + source4/torture/smb2/read_write.c | 361 + source4/torture/smb2/rename.c | 1751 +++ source4/torture/smb2/replay.c | 5515 +++++++++ source4/torture/smb2/samba3misc.c | 189 + source4/torture/smb2/scan.c | 265 + source4/torture/smb2/secleak.c | 91 + source4/torture/smb2/sessid.c | 100 + source4/torture/smb2/session.c | 5670 +++++++++ source4/torture/smb2/setinfo.c | 410 + source4/torture/smb2/sharemode.c | 755 ++ source4/torture/smb2/smb2.c | 230 + source4/torture/smb2/streams.c | 2424 ++++ source4/torture/smb2/tcon.c | 146 + source4/torture/smb2/timestamps.c | 1344 +++ source4/torture/smb2/util.c | 1045 ++ source4/torture/smb2/wscript_build | 59 + source4/torture/smbtorture.c | 770 ++ source4/torture/smbtorture.h | 146 + source4/torture/tests/test_gentest.sh | 35 + source4/torture/tests/test_locktest.sh | 28 + source4/torture/tests/test_masktest.sh | 28 + source4/torture/torture.c | 59 + source4/torture/unix/unix.c | 40 + source4/torture/unix/unix_info2.c | 503 + source4/torture/unix/whoami.c | 433 + source4/torture/util.h | 121 + source4/torture/util_smb.c | 1020 ++ source4/torture/vfs/acl_xattr.c | 281 + source4/torture/vfs/fruit.c | 8839 ++++++++++++++ source4/torture/vfs/vfs.c | 123 + source4/torture/winbind/struct_based.c | 1190 ++ source4/torture/winbind/winbind.c | 335 + source4/torture/winbind/wscript_build | 10 + source4/torture/wscript_build | 361 + 323 files changed, 297361 insertions(+) create mode 100644 source4/torture/auth/ntlmssp.c create mode 100644 source4/torture/auth/pac.c create mode 100644 source4/torture/auth/smbencrypt.c create mode 100644 source4/torture/basic/aliases.c create mode 100644 source4/torture/basic/attr.c create mode 100644 source4/torture/basic/base.c create mode 100644 source4/torture/basic/charset.c create mode 100644 source4/torture/basic/cxd_known.h create mode 100644 source4/torture/basic/delaywrite.c create mode 100644 source4/torture/basic/delete.c create mode 100644 source4/torture/basic/denytest.c create mode 100644 source4/torture/basic/dir.c create mode 100644 source4/torture/basic/disconnect.c create mode 100644 source4/torture/basic/locking.c create mode 100644 source4/torture/basic/mangle_test.c create mode 100644 source4/torture/basic/misc.c create mode 100644 source4/torture/basic/properties.c create mode 100644 source4/torture/basic/rename.c create mode 100644 source4/torture/basic/scanner.c create mode 100644 source4/torture/basic/secleak.c create mode 100644 source4/torture/basic/unlink.c create mode 100644 source4/torture/basic/utable.c create mode 100644 source4/torture/dfs/common.c create mode 100644 source4/torture/dfs/domaindfs.c create mode 100644 source4/torture/dns/dlz_bind9.c create mode 100644 source4/torture/dns/internal_dns.c create mode 100644 source4/torture/dns/wscript_build create mode 100644 source4/torture/drs/drs_init.c create mode 100644 source4/torture/drs/drs_util.c create mode 100644 source4/torture/drs/python/cracknames.py create mode 100644 source4/torture/drs/python/delete_object.py create mode 100644 source4/torture/drs/python/drs_base.py create mode 100644 source4/torture/drs/python/fsmo.py create mode 100644 source4/torture/drs/python/getnc_exop.py create mode 100644 source4/torture/drs/python/getnc_schema.py create mode 100644 source4/torture/drs/python/getnc_unpriv.py create mode 100644 source4/torture/drs/python/getncchanges.py create mode 100644 source4/torture/drs/python/link_conflicts.py create mode 100644 source4/torture/drs/python/linked_attributes_drs.py create mode 100644 source4/torture/drs/python/repl_move.py create mode 100644 source4/torture/drs/python/repl_rodc.py create mode 100644 source4/torture/drs/python/repl_schema.py create mode 100644 source4/torture/drs/python/repl_secdesc.py create mode 100644 source4/torture/drs/python/replica_sync.py create mode 100644 source4/torture/drs/python/replica_sync_rodc.py create mode 100644 source4/torture/drs/python/ridalloc_exop.py create mode 100644 source4/torture/drs/python/samba_tool_drs.py create mode 100644 source4/torture/drs/python/samba_tool_drs_critical.py create mode 100644 source4/torture/drs/python/samba_tool_drs_no_dns.py create mode 100644 source4/torture/drs/python/samba_tool_drs_showrepl.py create mode 100644 source4/torture/drs/rpc/dssync.c create mode 100644 source4/torture/drs/rpc/msds_intid.c create mode 100644 source4/torture/drs/unit/prefixmap_tests.c create mode 100644 source4/torture/drs/unit/schemainfo_tests.c create mode 100644 source4/torture/drs/wscript_build create mode 100644 source4/torture/gentest.c create mode 100644 source4/torture/gpo/apply.c create mode 100644 source4/torture/gpo/gpo.c create mode 100644 source4/torture/gpo/wscript_build create mode 100644 source4/torture/krb5/kdc-canon-heimdal.c create mode 100644 source4/torture/krb5/kdc-heimdal.c create mode 100644 source4/torture/krb5/kdc-mit.c create mode 100644 source4/torture/krb5/wscript_build create mode 100644 source4/torture/ldap/basic.c create mode 100644 source4/torture/ldap/cldap.c create mode 100644 source4/torture/ldap/cldapbench.c create mode 100644 source4/torture/ldap/common.c create mode 100644 source4/torture/ldap/ldap_sort.c create mode 100644 source4/torture/ldap/nested_search.c create mode 100644 source4/torture/ldap/netlogon.c create mode 100644 source4/torture/ldap/schema.c create mode 100644 source4/torture/ldap/session_expiry.c create mode 100644 source4/torture/ldap/uptodatevector.c create mode 100644 source4/torture/ldb/ldb.c create mode 100644 source4/torture/libnet/domain.c create mode 100644 source4/torture/libnet/groupinfo.c create mode 100644 source4/torture/libnet/groupman.c create mode 100644 source4/torture/libnet/grouptest.h create mode 100644 source4/torture/libnet/libnet.c create mode 100644 source4/torture/libnet/libnet_BecomeDC.c create mode 100644 source4/torture/libnet/libnet_domain.c create mode 100644 source4/torture/libnet/libnet_group.c create mode 100644 source4/torture/libnet/libnet_lookup.c create mode 100644 source4/torture/libnet/libnet_rpc.c create mode 100644 source4/torture/libnet/libnet_share.c create mode 100644 source4/torture/libnet/libnet_user.c create mode 100644 source4/torture/libnet/python/samr-test.py create mode 100644 source4/torture/libnet/userinfo.c create mode 100644 source4/torture/libnet/userman.c create mode 100644 source4/torture/libnet/usertest.h create mode 100644 source4/torture/libnet/utils.c create mode 100644 source4/torture/libnetapi/libnetapi.c create mode 100644 source4/torture/libnetapi/libnetapi_group.c create mode 100644 source4/torture/libnetapi/libnetapi_server.c create mode 100644 source4/torture/libnetapi/libnetapi_user.c create mode 100644 source4/torture/libnetapi/wscript_build create mode 100644 source4/torture/libsmbclient/libsmbclient.c create mode 100644 source4/torture/libsmbclient/wscript_build create mode 100644 source4/torture/local/dbspeed.c create mode 100644 source4/torture/local/fsrvp_state.c create mode 100644 source4/torture/local/local.c create mode 100644 source4/torture/local/mdspkt.c create mode 100644 source4/torture/local/nss_tests.c create mode 100644 source4/torture/local/smbtorture_fullname.c create mode 100644 source4/torture/local/torture.c create mode 100644 source4/torture/local/verif_trailer.c create mode 100644 source4/torture/local/wscript_build create mode 100644 source4/torture/locktest.c create mode 100644 source4/torture/man/gentest.1.xml create mode 100644 source4/torture/man/locktest.1.xml create mode 100644 source4/torture/man/masktest.1.xml create mode 100644 source4/torture/man/smbtorture.1.xml create mode 100644 source4/torture/masktest.c create mode 100644 source4/torture/nbench/nbench.c create mode 100644 source4/torture/nbench/nbio.c create mode 100644 source4/torture/nbt/dgram.c create mode 100644 source4/torture/nbt/nbt.c create mode 100644 source4/torture/nbt/query.c create mode 100644 source4/torture/nbt/register.c create mode 100644 source4/torture/nbt/wins.c create mode 100644 source4/torture/nbt/winsbench.c create mode 100644 source4/torture/nbt/winsreplication.c create mode 100644 source4/torture/ndr/README create mode 100644 source4/torture/ndr/atsvc.c create mode 100644 source4/torture/ndr/backupkey.c create mode 100644 source4/torture/ndr/cabinet.c create mode 100644 source4/torture/ndr/charset.c create mode 100644 source4/torture/ndr/clusapi.c create mode 100644 source4/torture/ndr/dcerpc.c create mode 100644 source4/torture/ndr/dfs.c create mode 100644 source4/torture/ndr/dfsblob.c create mode 100644 source4/torture/ndr/dnsp.c create mode 100644 source4/torture/ndr/drsblobs.c create mode 100644 source4/torture/ndr/drsuapi.c create mode 100644 source4/torture/ndr/epmap.c create mode 100644 source4/torture/ndr/krb5pac.c create mode 100644 source4/torture/ndr/lsa.c create mode 100644 source4/torture/ndr/nbt.c create mode 100644 source4/torture/ndr/ndr.c create mode 100644 source4/torture/ndr/ndr.h create mode 100644 source4/torture/ndr/negoex.c create mode 100644 source4/torture/ndr/netlogon.c create mode 100644 source4/torture/ndr/ntlmssp.c create mode 100644 source4/torture/ndr/ntprinting.c create mode 100644 source4/torture/ndr/odj.c create mode 100644 source4/torture/ndr/samr.c create mode 100644 source4/torture/ndr/spoolss.c create mode 100644 source4/torture/ndr/string.c create mode 100644 source4/torture/ndr/svcctl.c create mode 100644 source4/torture/ndr/winreg.c create mode 100644 source4/torture/ndr/winspool.c create mode 100644 source4/torture/ndr/witness.c create mode 100644 source4/torture/ntp/ntp_signd.c create mode 100644 source4/torture/rap/printing.c create mode 100644 source4/torture/rap/rap.c create mode 100644 source4/torture/rap/rpc.c create mode 100644 source4/torture/rap/sam.c create mode 100644 source4/torture/raw/acls.c create mode 100644 source4/torture/raw/chkpath.c create mode 100644 source4/torture/raw/close.c create mode 100644 source4/torture/raw/composite.c create mode 100644 source4/torture/raw/context.c create mode 100644 source4/torture/raw/eas.c create mode 100644 source4/torture/raw/ioctl.c create mode 100644 source4/torture/raw/lock.c create mode 100644 source4/torture/raw/lockbench.c create mode 100644 source4/torture/raw/lookuprate.c create mode 100644 source4/torture/raw/missing.txt create mode 100644 source4/torture/raw/mkdir.c create mode 100644 source4/torture/raw/mux.c create mode 100644 source4/torture/raw/notify.c create mode 100644 source4/torture/raw/offline.c create mode 100644 source4/torture/raw/open.c create mode 100644 source4/torture/raw/openbench.c create mode 100644 source4/torture/raw/oplock.c create mode 100644 source4/torture/raw/pingpong.c create mode 100644 source4/torture/raw/qfileinfo.c create mode 100644 source4/torture/raw/qfsinfo.c create mode 100644 source4/torture/raw/raw.c create mode 100644 source4/torture/raw/read.c create mode 100644 source4/torture/raw/rename.c create mode 100644 source4/torture/raw/samba3hide.c create mode 100644 source4/torture/raw/samba3misc.c create mode 100644 source4/torture/raw/search.c create mode 100644 source4/torture/raw/seek.c create mode 100644 source4/torture/raw/session.c create mode 100644 source4/torture/raw/setfileinfo.c create mode 100644 source4/torture/raw/streams.c create mode 100644 source4/torture/raw/tconrate.c create mode 100644 source4/torture/raw/unlink.c create mode 100644 source4/torture/raw/write.c create mode 100644 source4/torture/rpc/alter_context.c create mode 100644 source4/torture/rpc/async_bind.c create mode 100644 source4/torture/rpc/atsvc.c create mode 100644 source4/torture/rpc/backupkey.c create mode 100644 source4/torture/rpc/bench.c create mode 100644 source4/torture/rpc/bind.c create mode 100644 source4/torture/rpc/browser.c create mode 100644 source4/torture/rpc/clusapi.c create mode 100644 source4/torture/rpc/countcalls.c create mode 100644 source4/torture/rpc/dfs.c create mode 100644 source4/torture/rpc/drsuapi.c create mode 100644 source4/torture/rpc/drsuapi.h create mode 100644 source4/torture/rpc/drsuapi_cracknames.c create mode 100644 source4/torture/rpc/drsuapi_w2k8.c create mode 100644 source4/torture/rpc/dsgetinfo.c create mode 100644 source4/torture/rpc/dssetup.c create mode 100644 source4/torture/rpc/echo.c create mode 100644 source4/torture/rpc/epmapper.c create mode 100644 source4/torture/rpc/eventlog.c create mode 100644 source4/torture/rpc/forest_trust.c create mode 100644 source4/torture/rpc/frsapi.c create mode 100644 source4/torture/rpc/fsrvp.c create mode 100644 source4/torture/rpc/handles.c create mode 100644 source4/torture/rpc/initshutdown.c create mode 100644 source4/torture/rpc/iremotewinspool.c create mode 100644 source4/torture/rpc/iremotewinspool_common.c create mode 100644 source4/torture/rpc/iremotewinspool_common.h create mode 100644 source4/torture/rpc/iremotewinspool_driver.c create mode 100644 source4/torture/rpc/join.c create mode 100644 source4/torture/rpc/lsa.c create mode 100644 source4/torture/rpc/lsa_lookup.c create mode 100644 source4/torture/rpc/mdssvc.c create mode 100644 source4/torture/rpc/mgmt.c create mode 100644 source4/torture/rpc/netlogon.c create mode 100644 source4/torture/rpc/netlogon.h create mode 100644 source4/torture/rpc/netlogon_crypto.c create mode 100644 source4/torture/rpc/ntsvcs.c create mode 100644 source4/torture/rpc/object_uuid.c create mode 100644 source4/torture/rpc/remote_pac.c create mode 100644 source4/torture/rpc/rpc.c create mode 100644 source4/torture/rpc/samba3rpc.c create mode 100644 source4/torture/rpc/samlogon.c create mode 100644 source4/torture/rpc/samr.c create mode 100644 source4/torture/rpc/samr_accessmask.c create mode 100644 source4/torture/rpc/samr_handletype.c create mode 100644 source4/torture/rpc/samr_priv.c create mode 100644 source4/torture/rpc/samsync.c create mode 100644 source4/torture/rpc/scanner.c create mode 100644 source4/torture/rpc/schannel.c create mode 100644 source4/torture/rpc/session_key.c create mode 100644 source4/torture/rpc/spoolss.c create mode 100644 source4/torture/rpc/spoolss_access.c create mode 100644 source4/torture/rpc/spoolss_notify.c create mode 100644 source4/torture/rpc/spoolss_win.c create mode 100644 source4/torture/rpc/srvsvc.c create mode 100644 source4/torture/rpc/svcctl.c create mode 100644 source4/torture/rpc/testjoin.c create mode 100644 source4/torture/rpc/torture_rpc.h create mode 100644 source4/torture/rpc/unixinfo.c create mode 100644 source4/torture/rpc/winreg.c create mode 100644 source4/torture/rpc/witness.c create mode 100644 source4/torture/rpc/wkssvc.c create mode 100644 source4/torture/shell.c create mode 100644 source4/torture/smb2/acls.c create mode 100644 source4/torture/smb2/attr.c create mode 100644 source4/torture/smb2/bench.c create mode 100644 source4/torture/smb2/block.c create mode 100644 source4/torture/smb2/block.h create mode 100644 source4/torture/smb2/charset.c create mode 100644 source4/torture/smb2/compound.c create mode 100644 source4/torture/smb2/connect.c create mode 100644 source4/torture/smb2/create.c create mode 100644 source4/torture/smb2/credits.c create mode 100644 source4/torture/smb2/delete-on-close.c create mode 100644 source4/torture/smb2/deny.c create mode 100644 source4/torture/smb2/dir.c create mode 100644 source4/torture/smb2/dosmode.c create mode 100644 source4/torture/smb2/durable_open.c create mode 100644 source4/torture/smb2/durable_v2_open.c create mode 100644 source4/torture/smb2/ea.c create mode 100644 source4/torture/smb2/getinfo.c create mode 100644 source4/torture/smb2/ioctl.c create mode 100644 source4/torture/smb2/lease.c create mode 100644 source4/torture/smb2/lease_break_handler.c create mode 100644 source4/torture/smb2/lease_break_handler.h create mode 100644 source4/torture/smb2/lock.c create mode 100644 source4/torture/smb2/mangle.c create mode 100644 source4/torture/smb2/max_allowed.c create mode 100644 source4/torture/smb2/maxfid.c create mode 100644 source4/torture/smb2/maxwrite.c create mode 100644 source4/torture/smb2/mkdir.c create mode 100644 source4/torture/smb2/multichannel.c create mode 100644 source4/torture/smb2/notify.c create mode 100644 source4/torture/smb2/notify_disabled.c create mode 100644 source4/torture/smb2/oplock.c create mode 100644 source4/torture/smb2/oplock_break_handler.c create mode 100644 source4/torture/smb2/oplock_break_handler.h create mode 100644 source4/torture/smb2/read.c create mode 100644 source4/torture/smb2/read_write.c create mode 100644 source4/torture/smb2/rename.c create mode 100644 source4/torture/smb2/replay.c create mode 100644 source4/torture/smb2/samba3misc.c create mode 100644 source4/torture/smb2/scan.c create mode 100644 source4/torture/smb2/secleak.c create mode 100644 source4/torture/smb2/sessid.c create mode 100644 source4/torture/smb2/session.c create mode 100644 source4/torture/smb2/setinfo.c create mode 100644 source4/torture/smb2/sharemode.c create mode 100644 source4/torture/smb2/smb2.c create mode 100644 source4/torture/smb2/streams.c create mode 100644 source4/torture/smb2/tcon.c create mode 100644 source4/torture/smb2/timestamps.c create mode 100644 source4/torture/smb2/util.c create mode 100644 source4/torture/smb2/wscript_build create mode 100644 source4/torture/smbtorture.c create mode 100644 source4/torture/smbtorture.h create mode 100755 source4/torture/tests/test_gentest.sh create mode 100755 source4/torture/tests/test_locktest.sh create mode 100755 source4/torture/tests/test_masktest.sh create mode 100644 source4/torture/torture.c create mode 100644 source4/torture/unix/unix.c create mode 100644 source4/torture/unix/unix_info2.c create mode 100644 source4/torture/unix/whoami.c create mode 100644 source4/torture/util.h create mode 100644 source4/torture/util_smb.c create mode 100644 source4/torture/vfs/acl_xattr.c create mode 100644 source4/torture/vfs/fruit.c create mode 100644 source4/torture/vfs/vfs.c create mode 100644 source4/torture/winbind/struct_based.c create mode 100644 source4/torture/winbind/winbind.c create mode 100644 source4/torture/winbind/wscript_build create mode 100644 source4/torture/wscript_build (limited to 'source4/torture') diff --git a/source4/torture/auth/ntlmssp.c b/source4/torture/auth/ntlmssp.c new file mode 100644 index 0000000..e549671 --- /dev/null +++ b/source4/torture/auth/ntlmssp.c @@ -0,0 +1,163 @@ +/* + Unix SMB/CIFS implementation. + Small self-tests for the NTLMSSP code + Copyright (C) Andrew Bartlett 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "auth/ntlmssp/ntlmssp_private.h" +#include "lib/cmdline/cmdline.h" +#include "torture/torture.h" +#include "param/param.h" +#include "torture/auth/proto.h" + +static bool torture_ntlmssp_self_check(struct torture_context *tctx) +{ + struct gensec_security *gensec_security; + struct gensec_ntlmssp_context *gensec_ntlmssp; + struct ntlmssp_state *ntlmssp_state; + DATA_BLOB data; + DATA_BLOB sig, expected_sig; + TALLOC_CTX *mem_ctx = tctx; + + torture_assert_ntstatus_ok(tctx, + gensec_client_start(mem_ctx, &gensec_security, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)), + "gensec client start"); + + gensec_set_credentials(gensec_security, samba_cmdline_get_creds()); + + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); + + torture_assert_ntstatus_ok(tctx, + gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP), + "Failed to start GENSEC for NTLMSSP"); + + gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, + struct gensec_ntlmssp_context); + ntlmssp_state = gensec_ntlmssp->ntlmssp_state; + + ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405060708090a0b0c0d0e0f00"); + dump_data_pw("NTLMSSP session key: \n", + ntlmssp_state->session_key.data, + ntlmssp_state->session_key.length); + + ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_NTLM2; + + torture_assert_ntstatus_ok(tctx, + ntlmssp_sign_init(ntlmssp_state), + "Failed to sign_init"); + + data = strhex_to_data_blob(tctx, "6a43494653"); + gensec_ntlmssp_sign_packet(gensec_security, gensec_security, + data.data, data.length, data.data, data.length, &sig); + + expected_sig = strhex_to_data_blob(tctx, "01000000e37f97f2544f4d7e00000000"); + + dump_data_pw("NTLMSSP calc sig: ", sig.data, sig.length); + dump_data_pw("NTLMSSP expected sig: ", expected_sig.data, expected_sig.length); + + torture_assert_int_equal(tctx, sig.length, expected_sig.length, "Wrong sig length"); + + torture_assert_mem_equal(tctx, sig.data, expected_sig.data, sig.length, + "data mismatch"); + + torture_assert_ntstatus_equal(tctx, + gensec_ntlmssp_check_packet(gensec_security, + data.data, data.length, data.data, data.length, &sig), + NT_STATUS_ACCESS_DENIED, "Check of just signed packet (should fail, wrong end)"); + + ntlmssp_state->session_key = data_blob(NULL, 0); + + torture_assert_ntstatus_equal(tctx, + gensec_ntlmssp_check_packet(gensec_security, + data.data, data.length, data.data, data.length, &sig), + NT_STATUS_NO_USER_SESSION_KEY, "Check of just signed packet without a session key should fail"); + + talloc_free(gensec_security); + + torture_assert_ntstatus_ok(tctx, + gensec_client_start(mem_ctx, &gensec_security, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)), + "Failed to start GENSEC for NTLMSSP"); + + gensec_set_credentials(gensec_security, samba_cmdline_get_creds()); + + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); + + torture_assert_ntstatus_ok(tctx, + gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP), + "GENSEC start mech by oid"); + + gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, + struct gensec_ntlmssp_context); + ntlmssp_state = gensec_ntlmssp->ntlmssp_state; + + ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405e538b0"); + dump_data_pw("NTLMSSP session key: \n", + ntlmssp_state->session_key.data, + ntlmssp_state->session_key.length); + + ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_KEY_EXCH; + + torture_assert_ntstatus_ok(tctx, + ntlmssp_sign_init(ntlmssp_state), + "Failed to sign_init"); + + data = strhex_to_data_blob(tctx, "6a43494653"); + gensec_ntlmssp_sign_packet(gensec_security, gensec_security, + data.data, data.length, data.data, data.length, &sig); + + expected_sig = strhex_to_data_blob(tctx, "0100000078010900397420fe0e5a0f89"); + + dump_data_pw("NTLMSSP calc sig: ", sig.data, sig.length); + dump_data_pw("NTLMSSP expected sig: ", expected_sig.data, expected_sig.length); + + torture_assert_int_equal(tctx, sig.length, expected_sig.length, "Wrong sig length"); + + torture_assert_mem_equal(tctx, sig.data+8, expected_sig.data+8, sig.length-8, + "data mismatch"); + + torture_assert_ntstatus_equal(tctx, + gensec_ntlmssp_check_packet(gensec_security, + data.data, data.length, data.data, data.length, &sig), + NT_STATUS_ACCESS_DENIED, "Check of just signed packet (should fail, wrong end)"); + + sig.length /= 2; + + torture_assert_ntstatus_equal(tctx, + gensec_ntlmssp_check_packet(gensec_security, + data.data, data.length, data.data, data.length, &sig), + NT_STATUS_ACCESS_DENIED, "Check of just signed packet with short sig"); + + talloc_free(gensec_security); + return true; +} + +struct torture_suite *torture_ntlmssp(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "ntlmssp"); + + torture_suite_add_simple_test(suite, "NTLMSSP self check", + torture_ntlmssp_self_check); + + return suite; +} diff --git a/source4/torture/auth/pac.c b/source4/torture/auth/pac.c new file mode 100644 index 0000000..8ba8ed4 --- /dev/null +++ b/source4/torture/auth/pac.c @@ -0,0 +1,741 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "auth/auth.h" +#include "auth/kerberos/kerberos.h" +#include "samba3/samba3.h" +#include "libcli/security/security.h" +#include "torture/torture.h" +#include "auth/auth_sam_reply.h" +#include "param/param.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "torture/auth/proto.h" +#include "auth/kerberos/pac_utils.h" + +static bool torture_pac_self_check(struct torture_context *tctx) +{ + NTSTATUS nt_status; + DATA_BLOB tmp_blob; + struct PAC_DATA *pac_data; + struct PAC_LOGON_INFO *logon_info; + union netr_Validation validation; + + /* Generate a nice, arbitrary keyblock */ + uint8_t server_bytes[16]; + uint8_t krbtgt_bytes[16]; + krb5_keyblock server_keyblock; + krb5_keyblock krbtgt_keyblock; + + krb5_error_code ret; + + struct smb_krb5_context *smb_krb5_context; + + struct auth_user_info_dc *user_info_dc; + struct auth_user_info_dc *user_info_dc_out; + + krb5_principal client_principal; + time_t logon_time = time(NULL); + + TALLOC_CTX *mem_ctx = tctx; + + torture_assert(tctx, 0 == smb_krb5_init_context(mem_ctx, + tctx->lp_ctx, + &smb_krb5_context), + "smb_krb5_init_context"); + + generate_random_buffer(server_bytes, 16); + generate_random_buffer(krbtgt_bytes, 16); + + ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + server_bytes, sizeof(server_bytes), + &server_keyblock); + torture_assert(tctx, !ret, talloc_asprintf(tctx, + "(self test) Server Keyblock encoding failed: %s", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + + ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + krbtgt_bytes, sizeof(krbtgt_bytes), + &krbtgt_keyblock); + if (ret) { + char *err = smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx); + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + + torture_fail(tctx, talloc_asprintf(tctx, + "(self test) KRBTGT Keyblock encoding failed: %s", err)); + } + + /* We need an input, and this one requires no underlying database */ + nt_status = auth_anonymous_user_info_dc(mem_ctx, lpcfg_netbios_name(tctx->lp_ctx), &user_info_dc); + + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + torture_fail(tctx, "auth_anonymous_user_info_dc"); + } + + ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, + user_info_dc->info->account_name, + KRB5_PRINCIPAL_PARSE_NO_REALM, + &client_principal); + if (ret) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + torture_fail(tctx, "krb5_parse_name_flags(norealm)"); + } + + /* OK, go ahead and make a PAC */ + ret = kerberos_create_pac(mem_ctx, + user_info_dc, + smb_krb5_context->krb5_context, + &krbtgt_keyblock, + &server_keyblock, + client_principal, + logon_time, + &tmp_blob); + + if (ret) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, + client_principal); + + torture_fail(tctx, talloc_asprintf(tctx, + "(self test) PAC encoding failed: %s", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + } + + dump_data(10,tmp_blob.data,tmp_blob.length); + + /* Now check that we can read it back (using full decode and validate) */ + nt_status = kerberos_decode_pac(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + &krbtgt_keyblock, + &server_keyblock, + client_principal, + logon_time, + &pac_data); + + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, + client_principal); + + torture_fail(tctx, talloc_asprintf(tctx, + "(self test) PAC decoding failed: %s", + nt_errstr(nt_status))); + } + + /* Now check we can read it back (using Heimdal's pac parsing) */ + nt_status = kerberos_pac_blob_to_user_info_dc(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + &user_info_dc_out, NULL, NULL); + + /* The user's SID is the first element in the list */ + if (!dom_sid_equal(&user_info_dc->sids[PRIMARY_USER_SID_INDEX].sid, + &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, + client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(self test) PAC Decode resulted in *different* domain SID: %s != %s", + dom_sid_string(mem_ctx, &user_info_dc->sids[PRIMARY_USER_SID_INDEX].sid), + dom_sid_string(mem_ctx, &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid))); + } + talloc_free(user_info_dc_out); + + /* Now check that we can read it back (yet again) */ + nt_status = kerberos_pac_logon_info(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + &krbtgt_keyblock, + &server_keyblock, + client_principal, + logon_time, + &logon_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, + client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(self test) PAC decoding (for logon info) failed: %s", + nt_errstr(nt_status))); + } + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, + client_principal); + + /* And make a server info from the samba-parsed PAC */ + validation.sam3 = &logon_info->info3; + nt_status = make_user_info_dc_netlogon_validation(mem_ctx, + "", + 3, &validation, + true, /* This user was authenticated */ + &user_info_dc_out); + if (!NT_STATUS_IS_OK(nt_status)) { + torture_fail(tctx, + talloc_asprintf(tctx, + "(self test) PAC decoding (make server info) failed: %s", + nt_errstr(nt_status))); + } + + if (!dom_sid_equal(&user_info_dc->sids[PRIMARY_USER_SID_INDEX].sid, + &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid)) { + torture_fail(tctx, + talloc_asprintf(tctx, + "(self test) PAC Decode resulted in *different* domain SID: %s != %s", + dom_sid_string(mem_ctx, &user_info_dc->sids[PRIMARY_USER_SID_INDEX].sid), + dom_sid_string(mem_ctx, &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid))); + } + return true; +} + + +/* This is the PAC generated on my test network, by my test Win2k3 server. + -- abartlet 2005-07-04 +*/ + +static const uint8_t saved_pac[] = { + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0xdf, 0xa6, 0xcb, + 0x4f, 0x7d, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xc0, 0x3c, 0x4e, 0x59, 0x62, 0x73, 0xc5, 0x01, 0xc0, 0x3c, 0x4e, 0x59, + 0x62, 0x73, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x16, 0x00, 0x16, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xed, 0x03, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00, 0x20, 0x00, 0x02, 0x00, 0x16, 0x00, 0x18, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00, + 0x41, 0x00, 0x4c, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x4c, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, + 0x4e, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x33, 0x00, 0x54, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4e, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x11, 0x2f, 0xaf, 0xb5, 0x90, 0x04, 0x1b, 0xec, 0x50, 0x3b, 0xec, 0xdc, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x66, 0x28, 0xea, 0x37, 0x80, 0xc5, 0x01, 0x16, 0x00, 0x77, 0x00, 0x32, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x33, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x24, 0x00, + 0x76, 0xff, 0xff, 0xff, 0x37, 0xd5, 0xb0, 0xf7, 0x24, 0xf0, 0xd6, 0xd4, 0xec, 0x09, 0x86, 0x5a, + 0xa0, 0xe8, 0xc3, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xb4, 0xd8, 0xb8, 0xfe, + 0x83, 0xb3, 0x13, 0x3f, 0xfc, 0x5c, 0x41, 0xad, 0xe2, 0x64, 0x83, 0xe0, 0x00, 0x00, 0x00, 0x00 +}; + +/* Check with a known 'well formed' PAC, from my test server */ +static bool torture_pac_saved_check(struct torture_context *tctx) +{ + NTSTATUS nt_status; + enum ndr_err_code ndr_err; + DATA_BLOB tmp_blob, validate_blob; + struct PAC_DATA *pac_data, pac_data2; + struct PAC_LOGON_INFO *logon_info; + union netr_Validation validation; + const char *pac_file, *pac_kdc_key, *pac_member_key; + struct auth_user_info_dc *user_info_dc_out; + + krb5_keyblock server_keyblock; + krb5_keyblock krbtgt_keyblock, *krbtgt_keyblock_p; + struct samr_Password *krbtgt_bytes, *krbsrv_bytes; + + krb5_error_code ret; + struct smb_krb5_context *smb_krb5_context; + + const char *principal_string; + char *broken_principal_string; + krb5_principal client_principal; + const char *authtime_string; + time_t authtime; + TALLOC_CTX *mem_ctx = tctx; + + torture_assert(tctx, 0 == smb_krb5_init_context(mem_ctx, + tctx->lp_ctx, + &smb_krb5_context), + "smb_krb5_init_context"); + + pac_kdc_key = torture_setting_string(tctx, "pac_kdc_key", + "B286757148AF7FD252C53603A150B7E7"); + + pac_member_key = torture_setting_string(tctx, "pac_member_key", + "D217FAEAE5E6B5F95CCC94077AB8A5FC"); + + torture_comment(tctx, "Using pac_kdc_key '%s'\n", pac_kdc_key); + torture_comment(tctx, "Using pac_member_key '%s'\n", pac_member_key); + + /* The krbtgt key in use when the above PAC was generated. + * This is an arcfour-hmac-md5 key, extracted with our 'net + * samdump' tool. */ + if (*pac_kdc_key == 0) { + krbtgt_bytes = NULL; + } else { + krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, pac_kdc_key); + if (!krbtgt_bytes) { + torture_fail(tctx, "(saved test) Could not interpret krbtgt key"); + } + } + + krbsrv_bytes = smbpasswd_gethexpwd(mem_ctx, pac_member_key); + if (!krbsrv_bytes) { + torture_fail(tctx, "(saved test) Could not interpret krbsrv key"); + } + + ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + krbsrv_bytes->hash, sizeof(krbsrv_bytes->hash), + &server_keyblock); + torture_assert(tctx, !ret, + talloc_asprintf(tctx, + "(saved test) Server Keyblock encoding failed: %s", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + + if (krbtgt_bytes) { + ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + krbtgt_bytes->hash, sizeof(krbtgt_bytes->hash), + &krbtgt_keyblock); + if (ret) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) Server Keyblock encoding failed: %s", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + } + krbtgt_keyblock_p = &krbtgt_keyblock; + } else { + krbtgt_keyblock_p = NULL; + } + + pac_file = torture_setting_string(tctx, "pac_file", NULL); + if (pac_file) { + tmp_blob.data = (uint8_t *)file_load(pac_file, &tmp_blob.length, 0, mem_ctx); + torture_comment(tctx, "(saved test) Loaded pac of size %ld from %s\n", (long)tmp_blob.length, pac_file); + } else { + tmp_blob = data_blob_talloc(mem_ctx, saved_pac, sizeof(saved_pac)); + } + + dump_data(10,tmp_blob.data,tmp_blob.length); + + principal_string = torture_setting_string(tctx, "pac_client_principal", + "w2003final$@WIN2K3.THINKER.LOCAL"); + + authtime_string = torture_setting_string(tctx, "pac_authtime", "1120440609"); + authtime = strtoull(authtime_string, NULL, 0); + + ret = krb5_parse_name(smb_krb5_context->krb5_context, principal_string, + &client_principal); + if (ret) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) parsing of client principal [%s] failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); + } + + /* Decode and verify the signaure on the PAC */ + nt_status = kerberos_decode_pac(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, authtime, &pac_data); + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, talloc_asprintf(tctx, + "(saved test) PAC decoding failed: %s", + nt_errstr(nt_status))); + } + + /* Now check we can read it back (using Heimdal's pac parsing) */ + nt_status = kerberos_pac_blob_to_user_info_dc(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + &user_info_dc_out, + NULL, NULL); + + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, talloc_asprintf(tctx, + "(saved test) Heimdal PAC decoding failed: %s", + nt_errstr(nt_status))); + } + + if (!pac_file && + !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, + "S-1-5-21-3048156945-3961193616-3706469200-1005"), + &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) Heimdal PAC Decode resulted in *different* domain SID: %s != %s", + "S-1-5-21-3048156945-3961193616-3706469200-1005", + dom_sid_string(mem_ctx, &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid))); + } + + talloc_free(user_info_dc_out); + + /* Parse the PAC again, for the logon info this time (using Samba4's parsing) */ + nt_status = kerberos_pac_logon_info(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, authtime, &logon_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) PAC decoding (for logon info) failed: %s", + nt_errstr(nt_status))); + } + + validation.sam3 = &logon_info->info3; + nt_status = make_user_info_dc_netlogon_validation(mem_ctx, + "", + 3, &validation, + true, /* This user was authenticated */ + &user_info_dc_out); + if (!NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) PAC decoding (make server info) failed: %s", + nt_errstr(nt_status))); + } + + if (!pac_file && + !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, + "S-1-5-21-3048156945-3961193616-3706469200-1005"), + &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) PAC Decode resulted in *different* domain SID: %s != %s", + "S-1-5-21-3048156945-3961193616-3706469200-1005", + dom_sid_string(mem_ctx, &user_info_dc_out->sids[PRIMARY_USER_SID_INDEX].sid))); + } + + if (krbtgt_bytes == NULL) { + torture_comment(tctx, "skipping PAC encoding tests as non kdc key\n"); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + return true; + } + + ret = kerberos_encode_pac(mem_ctx, + pac_data, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + &validate_blob); + + if (ret != 0) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, "(saved test) PAC push failed"); + } + + dump_data(10, validate_blob.data, validate_blob.length); + + /* compare both the length and the data bytes after a + * pull/push cycle. This ensures we use the exact same + * pointer, padding etc algorithms as win2k3. + */ + if (tmp_blob.length != validate_blob.length) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, + talloc_asprintf(tctx, + "(saved test) PAC push failed: original buffer length[%u] != created buffer length[%u]", + (unsigned)tmp_blob.length, (unsigned)validate_blob.length)); + } + + if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + DEBUG(0, ("tmp_data:\n")); + dump_data(0, tmp_blob.data, tmp_blob.length); + DEBUG(0, ("validate_blob:\n")); + dump_data(0, validate_blob.data, validate_blob.length); + + torture_fail(tctx, talloc_asprintf(tctx, "(saved test) PAC push failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length)); + } + + ret = kerberos_create_pac(mem_ctx, + user_info_dc_out, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, authtime, + &validate_blob); + + if (ret != 0) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, "(saved test) regnerated PAC create failed"); + } + + dump_data(10,validate_blob.data,validate_blob.length); + + /* compare both the length and the data bytes after a + * pull/push cycle. This ensures we use the exact same + * pointer, padding etc algorithms as win2k3. + */ + if (tmp_blob.length != validate_blob.length) { + ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, + &pac_data2, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + nt_status = ndr_map_error2ntstatus(ndr_err); + torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC"); + + NDR_PRINT_DEBUG(PAC_DATA, pac_data); + + NDR_PRINT_DEBUG(PAC_DATA, &pac_data2); + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + torture_fail(tctx, talloc_asprintf(tctx, + "(saved test) PAC regenerate failed: original buffer length[%u] != created buffer length[%u]", + (unsigned)tmp_blob.length, (unsigned)validate_blob.length)); + } + + if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) { + ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, + &pac_data2, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + nt_status = ndr_map_error2ntstatus(ndr_err); + torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC"); + + NDR_PRINT_DEBUG(PAC_DATA, pac_data); + + NDR_PRINT_DEBUG(PAC_DATA, &pac_data2); + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + DEBUG(0, ("tmp_data:\n")); + dump_data(0, tmp_blob.data, tmp_blob.length); + DEBUG(0, ("validate_blob:\n")); + dump_data(0, validate_blob.data, validate_blob.length); + + torture_fail(tctx, talloc_asprintf(tctx, + "(saved test) PAC regenerate failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length)); + } + + /* Break the auth time, to ensure we check this vital detail (not setting this caused all the pain in the first place... */ + nt_status = kerberos_decode_pac(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, + authtime + 1, &pac_data); + if (NT_STATUS_IS_OK(nt_status)) { + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken auth time (time + 1)"); + } + + /* Break the client principal */ + krb5_free_principal(smb_krb5_context->krb5_context, client_principal); + + broken_principal_string = talloc_strdup(mem_ctx, principal_string); + broken_principal_string[0]++; + + ret = krb5_parse_name(smb_krb5_context->krb5_context, + broken_principal_string, &client_principal); + if (ret) { + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + torture_fail(tctx, talloc_asprintf(tctx, + "(saved test) parsing of broken client principal failed: %s", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); + } + + nt_status = kerberos_decode_pac(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, + authtime, &pac_data); + if (NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on modified principal"); + } + + /* Finally... Bugger up the signature, and check we fail the checksum */ + tmp_blob.data[tmp_blob.length - 2]++; + + nt_status = kerberos_decode_pac(mem_ctx, + tmp_blob, + smb_krb5_context->krb5_context, + krbtgt_keyblock_p, + &server_keyblock, + client_principal, + authtime, + &pac_data); + if (NT_STATUS_IS_OK(nt_status)) { + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken checksum"); + } + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + krbtgt_keyblock_p); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + return true; +} + +struct torture_suite *torture_pac(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "pac"); + + torture_suite_add_simple_test(suite, "self check", + torture_pac_self_check); + torture_suite_add_simple_test(suite, "saved check", + torture_pac_saved_check); + return suite; +} diff --git a/source4/torture/auth/smbencrypt.c b/source4/torture/auth/smbencrypt.c new file mode 100644 index 0000000..79c90eb --- /dev/null +++ b/source4/torture/auth/smbencrypt.c @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + + tests for smbencrypt code + + Copyright (C) Andrew Tridgell 2011 + Copyright (C) Andrew Bartlett 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/auth/libcli_auth.h" +#include "torture/torture.h" +#include "torture/auth/proto.h" + +static bool torture_deshash(struct torture_context *tctx) +{ + struct { + const char *input; + uint8_t output[16]; + bool should_pass; + } testcases[] = { + { "", + { 0xAA, 0xD3, 0xB4, 0x35, 0xB5, 0x14, 0x04, 0xEE, + 0xAA, 0xD3, 0xB4, 0x35, 0xB5, 0x14, 0x04, 0xEE }, true}, + { "abcdefgh", + { 0xE0, 0xC5, 0x10, 0x19, 0x9C, 0xC6, 0x6A, 0xBD, + 0x5A, 0xCD, 0xCD, 0x7C, 0x24, 0x7F, 0xA8, 0x3A }, true}, + { "0123456789abc", + { 0x56, 0x45, 0xF1, 0x3F, 0x50, 0x08, 0x82, 0xB2, + 0x50, 0x79, 0x8A, 0xE6, 0x33, 0x38, 0xAF, 0xE9 }, true}, + { "0123456789abcd", + { 0x56, 0x45, 0xF1, 0x3F, 0x50, 0x08, 0x82, 0xB2, + 0x1A, 0xC3, 0x88, 0x4B, 0x83, 0x32, 0x45, 0x40 }, true}, + { "0123456789abcde", + { 0x56, 0x45, 0xF1, 0x3F, 0x50, 0x08, 0x82, 0xB2, + 0x1A, 0xC3, 0x88, 0x4B, 0x83, 0x32, 0x45, 0x40 }, false}, + }; + int i; + for (i=0; i. +*/ + +#include "includes.h" +#include "../lib/util/dlinklist.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname); + +struct trans2_blobs { + struct trans2_blobs *next, *prev; + uint16_t level; + DATA_BLOB params, data; +}; + +/* look for aliases for a query */ +static bool gen_aliases(struct torture_context *tctx, + struct smbcli_state *cli, struct smb_trans2 *t2, + int level_offset) +{ + uint16_t level; + struct trans2_blobs *alias_blobs = NULL; + struct trans2_blobs *t2b, *t2b2; + int count=0, alias_count=0; + + for (level=0;level<2000;level++) { + NTSTATUS status; + + SSVAL(t2->in.params.data, level_offset, level); + + status = smb_raw_trans2(cli->tree, tctx, t2); + if (!NT_STATUS_IS_OK(status)) continue; + + t2b = talloc(tctx, struct trans2_blobs); + t2b->level = level; + t2b->params = t2->out.params; + t2b->data = t2->out.data; + DLIST_ADD(alias_blobs, t2b); + torture_comment(tctx, + "\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", + level, level, + (int)t2b->data.length, (int)t2b->data.length); + count++; + } + + torture_comment(tctx, "Found %d levels with success status\n", count); + + for (t2b=alias_blobs; t2b; t2b=t2b->next) { + for (t2b2=alias_blobs; t2b2; t2b2=t2b2->next) { + if (t2b->level >= t2b2->level) continue; + if (data_blob_cmp(&t2b->params, &t2b2->params) == 0 && + data_blob_cmp(&t2b->data, &t2b2->data) == 0) { + torture_comment(tctx, + "\tLevel %u (0x%x) and level %u (0x%x) are possible aliases\n", + t2b->level, t2b->level, t2b2->level, t2b2->level); + alias_count++; + } + } + } + + torture_comment(tctx, "Found %d aliased levels\n", alias_count); + + return true; +} + +/* look for qfsinfo aliases */ +static bool qfsinfo_aliases(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_QFSINFO; + + t2.in.max_param = 0; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 2); + t2.in.data = data_blob(NULL, 0); + ZERO_STRUCT(t2.out); + + return gen_aliases(tctx, cli, &t2, 0); +} + +/* look for qfileinfo aliases */ +static bool qfileinfo_aliases(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_QFILEINFO; + const char *fname = "\\qfileinfo_aliases.txt"; + int fnum; + + t2.in.max_param = 2; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 4); + t2.in.data = data_blob(NULL, 0); + ZERO_STRUCT(t2.out); + + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, cli, fname); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", fname, + smbcli_errstr(cli->tree))); + + smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2)); + + SSVAL(t2.in.params.data, 0, fnum); + + if (!gen_aliases(tctx, cli, &t2, 2)) + return false; + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + return true; +} + + +/* look for qpathinfo aliases */ +static bool qpathinfo_aliases(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_QPATHINFO; + const char *fname = "\\qpathinfo_aliases.txt"; + int fnum; + + ZERO_STRUCT(t2); + t2.in.max_param = 2; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 6); + t2.in.data = data_blob(NULL, 0); + + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, cli, fname); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", fname, + smbcli_errstr(cli->tree))); + + smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2)); + smbcli_close(cli->tree, fnum); + + SIVAL(t2.in.params.data, 2, 0); + + smbcli_blob_append_string(cli->session, tctx, &t2.in.params, + fname, STR_TERMINATE); + + if (!gen_aliases(tctx, cli, &t2, 0)) + return false; + + smbcli_unlink(cli->tree, fname); + + return true; +} + + +/* look for trans2 findfirst aliases */ +static bool findfirst_aliases(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_FINDFIRST; + const char *fname = "\\findfirst_aliases.txt"; + int fnum; + + ZERO_STRUCT(t2); + t2.in.max_param = 16; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 12); + t2.in.data = data_blob(NULL, 0); + + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, cli, fname); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", fname, + smbcli_errstr(cli->tree))); + + smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2)); + smbcli_close(cli->tree, fnum); + + SSVAL(t2.in.params.data, 0, 0); + SSVAL(t2.in.params.data, 2, 1); + SSVAL(t2.in.params.data, 4, FLAG_TRANS2_FIND_CLOSE); + SSVAL(t2.in.params.data, 6, 0); + SIVAL(t2.in.params.data, 8, 0); + + smbcli_blob_append_string(cli->session, tctx, &t2.in.params, + fname, STR_TERMINATE); + + if (!gen_aliases(tctx, cli, &t2, 6)) + return false; + + smbcli_unlink(cli->tree, fname); + + return true; +} + + + +/* look for aliases for a set function */ +static bool gen_set_aliases(struct torture_context *tctx, + struct smbcli_state *cli, + struct smb_trans2 *t2, int level_offset) +{ + uint16_t level; + struct trans2_blobs *alias_blobs = NULL; + struct trans2_blobs *t2b; + int count=0, dsize; + + for (level=1;level<1100;level++) { + NTSTATUS status, status1; + SSVAL(t2->in.params.data, level_offset, level); + + status1 = NT_STATUS_OK; + + for (dsize=2; dsize<1024; dsize += 2) { + data_blob_free(&t2->in.data); + t2->in.data = data_blob(NULL, dsize); + data_blob_clear(&t2->in.data); + status = smb_raw_trans2(cli->tree, tctx, t2); + /* some error codes mean that this whole level doesn't exist */ + if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, status) || + NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status) || + NT_STATUS_EQUAL(NT_STATUS_NOT_SUPPORTED, status)) { + break; + } + if (NT_STATUS_IS_OK(status)) break; + + /* invalid parameter means that the level exists at this + size, but the contents are wrong (not surprising with + all zeros!) */ + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) break; + + /* this is the usual code for 'wrong size' */ + if (NT_STATUS_EQUAL(status, NT_STATUS_INFO_LENGTH_MISMATCH)) { + continue; + } + + if (!NT_STATUS_EQUAL(status, status1)) { + torture_comment(tctx, "level=%d size=%d %s\n", level, dsize, nt_errstr(status)); + } + status1 = status; + } + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) continue; + + t2b = talloc(tctx, struct trans2_blobs); + t2b->level = level; + t2b->params = t2->out.params; + t2b->data = t2->out.data; + DLIST_ADD(alias_blobs, t2b); + torture_comment(tctx, + "\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", + level, level, + (int)t2->in.data.length, (int)t2->in.data.length); + count++; + } + + torture_comment(tctx, "Found %d valid levels\n", count); + + return true; +} + + + +/* look for setfileinfo aliases */ +static bool setfileinfo_aliases(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_SETFILEINFO; + const char *fname = "\\setfileinfo_aliases.txt"; + int fnum; + + ZERO_STRUCT(t2); + t2.in.max_param = 2; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 6); + t2.in.data = data_blob(NULL, 0); + + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, cli, fname); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", fname, + smbcli_errstr(cli->tree))); + + smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2)); + + SSVAL(t2.in.params.data, 0, fnum); + SSVAL(t2.in.params.data, 4, 0); + + gen_set_aliases(tctx, cli, &t2, 2); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + return true; +} + +/* look for setpathinfo aliases */ +static bool setpathinfo_aliases(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_SETPATHINFO; + const char *fname = "\\setpathinfo_aliases.txt"; + int fnum; + + ZERO_STRUCT(t2); + t2.in.max_param = 32; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc_zero(tctx, 4); + t2.in.data = data_blob(NULL, 0); + + smbcli_unlink(cli->tree, fname); + + fnum = create_complex_file(cli, cli, fname); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", fname, + smbcli_errstr(cli->tree))); + + smbcli_write(cli->tree, fnum, 0, &t2, 0, sizeof(t2)); + smbcli_close(cli->tree, fnum); + + SSVAL(t2.in.params.data, 2, 0); + + smbcli_blob_append_string(cli->session, tctx, &t2.in.params, + fname, STR_TERMINATE); + + if (!gen_set_aliases(tctx, cli, &t2, 0)) + return false; + + torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli->tree, fname), + talloc_asprintf(tctx, "unlink: %s", smbcli_errstr(cli->tree))); + + return true; +} + + +/* look for aliased info levels in trans2 calls */ +struct torture_suite *torture_trans2_aliases(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "aliases"); + + torture_suite_add_1smb_test(suite, "QFSINFO aliases", qfsinfo_aliases); + torture_suite_add_1smb_test(suite, "QFILEINFO aliases", qfileinfo_aliases); + torture_suite_add_1smb_test(suite, "QPATHINFO aliases", qpathinfo_aliases); + torture_suite_add_1smb_test(suite, "FINDFIRST aliases", findfirst_aliases); + torture_suite_add_1smb_test(suite, "setfileinfo_aliases", setfileinfo_aliases); + torture_suite_add_1smb_test(suite, "setpathinfo_aliases", setpathinfo_aliases); + + return suite; +} diff --git a/source4/torture/basic/attr.c b/source4/torture/basic/attr.c new file mode 100644 index 0000000..f2a554d --- /dev/null +++ b/source4/torture/basic/attr.c @@ -0,0 +1,437 @@ +/* + Unix SMB/CIFS implementation. + + openattr tester + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/filesys.h" +#include "libcli/security/security_descriptor.h" +#include "torture/basic/proto.h" + +extern int torture_failures; + +#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0) + + +static const uint32_t open_attrs_table[] = { + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_SYSTEM, + + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM, +}; + +struct trunc_open_results { + unsigned int num; + uint32_t init_attr; + uint32_t trunc_attr; + uint32_t result_attr; +}; + +static const struct trunc_open_results attr_results[] = { + { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE }, + { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE }, + { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY }, + { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE }, + { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE }, + { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY }, + { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }, + { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }, + { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN }, + { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM }, + { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM } +}; + + +bool torture_openattrtest(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = "\\openattr.file"; + int fnum1; + uint16_t attr; + unsigned int i, j, k, l; + int failures = 0; + + for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32_t); i++) { + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_WRITE_DATA, + open_attrs_table[i], + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open %d (1) of %s failed (%s)", i, + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close %d (1) of %s failed (%s)", i, fname, + smbcli_errstr(cli1->tree))); + + for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) { + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA, + open_attrs_table[j], + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE, 0, 0); + + if (fnum1 == -1) { + for (l = 0; l < ARRAY_SIZE(attr_results); l++) { + if (attr_results[l].num == k) { + torture_result(tctx, TORTURE_FAIL, + "[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(%s)", + k, open_attrs_table[i], + open_attrs_table[j], + fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_exit); + } + } + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli1->tree), NT_STATUS_ACCESS_DENIED)) { + torture_result(tctx, TORTURE_FAIL, + "[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s", + k, open_attrs_table[i], open_attrs_table[j], + smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_exit); + } +#if 0 + torture_comment(tctx, "[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]); +#endif + k++; + continue; + } + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close %d (2) of %s failed (%s)", j, + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_getatr(cli1->tree, fname, &attr, NULL, NULL), + talloc_asprintf(tctx, "getatr(2) failed (%s)", smbcli_errstr(cli1->tree))); + +#if 0 + torture_comment(tctx, "[%d] getatr check [0x%x] trunc [0x%x] got attr 0x%x\n", + k, open_attrs_table[i], open_attrs_table[j], attr ); +#endif + + for (l = 0; l < ARRAY_SIZE(attr_results); l++) { + if (attr_results[l].num == k) { + if (attr != attr_results[l].result_attr || + open_attrs_table[i] != attr_results[l].init_attr || + open_attrs_table[j] != attr_results[l].trunc_attr) { + torture_result(tctx, TORTURE_FAIL, + "[%d] getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x", + k, open_attrs_table[i], + open_attrs_table[j], + (unsigned int)attr, + attr_results[l].result_attr); + CHECK_MAX_FAILURES(error_exit); + } + break; + } + } + k++; + } + } +error_exit: + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + if (failures) { + return false; + } + return true; +} + +bool torture_winattrtest(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = "\\winattr1.file"; + const char *dname = "\\winattr1.dir"; + int fnum1; + uint16_t attr; + uint16_t j; + uint32_t aceno; + int failures = 0; + union smb_fileinfo query, query_org; + NTSTATUS status; + struct security_descriptor *sd1, *sd2; + ZERO_STRUCT(query); + ZERO_STRUCT(query_org); + + /* Test winattrs for file */ + smbcli_unlink(cli1->tree, fname); + + /* Open a file*/ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open(1) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + + + /* Get security descriptor and store it*/ + query_org.generic.level = RAW_FILEINFO_SEC_DESC; + query_org.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli1->tree, tctx, &query_org); + if(!NT_STATUS_IS_OK(status)){ + torture_comment(tctx, "smb_raw_fileinfo(1) of %s failed (%s)\n", + fname, nt_errstr(status)); + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close(1) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + CHECK_MAX_FAILURES(error_exit_file); + } + sd1 = query_org.query_secdesc.out.sd; + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close(1) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + + /*Set and get attributes*/ + for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) { + torture_assert_ntstatus_ok(tctx, + smbcli_setatr(cli1->tree, fname, open_attrs_table[j],0), + talloc_asprintf(tctx, "setatr(2) failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_getatr(cli1->tree, fname, &attr, NULL, NULL), + talloc_asprintf(tctx, "getatr(2) failed (%s)", + smbcli_errstr(cli1->tree))); + + /* Check the result */ + if((j == 0)&&(attr != FILE_ATTRIBUTE_ARCHIVE)){ + torture_comment(tctx, "getatr check failed. \ + Attr applied [0x%x], got attr [0x%x], \ + should be [0x%x]", + open_attrs_table[j], + (uint16_t)attr,open_attrs_table[j +1]); + CHECK_MAX_FAILURES(error_exit_file); + }else{ + + if((j != 0) &&(attr != open_attrs_table[j])){ + torture_comment(tctx, "getatr check failed. \ + Attr applied [0x%x],got attr 0x%x, \ + should be 0x%x ", + open_attrs_table[j], (uint16_t)attr, + open_attrs_table[j]); + CHECK_MAX_FAILURES(error_exit_file); + } + + } + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY | O_CREAT, + DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open(2) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + /*Get security descriptor */ + query.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + query.query_secdesc.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli1->tree, tctx, &query); + if(!NT_STATUS_IS_OK(status)){ + torture_comment(tctx, + "smb_raw_fileinfo(2) of %s failed (%s)\n", + fname, nt_errstr(status)); + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close(2) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + CHECK_MAX_FAILURES(error_exit_file); + } + sd2 = query.query_secdesc.out.sd; + + torture_assert_ntstatus_ok(tctx,smbcli_close(cli1->tree,fnum1), + talloc_asprintf(tctx, "close(2) of %s failed (%s)\n", + fname, smbcli_errstr(cli1->tree))); + + /*Compare security descriptors -- Must be same*/ + for (aceno=0;(sd1->dacl&&aceno < sd1->dacl->num_aces);aceno++){ + struct security_ace *ace1 = &sd1->dacl->aces[aceno]; + struct security_ace *ace2 = &sd2->dacl->aces[aceno]; + + if (!security_ace_equal(ace1, ace2)) { + torture_comment(tctx, + "ACLs changed! Not expected!\n"); + CHECK_MAX_FAILURES(error_exit_file); + } + } + + torture_comment(tctx, "[%d] setattr = [0x%x] got attr 0x%x\n", + j, open_attrs_table[j], attr ); + + } + +error_exit_file: + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + +/* Check for Directory. */ + + smbcli_deltree(cli1->tree, dname); + smbcli_rmdir(cli1->tree,dname); + + /* Open a directory */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_DIR_ALL, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DIRECTORY, 0); + /*smbcli_mkdir(cli1->tree,dname);*/ + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, + "open (1) of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + + + /* Get Security Descriptor */ + query_org.generic.level = RAW_FILEINFO_SEC_DESC; + query_org.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli1->tree, tctx, &query_org); + if(!NT_STATUS_IS_OK(status)){ + torture_comment(tctx, "smb_raw_fileinfo(1) of %s failed (%s)\n", + dname, nt_errstr(status)); + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close(1) of %s failed (%s)\n", + dname, smbcli_errstr(cli1->tree))); + CHECK_MAX_FAILURES(error_exit_dir); + } + sd1 = query_org.query_secdesc.out.sd; + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close (1) of %s failed (%s)", dname, + smbcli_errstr(cli1->tree))); + + /* Set and get win attributes*/ + for (j = 1; j < ARRAY_SIZE(open_attrs_table); j++) { + + torture_assert_ntstatus_ok(tctx, + smbcli_setatr(cli1->tree, dname, open_attrs_table[j], 0), + talloc_asprintf(tctx, "setatr(2) failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_getatr(cli1->tree, dname, &attr, NULL, NULL), + talloc_asprintf(tctx, "getatr(2) failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_comment(tctx, "[%d] setatt = [0x%x] got attr 0x%x\n", + j, open_attrs_table[j], attr ); + + /* Check the result */ + if(attr != (open_attrs_table[j]|FILE_ATTRIBUTE_DIRECTORY)){ + torture_comment(tctx, "getatr check failed. set attr \ + [0x%x], got attr 0x%x, should be 0x%x\n", + open_attrs_table[j], + (uint16_t)attr, + (unsigned int)(open_attrs_table[j]|FILE_ATTRIBUTE_DIRECTORY)); + CHECK_MAX_FAILURES(error_exit_dir); + } + + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_DIR_READ, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN, + 0,0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, + "open (2) of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + /* Get security descriptor */ + query.generic.level = RAW_FILEINFO_SEC_DESC; + query.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli1->tree, tctx, &query); + if(!NT_STATUS_IS_OK(status)){ + torture_comment(tctx, "smb_raw_fileinfo(2) of %s failed\ + (%s)\n", dname, nt_errstr(status)); + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close (2) of %s failed (%s)", dname, + smbcli_errstr(cli1->tree))); + CHECK_MAX_FAILURES(error_exit_dir); + } + sd2 = query.query_secdesc.out.sd; + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close (2) of %s failed (%s)", dname, + smbcli_errstr(cli1->tree))); + + /* Security descriptor must be same*/ + for (aceno=0;(sd1->dacl&&aceno < sd1->dacl->num_aces);aceno++){ + struct security_ace *ace1 = &sd1->dacl->aces[aceno]; + struct security_ace *ace2 = &sd2->dacl->aces[aceno]; + + if (!security_ace_equal(ace1, ace2)) { + torture_comment(tctx, + "ACLs changed! Not expected!\n"); + CHECK_MAX_FAILURES(error_exit_dir); + } + } + + } +error_exit_dir: + smbcli_deltree(cli1->tree, dname); + smbcli_rmdir(cli1->tree,dname); + + if(failures) + return false; + return true; +} diff --git a/source4/torture/basic/base.c b/source4/torture/basic/base.c new file mode 100644 index 0000000..fc36e6b --- /dev/null +++ b/source4/torture/basic/base.c @@ -0,0 +1,2090 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/basic/proto.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "torture/util.h" +#include "system/filesys.h" +#include "system/time.h" +#include "libcli/resolve/resolve.h" +#include "lib/events/events.h" +#include "param/param.h" + + +#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0) + + +static struct smbcli_state *open_nbt_connection(struct torture_context *tctx) +{ + struct nbt_name called, calling; + struct smbcli_state *cli; + const char *host = torture_setting_string(tctx, "host", NULL); + struct smbcli_options options; + bool ok; + + make_nbt_name_client(&calling, lpcfg_netbios_name(tctx->lp_ctx)); + + nbt_choose_called_name(NULL, &called, host, NBT_NAME_SERVER); + + cli = smbcli_state_init(NULL); + if (!cli) { + torture_result(tctx, TORTURE_FAIL, "Failed initialize smbcli_struct to connect with %s\n", host); + goto failed; + } + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + ok = smbcli_socket_connect(cli, host, lpcfg_smb_ports(tctx->lp_ctx), + tctx->ev, + lpcfg_resolve_context(tctx->lp_ctx), + &options, + lpcfg_socket_options(tctx->lp_ctx), + &calling, &called); + if (!ok) { + torture_result(tctx, TORTURE_FAIL, "Failed to connect with %s\n", host); + goto failed; + } + + cli->transport = smbcli_transport_init(cli->sock, cli, + true, &cli->options); + cli->sock = NULL; + if (!cli->transport) { + torture_result(tctx, TORTURE_FAIL, "smbcli_transport_init failed\n"); + goto failed; + } + + return cli; + +failed: + talloc_free(cli); + return NULL; +} + +static bool tcon_devtest(struct torture_context *tctx, + struct smbcli_state *cli, + const char *myshare, const char *devtype, + NTSTATUS expected_error) +{ + bool status; + const char *password = torture_setting_string(tctx, "password", NULL); + + status = NT_STATUS_IS_OK(smbcli_tconX(cli, myshare, devtype, + password)); + + torture_comment(tctx, "Trying share %s with devtype %s\n", myshare, devtype); + + if (NT_STATUS_IS_OK(expected_error)) { + if (!status) { + torture_fail(tctx, talloc_asprintf(tctx, + "tconX to share %s with type %s " + "should have succeeded but failed", + myshare, devtype)); + } + smbcli_tdis(cli); + } else { + if (status) { + torture_fail(tctx, talloc_asprintf(tctx, + "tconx to share %s with type %s " + "should have failed but succeeded", + myshare, devtype)); + } else { + if (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree), + expected_error)) { + } else { + torture_fail(tctx, "Returned unexpected error"); + } + } + } + return true; +} + + + +/** +test whether fnums and tids open on one VC are available on another (a major +security hole) +*/ +static bool run_fdpasstest(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = "\\fdpass.tst"; + int fnum1, oldtid; + uint8_t buf[1024]; + + smbcli_unlink(cli1->tree, fname); + + torture_comment(tctx, "Opening a file on connection 1\n"); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, + "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); + + torture_comment(tctx, "writing to file on connection 1\n"); + + torture_assert(tctx, + smbcli_write(cli1->tree, fnum1, 0, "hello world\n", 0, 13) == 13, + talloc_asprintf(tctx, + "write failed (%s)\n", smbcli_errstr(cli1->tree))); + + oldtid = cli2->tree->tid; + cli2->session->vuid = cli1->session->vuid; + cli2->tree->tid = cli1->tree->tid; + cli2->session->pid = cli1->session->pid; + + torture_comment(tctx, "reading from file on connection 2\n"); + + torture_assert(tctx, smbcli_read(cli2->tree, fnum1, buf, 0, 13) != 13, + talloc_asprintf(tctx, + "read succeeded! nasty security hole [%s]\n", buf)); + + smbcli_close(cli1->tree, fnum1); + smbcli_unlink(cli1->tree, fname); + + cli2->tree->tid = oldtid; + + return true; +} + +/** + This checks how the getatr calls works +*/ +static bool run_attrtest(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int fnum; + time_t t, t2; + const char *fname = "\\attrib123456789.tst"; + bool correct = true; + + smbcli_unlink(cli->tree, fname); + fnum = smbcli_open(cli->tree, fname, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + smbcli_close(cli->tree, fnum); + + if (NT_STATUS_IS_ERR(smbcli_getatr(cli->tree, fname, NULL, NULL, &t))) { + torture_result(tctx, TORTURE_FAIL, "getatr failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } + + torture_comment(tctx, "New file time is %s", ctime(&t)); + + if (labs(t - time(NULL)) > 60*60*24*10) { + torture_result(tctx, TORTURE_FAIL, "ERROR: SMBgetatr bug. time is %s", + ctime(&t)); + t = time(NULL); + correct = false; + } + + t2 = t-60*60*24; /* 1 day ago */ + + torture_comment(tctx, "Setting file time to %s", ctime(&t2)); + + if (NT_STATUS_IS_ERR(smbcli_setatr(cli->tree, fname, 0, t2))) { + torture_comment(tctx, "setatr failed (%s)\n", smbcli_errstr(cli->tree)); + correct = true; + } + + if (NT_STATUS_IS_ERR(smbcli_getatr(cli->tree, fname, NULL, NULL, &t))) { + torture_comment(tctx, "getatr failed (%s)\n", smbcli_errstr(cli->tree)); + correct = true; + } + + torture_comment(tctx, "Retrieved file time as %s", ctime(&t)); + + if (t != t2) { + torture_comment(tctx, "ERROR: getatr/setatr bug. times are\n%s", + ctime(&t)); + torture_comment(tctx, "%s", ctime(&t2)); + correct = true; + } + + smbcli_unlink(cli->tree, fname); + + return correct; +} + +/** + This checks a couple of trans2 calls +*/ +static bool run_trans2test(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int fnum; + size_t size; + time_t c_time, a_time, m_time, w_time, m_time2; + const char *fname = "\\trans2.tst"; + const char *dname = "\\trans2"; + const char *fname2 = "\\trans2\\trans2.tst"; + const char *pname; + bool correct = true; + + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Testing qfileinfo\n"); + + fnum = smbcli_open(cli->tree, fname, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + if (NT_STATUS_IS_ERR(smbcli_qfileinfo(cli->tree, fnum, NULL, &size, &c_time, &a_time, &m_time, + NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qfileinfo failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } + + torture_comment(tctx, "Testing NAME_INFO\n"); + + if (NT_STATUS_IS_ERR(smbcli_qfilename(cli->tree, fnum, &pname))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qfilename failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } + + if (!pname || strcmp(pname, fname)) { + torture_result(tctx, TORTURE_FAIL, "qfilename gave different name? [%s] [%s]\n", + fname, pname); + correct = false; + } + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + fnum = smbcli_open(cli->tree, fname, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + smbcli_close(cli->tree, fnum); + + torture_comment(tctx, "Checking for sticky create times\n"); + + if (NT_STATUS_IS_ERR(smbcli_qpathinfo(cli->tree, fname, &c_time, &a_time, &m_time, &size, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } else { + time_t t = time(NULL); + + if (c_time != m_time) { + torture_comment(tctx, "create time=%s", ctime(&c_time)); + torture_comment(tctx, "modify time=%s", ctime(&m_time)); + torture_comment(tctx, "This system appears to have sticky create times\n"); + } + if ((labs(a_time - t) > 60) && (a_time % (60*60) == 0)) { + torture_comment(tctx, "access time=%s", ctime(&a_time)); + torture_result(tctx, TORTURE_FAIL, "This system appears to set a midnight access time\n"); + correct = false; + } + + if (labs(m_time - t) > 60*60*24*7) { + torture_result(tctx, TORTURE_FAIL, "ERROR: totally incorrect times - maybe word reversed? mtime=%s", ctime(&m_time)); + correct = false; + } + } + + + smbcli_unlink(cli->tree, fname); + fnum = smbcli_open(cli->tree, fname, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + smbcli_close(cli->tree, fnum); + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, fname, &c_time, &a_time, &m_time, &w_time, &size, NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } else { + if (w_time < 60*60*24*2) { + torture_comment(tctx, "write time=%s", ctime(&w_time)); + torture_result(tctx, TORTURE_FAIL, "This system appears to set a initial 0 write time\n"); + correct = false; + } + } + + smbcli_unlink(cli->tree, fname); + + + /* check if the server updates the directory modification time + when creating a new file */ + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: mkdir failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } + sleep(3); + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, "\\trans2\\", &c_time, &a_time, &m_time, &w_time, &size, NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } + + fnum = smbcli_open(cli->tree, fname2, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + smbcli_write(cli->tree, fnum, 0, &fnum, 0, sizeof(fnum)); + smbcli_close(cli->tree, fnum); + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, "\\trans2\\", &c_time, &a_time, &m_time2, &w_time, &size, NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", smbcli_errstr(cli->tree)); + correct = false; + } else { + if (m_time2 == m_time) { + torture_result(tctx, TORTURE_FAIL, "This system does not update directory modification times\n"); + correct = false; + } + } + smbcli_unlink(cli->tree, fname2); + smbcli_rmdir(cli->tree, dname); + + return correct; +} + +/* send smb negprot commands, not reading the response */ +static bool run_negprot_nowait(struct torture_context *tctx) +{ + int i; + struct smbcli_state *cli, *cli2; + bool correct = true; + + torture_comment(tctx, "starting negprot nowait test\n"); + + cli = open_nbt_connection(tctx); + if (!cli) { + return false; + } + + torture_comment(tctx, "Filling send buffer\n"); + + for (i=0;i<100;i++) { + struct tevent_req *req; + req = smb_raw_negotiate_send(cli, tctx->ev, + cli->transport, + PROTOCOL_CORE, + PROTOCOL_NT1); + tevent_loop_once(tctx->ev); + if (!tevent_req_is_in_progress(req)) { + NTSTATUS status; + + status = smb_raw_negotiate_recv(req); + TALLOC_FREE(req); + if (i > 0) { + torture_comment(tctx, "Failed to fill pipe packet[%d] - %s (ignored)\n", + i+1, nt_errstr(status)); + break; + } else { + torture_result(tctx, TORTURE_FAIL, "Failed to fill pipe - %s \n", + nt_errstr(status)); + torture_close_connection(cli); + return false; + } + } + } + + torture_comment(tctx, "Opening secondary connection\n"); + if (!torture_open_connection(&cli2, tctx, 1)) { + torture_result(tctx, TORTURE_FAIL, "Failed to open secondary connection\n"); + correct = false; + } + + if (!torture_close_connection(cli2)) { + torture_result(tctx, TORTURE_FAIL, "Failed to close secondary connection\n"); + correct = false; + } + + torture_close_connection(cli); + + return correct; +} + +/** + this checks to see if a secondary tconx can use open files from an + earlier tconx + */ +static bool run_tcon_test(struct torture_context *tctx, struct smbcli_state *cli) +{ + const char *fname = "\\tcontest.tmp"; + int fnum1; + uint16_t cnum1, cnum2, cnum3; + uint16_t vuid1, vuid2; + uint8_t buf[4]; + bool ret = true; + struct smbcli_tree *tree1; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + const char *password = torture_setting_string(tctx, "password", NULL); + + if (smbcli_deltree(cli->tree, fname) == -1) { + torture_comment(tctx, "unlink of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + } + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + + cnum1 = cli->tree->tid; + vuid1 = cli->session->vuid; + + memset(buf, 0, 4); /* init buf so valgrind won't complain */ + if (smbcli_write(cli->tree, fnum1, 0, buf, 130, 4) != 4) { + torture_result(tctx, TORTURE_FAIL, "initial write failed (%s)\n", smbcli_errstr(cli->tree)); + return false; + } + + tree1 = cli->tree; /* save old tree connection */ + if (NT_STATUS_IS_ERR(smbcli_tconX(cli, share, "?????", password))) { + torture_result(tctx, TORTURE_FAIL, "%s refused 2nd tree connect (%s)\n", host, + smbcli_errstr(cli->tree)); + return false; + } + + cnum2 = cli->tree->tid; + cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */ + vuid2 = cli->session->vuid + 1; + + /* try a write with the wrong tid */ + cli->tree->tid = cnum2; + + if (smbcli_write(cli->tree, fnum1, 0, buf, 130, 4) == 4) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with wrong TID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with wrong TID : %s\n", smbcli_errstr(cli->tree)); + } + + + /* try a write with an invalid tid */ + cli->tree->tid = cnum3; + + if (smbcli_write(cli->tree, fnum1, 0, buf, 130, 4) == 4) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with invalid TID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with invalid TID : %s\n", smbcli_errstr(cli->tree)); + } + + /* try a write with an invalid vuid */ + cli->session->vuid = vuid2; + cli->tree->tid = cnum1; + + if (smbcli_write(cli->tree, fnum1, 0, buf, 130, 4) == 4) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with invalid VUID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with invalid VUID : %s\n", smbcli_errstr(cli->tree)); + } + + cli->session->vuid = vuid1; + cli->tree->tid = cnum1; + + if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, "close failed (%s)\n", smbcli_errstr(cli->tree)); + return false; + } + + cli->tree->tid = cnum2; + + if (NT_STATUS_IS_ERR(smbcli_tdis(cli))) { + torture_result(tctx, TORTURE_FAIL, "secondary tdis failed (%s)\n", smbcli_errstr(cli->tree)); + return false; + } + + cli->tree = tree1; /* restore initial tree */ + cli->tree->tid = cnum1; + + smbcli_unlink(tree1, fname); + + return ret; +} + +/** + checks for correct tconX support + */ +static bool run_tcon_devtype_test(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *share = torture_setting_string(tctx, "share", NULL); + + if (!tcon_devtest(tctx, cli1, "IPC$", "A:", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + if (!tcon_devtest(tctx, cli1, "IPC$", "?????", NT_STATUS_OK)) + return false; + + if (!tcon_devtest(tctx, cli1, "IPC$", "LPT:", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + if (!tcon_devtest(tctx, cli1, "IPC$", "IPC", NT_STATUS_OK)) + return false; + + if (!tcon_devtest(tctx, cli1, "IPC$", "FOOBA", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + if (!tcon_devtest(tctx, cli1, share, "A:", NT_STATUS_OK)) + return false; + + if (!tcon_devtest(tctx, cli1, share, "?????", NT_STATUS_OK)) + return false; + + if (!tcon_devtest(tctx, cli1, share, "LPT:", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + if (!tcon_devtest(tctx, cli1, share, "IPC", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + if (!tcon_devtest(tctx, cli1, share, "FOOBA", NT_STATUS_BAD_DEVICE_TYPE)) + return false; + + return true; +} + +static bool rw_torture2(struct torture_context *tctx, + struct smbcli_state *c1, struct smbcli_state *c2) +{ + const char *lockfname = "\\torture2.lck"; + int fnum1; + int fnum2; + int i; + uint8_t buf[131072]; + uint8_t buf_rd[131072]; + bool correct = true; + ssize_t bytes_read, bytes_written; + + torture_assert(tctx, smbcli_deltree(c1->tree, lockfname) != -1, + talloc_asprintf(tctx, + "unlink failed (%s)", smbcli_errstr(c1->tree))); + + fnum1 = smbcli_open(c1->tree, lockfname, O_RDWR | O_CREAT | O_EXCL, + DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, + "first open read/write of %s failed (%s)", + lockfname, smbcli_errstr(c1->tree))); + fnum2 = smbcli_open(c2->tree, lockfname, O_RDONLY, + DENY_NONE); + torture_assert(tctx, fnum2 != -1, + talloc_asprintf(tctx, + "second open read-only of %s failed (%s)", + lockfname, smbcli_errstr(c2->tree))); + + torture_comment(tctx, "Checking data integrity over %d ops\n", + torture_numops); + + for (i=0;itree, fnum1, 0, buf, 0, buf_size)) != buf_size) { + torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c1->tree)); + torture_result(tctx, TORTURE_FAIL, "wrote %d, expected %d\n", (int)bytes_written, (int)buf_size); + correct = false; + break; + } + + if ((bytes_read = smbcli_read(c2->tree, fnum2, buf_rd, 0, buf_size)) != buf_size) { + torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c2->tree)); + torture_result(tctx, TORTURE_FAIL, "read %d, expected %d\n", (int)bytes_read, (int)buf_size); + correct = false; + break; + } + + torture_assert_mem_equal(tctx, buf_rd, buf, buf_size, + "read/write compare failed\n"); + } + + torture_assert_ntstatus_ok(tctx, smbcli_close(c2->tree, fnum2), + talloc_asprintf(tctx, "close failed (%s)", smbcli_errstr(c2->tree))); + torture_assert_ntstatus_ok(tctx, smbcli_close(c1->tree, fnum1), + talloc_asprintf(tctx, "close failed (%s)", smbcli_errstr(c1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_unlink(c1->tree, lockfname), + talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(c1->tree))); + + torture_comment(tctx, "\n"); + + return correct; +} + + + +static bool run_readwritetest(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + torture_comment(tctx, "Running readwritetest v1\n"); + if (!rw_torture2(tctx, cli1, cli2)) + return false; + + torture_comment(tctx, "Running readwritetest v2\n"); + + if (!rw_torture2(tctx, cli1, cli1)) + return false; + + return true; +} + +/* +test the timing of deferred open requests +*/ +static bool run_deferopen(struct torture_context *tctx, struct smbcli_state *cli, int dummy) +{ + const char *fname = "\\defer_open_test.dat"; + int i = 0; + bool correct = true; + int nsec; + int msec; + double sec; + NTSTATUS status; + + nsec = torture_setting_int(tctx, "sharedelay", 1000000); + msec = nsec / 1000; + sec = ((double)nsec) / ((double) 1000000); + + torture_comment(tctx, "pid %u: Testing deferred open requests.\n", + (unsigned)getpid()); + + while (i < 4) { + int fnum = -1; + int j = 1; + + do { + struct timeval tv; + tv = timeval_current(); + + torture_comment(tctx, + "pid %u: create[%d,%d]...\n", + (unsigned)getpid(), i, j); + + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN_IF, 0, 0); + status = smbcli_nt_error(cli->tree); + + torture_comment(tctx, + "pid %u: create[%d,%d] gave fnum %d, status %s\n", + (unsigned)getpid(), i, j, fnum, + nt_errstr(status)); + + if (fnum != -1) { + break; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + double e = timeval_elapsed(&tv); + + torture_comment(tctx, "pid %u: create[%d,%d] " + "time elapsed: %.2f (1 sec = %.2f)\n", + (unsigned)getpid(), i, j, e, sec); + if (e < (0.5 * sec) || e > ((1.5 * sec) + 1.5)) { + torture_comment(tctx, "pid %u: create[%d,%d] " + "timing incorrect\n", + (unsigned)getpid(), i, j); + torture_result(tctx, TORTURE_FAIL, "Timing incorrect %.2f violation 1 sec == %.2f\n", + e, sec); + return false; + } + } + + j++; + + } while (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); + + torture_comment(tctx, + "pid %u: create loop %d done: fnum %d, status %s\n", + (unsigned)getpid(), i, fnum, nt_errstr(status)); + + torture_assert(tctx, fnum != -1, + talloc_asprintf(tctx, + "pid %u: Failed to open %s, error=%s\n", + (unsigned)getpid(), fname, + smbcli_errstr(cli->tree))); + + torture_comment(tctx, "pid %u: open %d\n", (unsigned)getpid(), i); + + smb_msleep(10 * msec); + + status = smbcli_close(cli->tree, fnum); + + torture_comment(tctx, "pid %u: open %d closed, status %s\n", + (unsigned)getpid(), i, nt_errstr(status)); + + torture_assert(tctx, !NT_STATUS_IS_ERR(status), + talloc_asprintf(tctx, + "pid %u: Failed to close %s, " + "error=%s\n", + (unsigned)getpid(), fname, + smbcli_errstr(cli->tree))); + + smb_msleep(2 * msec); + + i++; + } + + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) { + /* All until the last unlink will fail with sharing violation + but also the last request can fail since the file could have + been successfully deleted by another (test) process */ + status = smbcli_nt_error(cli->tree); + if ((!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) + && (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))) { + torture_result(tctx, TORTURE_FAIL, "unlink of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + correct = false; + } + } + + torture_comment(tctx, "pid %u: deferred test finished\n", + (unsigned)getpid()); + return correct; +} + +/** + Try with a wrong vuid and check error message. + */ + +static bool run_vuidtest(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *fname = "\\vuid.tst"; + int fnum; + size_t size; + time_t c_time, a_time, m_time; + + NTSTATUS result; + + smbcli_unlink(cli->tree, fname); + + fnum = smbcli_open(cli->tree, fname, + O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + + cli->session->vuid += 1234; + + torture_comment(tctx, "Testing qfileinfo with wrong vuid\n"); + + if (NT_STATUS_IS_OK(result = smbcli_qfileinfo(cli->tree, fnum, NULL, + &size, &c_time, &a_time, + &m_time, NULL, NULL))) { + torture_fail(tctx, "qfileinfo passed with wrong vuid"); + } + + if (!NT_STATUS_EQUAL(cli->transport->error.e.nt_status, + NT_STATUS_DOS(ERRSRV, ERRbaduid)) && + !NT_STATUS_EQUAL(cli->transport->error.e.nt_status, + NT_STATUS_INVALID_HANDLE)) { + torture_fail(tctx, talloc_asprintf(tctx, + "qfileinfo should have returned DOS error " + "ERRSRV:ERRbaduid\n but returned %s", + smbcli_errstr(cli->tree))); + } + + cli->session->vuid -= 1234; + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum), + talloc_asprintf(tctx, "close failed (%s)", smbcli_errstr(cli->tree))); + + smbcli_unlink(cli->tree, fname); + + return true; +} + +/* + Test open mode returns on read-only files. + */ + static bool run_opentest(struct torture_context *tctx, struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = "\\readonly.file"; + char *control_char_fname; + int fnum1, fnum2; + uint8_t buf[20]; + size_t fsize; + bool correct = true; + char *tmp_path; + int failures = 0; + int i; + + control_char_fname = talloc_strdup(tctx, "\\readonly.afile"); + torture_assert_not_null(tctx, control_char_fname, "asprintf failed\n"); + + for (i = 1; i <= 0x1f; i++) { + control_char_fname[10] = i; + fnum1 = smbcli_nt_create_full(cli1->tree, control_char_fname, 0, SEC_FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (!check_error(__location__, cli1, ERRDOS, ERRinvalidname, + NT_STATUS_OBJECT_NAME_INVALID)) { + torture_result(tctx, TORTURE_FAIL, "Error code should be NT_STATUS_OBJECT_NAME_INVALID, was %s for file with %d char\n", + smbcli_errstr(cli1->tree), i); + failures++; + } + + if (fnum1 != -1) { + smbcli_close(cli1->tree, fnum1); + } + smbcli_setatr(cli1->tree, control_char_fname, 0, 0); + smbcli_unlink(cli1->tree, control_char_fname); + } + TALLOC_FREE(control_char_fname); + + if (!failures) + torture_comment(tctx, "Create file with control char names passed.\n"); + + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, "close2 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_setatr(cli1->tree, fname, FILE_ATTRIBUTE_READONLY, 0))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": smbcli_setatr failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test1); + return false; + } + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_WRITE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test1); + return false; + } + + /* This will fail - but the error should be ERRnoaccess, not ERRbadshare. */ + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_ALL); + + if (check_error(__location__, cli1, ERRDOS, ERRnoaccess, + NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "correct error code ERRDOS/ERRnoaccess returned\n"); + } + + torture_comment(tctx, "finished open test 1\n"); + +error_test1: + smbcli_close(cli1->tree, fnum1); + + /* Now try not readonly and ensure ERRbadshare is returned. */ + + smbcli_setatr(cli1->tree, fname, 0, 0); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_WRITE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + return false; + } + + /* This will fail - but the error should be ERRshare. */ + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_ALL); + + if (check_error(__location__, cli1, ERRDOS, ERRbadshare, + NT_STATUS_SHARING_VIOLATION)) { + torture_comment(tctx, "correct error code ERRDOS/ERRbadshare returned\n"); + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, "close2 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + + smbcli_unlink(cli1->tree, fname); + + torture_comment(tctx, "finished open test 2\n"); + + /* Test truncate open disposition on file opened for read. */ + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "(3) open (1) of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + return false; + } + + /* write 20 bytes. */ + + memset(buf, '\0', 20); + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, 20) != 20) { + torture_result(tctx, TORTURE_FAIL, "write failed (%s)\n", smbcli_errstr(cli1->tree)); + correct = false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, "(3) close1 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + + /* Ensure size == 20. */ + if (NT_STATUS_IS_ERR(smbcli_getatr(cli1->tree, fname, NULL, &fsize, NULL))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (3) getatr failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test3); + return false; + } + + if (fsize != 20) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (3) file size != 20\n"); + CHECK_MAX_FAILURES(error_test3); + return false; + } + + /* Now test if we can truncate a file opened for readonly. */ + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY|O_TRUNC, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (3) open (2) of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test3); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": close2 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + + /* Ensure size == 0. */ + if (NT_STATUS_IS_ERR(smbcli_getatr(cli1->tree, fname, NULL, &fsize, NULL))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (3) getatr failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test3); + return false; + } + + if (fsize != 0) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (3) file size != 0\n"); + CHECK_MAX_FAILURES(error_test3); + return false; + } + torture_comment(tctx, "finished open test 3\n"); +error_test3: + + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + + torture_comment(tctx, "Testing ctemp\n"); + fnum1 = smbcli_ctemp(cli1->tree, "\\", &tmp_path); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": ctemp failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test4); + return false; + } + torture_comment(tctx, "ctemp gave path %s\n", tmp_path); + +error_test4: + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "close of temp failed (%s)\n", smbcli_errstr(cli1->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_unlink(cli1->tree, tmp_path))) { + torture_comment(tctx, "unlink of temp failed (%s)\n", smbcli_errstr(cli1->tree)); + } + + /* Test the non-io opens... */ + + torture_comment(tctx, "Test #1 testing 2 non-io opens (no delete)\n"); + fnum1 = fnum2 = -1; + smbcli_setatr(cli2->tree, fname, 0, 0); + smbcli_unlink(cli2->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 1 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test10); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 1 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test10); + return false; + } + + torture_comment(tctx, "non-io open test #1 passed.\n"); +error_test10: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 1 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 1 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #2 testing 2 non-io opens (first with delete)\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 2 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test20); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 2 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test20); + return false; + } + + torture_comment(tctx, "non-io open test #2 passed.\n"); +error_test20: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 1 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 1 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + torture_comment(tctx, "Test #3 testing 2 non-io opens (second with delete)\n"); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 3 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test30); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 3 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test30); + return false; + } + + torture_comment(tctx, "non-io open test #3 passed.\n"); +error_test30: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 3 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 3 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #4 testing 2 non-io opens (both with delete)\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 4 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test40); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 != -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test40); + return false; + } + + torture_comment(tctx, "Test 4 open 2 of %s gave %s (correct error should be %s)\n", fname, smbcli_errstr(cli2->tree), "sharing violation"); + + torture_comment(tctx, "non-io open test #4 passed.\n"); +error_test40: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 4 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + if (fnum2 != -1 && NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 4 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #5 testing 2 non-io opens (both with delete - both with file share delete)\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 5 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test50); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 5 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test50); + return false; + } + + torture_comment(tctx, "non-io open test #5 passed.\n"); +error_test50: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 5 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 5 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #6 testing 1 non-io open, one io open\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 6 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test60); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 6 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test60); + return false; + } + + torture_comment(tctx, "non-io open test #6 passed.\n"); +error_test60: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 6 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 6 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #7 testing 1 non-io open, one io open with delete\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 7 open 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test70); + return false; + } + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, SEC_STD_DELETE|SEC_FILE_READ_ATTRIBUTE, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 != -1) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Test 7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + CHECK_MAX_FAILURES(error_test70); + return false; + } + + torture_comment(tctx, "Test 7 open 2 of %s gave %s (correct error should be %s)\n", fname, smbcli_errstr(cli2->tree), "sharing violation"); + + torture_comment(tctx, "non-io open test #7 passed.\n"); +error_test70: + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_comment(tctx, "Test 7 close 1 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + } + if (fnum2 != -1 && NT_STATUS_IS_ERR(smbcli_close(cli2->tree, fnum2))) { + torture_comment(tctx, "Test 7 close 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)); + } + + torture_comment(tctx, "Test #8 testing one normal open, followed by lock, followed by open with truncate\n"); + fnum1 = fnum2 = -1; + smbcli_unlink(cli1->tree, fname); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "(8) open (1) of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + return false; + } + + /* write 20 bytes. */ + + memset(buf, '\0', 20); + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, 20) != 20) { + torture_result(tctx, TORTURE_FAIL, "(8) write failed (%s)\n", smbcli_errstr(cli1->tree)); + correct = false; + } + + /* Ensure size == 20. */ + if (NT_STATUS_IS_ERR(smbcli_getatr(cli1->tree, fname, NULL, &fsize, NULL))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (8) getatr (1) failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test80); + return false; + } + + if (fsize != 20) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (8) file size %lu != 20\n", (unsigned long)fsize); + CHECK_MAX_FAILURES(error_test80); + return false; + } + + /* Get an exclusive lock on the open file. */ + if (NT_STATUS_IS_ERR(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (8) lock1 failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test80); + return false; + } + + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "(8) open (2) of %s with truncate failed (%s)\n", fname, smbcli_errstr(cli1->tree)); + return false; + } + + /* Ensure size == 0. */ + if (NT_STATUS_IS_ERR(smbcli_getatr(cli1->tree, fname, NULL, &fsize, NULL))) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (8) getatr (2) failed (%s)\n", smbcli_errstr(cli1->tree)); + CHECK_MAX_FAILURES(error_test80); + return false; + } + + if (fsize != 0) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": (8) file size %lu != 0\n", (unsigned long)fsize); + CHECK_MAX_FAILURES(error_test80); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + torture_result(tctx, TORTURE_FAIL, "(8) close1 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum2))) { + torture_result(tctx, TORTURE_FAIL, "(8) close1 failed (%s)\n", smbcli_errstr(cli1->tree)); + return false; + } + +error_test80: + + torture_comment(tctx, "open test #8 passed.\n"); + + smbcli_unlink(cli1->tree, fname); + + return failures > 0 ? false : correct; +} + +/* FIRST_DESIRED_ACCESS 0xf019f */ +#define FIRST_DESIRED_ACCESS SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA|\ + SEC_FILE_READ_EA| /* 0xf */ \ + SEC_FILE_WRITE_EA|SEC_FILE_READ_ATTRIBUTE| /* 0x90 */ \ + SEC_FILE_WRITE_ATTRIBUTE| /* 0x100 */ \ + SEC_STD_DELETE|SEC_STD_READ_CONTROL|\ + SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER /* 0xf0000 */ +/* SECOND_DESIRED_ACCESS 0xe0080 */ +#define SECOND_DESIRED_ACCESS SEC_FILE_READ_ATTRIBUTE| /* 0x80 */ \ + SEC_STD_READ_CONTROL|SEC_STD_WRITE_DAC|\ + SEC_STD_WRITE_OWNER /* 0xe0000 */ + +#if 0 +#define THIRD_DESIRED_ACCESS FILE_READ_ATTRIBUTE| /* 0x80 */ \ + READ_CONTROL|WRITE_DAC|\ + SEC_FILE_READ_DATA|\ + WRITE_OWNER /* */ +#endif + + + +/** + Test ntcreate calls made by xcopy + */ +static bool run_xcopy(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = "\\test.txt"; + int fnum1, fnum2; + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + FIRST_DESIRED_ACCESS, + FILE_ATTRIBUTE_ARCHIVE, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, + 0x4044, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, + "First open failed - %s", smbcli_errstr(cli1->tree))); + + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SECOND_DESIRED_ACCESS, 0, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN, + 0x200000, 0); + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, + "second open failed - %s", smbcli_errstr(cli1->tree))); + + return true; +} + +static bool run_iometer(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *fname = "\\iobw.tst"; + int fnum; + size_t filesize; + NTSTATUS status; + char buf[2048]; + int ops; + + memset(buf, 0, sizeof(buf)); + + status = smbcli_getatr(cli->tree, fname, NULL, &filesize, NULL); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "smbcli_getatr failed: %s", nt_errstr(status))); + + torture_comment(tctx, "size: %d\n", (int)filesize); + + filesize -= (sizeof(buf) - 1); + + fnum = smbcli_nt_create_full(cli->tree, fname, 0x16, + 0x2019f, 0, 0x3, 3, 0x42, 0x3); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, "open failed: %s", + smbcli_errstr(cli->tree))); + + ops = 0; + + while (true) { + int i, num_reads, num_writes; + + num_reads = random() % 10; + num_writes = random() % 3; + + for (i=0; i torture_numops) { + return true; + } + res = smbcli_read(cli->tree, fnum, buf, + random() % filesize, sizeof(buf)); + torture_assert(tctx, res == sizeof(buf), + talloc_asprintf(tctx, "read failed: %s", + smbcli_errstr(cli->tree))); + } + for (i=0; i torture_numops) { + return true; + } + res = smbcli_write(cli->tree, fnum, 0, buf, + random() % filesize, sizeof(buf)); + torture_assert(tctx, res == sizeof(buf), + talloc_asprintf(tctx, "read failed: %s", + smbcli_errstr(cli->tree))); + } + } +} + +/** + tries variants of chkpath + */ +static bool torture_chkpath_test(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int fnum; + bool ret; + + torture_comment(tctx, "Testing valid and invalid paths\n"); + + /* cleanup from an old run */ + smbcli_rmdir(cli->tree, "\\chkpath.dir\\dir2"); + smbcli_unlink_wcard(cli->tree, "\\chkpath.dir\\*"); + smbcli_rmdir(cli->tree, "\\chkpath.dir"); + + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\chkpath.dir"))) { + torture_result(tctx, TORTURE_FAIL, "mkdir1 failed : %s\n", smbcli_errstr(cli->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\chkpath.dir\\dir2"))) { + torture_result(tctx, TORTURE_FAIL, "mkdir2 failed : %s\n", smbcli_errstr(cli->tree)); + return false; + } + + fnum = smbcli_open(cli->tree, "\\chkpath.dir\\foo.txt", O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, "open1 failed (%s)\n", smbcli_errstr(cli->tree)); + return false; + } + smbcli_close(cli->tree, fnum); + + if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir"))) { + torture_result(tctx, TORTURE_FAIL, "chkpath1 failed: %s\n", smbcli_errstr(cli->tree)); + ret = false; + } + + if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir\\dir2"))) { + torture_result(tctx, TORTURE_FAIL, "chkpath2 failed: %s\n", smbcli_errstr(cli->tree)); + ret = false; + } + + if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir\\foo.txt"))) { + ret = check_error(__location__, cli, ERRDOS, ERRbadpath, + NT_STATUS_NOT_A_DIRECTORY); + } else { + torture_result(tctx, TORTURE_FAIL, "* chkpath on a file should fail\n"); + ret = false; + } + + if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir\\bar.txt"))) { + ret = check_error(__location__, cli, ERRDOS, ERRbadpath, + NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + torture_result(tctx, TORTURE_FAIL, "* chkpath on a non existent file should fail\n"); + ret = false; + } + + if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir\\dirxx\\bar.txt"))) { + ret = check_error(__location__, cli, ERRDOS, ERRbadpath, + NT_STATUS_OBJECT_PATH_NOT_FOUND); + } else { + torture_result(tctx, TORTURE_FAIL, "* chkpath on a non existent component should fail\n"); + ret = false; + } + + smbcli_rmdir(cli->tree, "\\chkpath.dir\\dir2"); + smbcli_unlink_wcard(cli->tree, "\\chkpath.dir\\*"); + smbcli_rmdir(cli->tree, "\\chkpath.dir"); + + return ret; +} + +/* + * This is a test to exercise some weird Samba3 error paths. + */ + +static bool torture_samba3_errorpaths(struct torture_context *tctx) +{ + bool nt_status_support; + bool client_ntlmv2_auth; + struct smbcli_state *cli_nt = NULL, *cli_dos = NULL; + bool result = false; + int fnum; + const char *os2_fname = ".+,;=[]."; + const char *dname = "samba3_errordir"; + union smb_open io; + NTSTATUS status; + + nt_status_support = lpcfg_nt_status_support(tctx->lp_ctx); + client_ntlmv2_auth = lpcfg_client_ntlmv2_auth(tctx->lp_ctx); + + if (!lpcfg_set_cmdline(tctx->lp_ctx, "nt status support", "yes")) { + torture_result(tctx, TORTURE_FAIL, "Could not set 'nt status support = yes'\n"); + goto fail; + } + if (!lpcfg_set_cmdline(tctx->lp_ctx, "client ntlmv2 auth", "yes")) { + torture_result(tctx, TORTURE_FAIL, "Could not set 'client ntlmv2 auth = yes'\n"); + goto fail; + } + + if (!torture_open_connection(&cli_nt, tctx, 0)) { + goto fail; + } + + if (!lpcfg_set_cmdline(tctx->lp_ctx, "nt status support", "no")) { + torture_result(tctx, TORTURE_FAIL, "Could not set 'nt status support = no'\n"); + goto fail; + } + if (!lpcfg_set_cmdline(tctx->lp_ctx, "client ntlmv2 auth", "no")) { + torture_result(tctx, TORTURE_FAIL, "Could not set 'client ntlmv2 auth = no'\n"); + goto fail; + } + + if (!torture_open_connection(&cli_dos, tctx, 1)) { + goto fail; + } + + if (!lpcfg_set_cmdline(tctx->lp_ctx, "nt status support", + nt_status_support ? "yes":"no")) { + torture_result(tctx, TORTURE_FAIL, "Could not reset 'nt status support'"); + goto fail; + } + if (!lpcfg_set_cmdline(tctx->lp_ctx, "client ntlmv2 auth", + client_ntlmv2_auth ? "yes":"no")) { + torture_result(tctx, TORTURE_FAIL, "Could not reset 'client ntlmv2 auth'"); + goto fail; + } + + smbcli_unlink(cli_nt->tree, os2_fname); + smbcli_rmdir(cli_nt->tree, dname); + + if (!NT_STATUS_IS_OK(smbcli_mkdir(cli_nt->tree, dname))) { + torture_result(tctx, TORTURE_FAIL, "smbcli_mkdir(%s) failed: %s\n", dname, + smbcli_errstr(cli_nt->tree)); + goto fail; + } + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 1024*1024; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli_nt->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_OBJECT_NAME_COLLISION)); + goto fail; + } + status = smb_raw_open(cli_dos->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRfilexists))) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_DOS(ERRDOS, ERRfilexists))); + goto fail; + } + + status = smbcli_mkdir(cli_nt->tree, dname); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_OBJECT_NAME_COLLISION)); + goto fail; + } + status = smbcli_mkdir(cli_dos->tree, dname); + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnoaccess))) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_DOS(ERRDOS, ERRnoaccess))); + goto fail; + } + + { + union smb_mkdir md; + md.t2mkdir.level = RAW_MKDIR_T2MKDIR; + md.t2mkdir.in.path = dname; + md.t2mkdir.in.num_eas = 0; + md.t2mkdir.in.eas = NULL; + + status = smb_raw_mkdir(cli_nt->tree, &md); + if (!NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { + torture_comment( + tctx, "(%s) incorrect status %s should be " + "NT_STATUS_OBJECT_NAME_COLLISION\n", + __location__, nt_errstr(status)); + goto fail; + } + status = smb_raw_mkdir(cli_dos->tree, &md); + if (!NT_STATUS_EQUAL(status, + NT_STATUS_DOS(ERRDOS, ERRrename))) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s " + "should be ERRDOS:ERRrename\n", + __location__, nt_errstr(status)); + goto fail; + } + } + + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + status = smb_raw_open(cli_nt->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_OBJECT_NAME_COLLISION)); + goto fail; + } + + status = smb_raw_open(cli_dos->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRfilexists))) { + torture_result(tctx, TORTURE_FAIL, "(%s) incorrect status %s should be %s\n", + __location__, nt_errstr(status), + nt_errstr(NT_STATUS_DOS(ERRDOS, ERRfilexists))); + goto fail; + } + + { + /* Test an invalid DOS deny mode */ + const char *fname = "test.txt"; + + fnum = smbcli_open(cli_nt->tree, fname, O_RDWR | O_CREAT, 5); + if (fnum != -1) { + torture_result(tctx, TORTURE_FAIL, "Open(%s) with invalid deny mode succeeded -- " + "expected failure\n", fname); + smbcli_close(cli_nt->tree, fnum); + goto fail; + } + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli_nt->tree), + NT_STATUS_DOS(ERRDOS,ERRbadaccess))) { + torture_result(tctx, TORTURE_FAIL, "Expected DOS error ERRDOS/ERRbadaccess, " + "got %s\n", smbcli_errstr(cli_nt->tree)); + goto fail; + } + + fnum = smbcli_open(cli_dos->tree, fname, O_RDWR | O_CREAT, 5); + if (fnum != -1) { + torture_result(tctx, TORTURE_FAIL, "Open(%s) with invalid deny mode succeeded -- " + "expected failure\n", fname); + smbcli_close(cli_nt->tree, fnum); + goto fail; + } + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli_nt->tree), + NT_STATUS_DOS(ERRDOS,ERRbadaccess))) { + torture_result(tctx, TORTURE_FAIL, "Expected DOS error ERRDOS:ERRbadaccess, " + "got %s\n", smbcli_errstr(cli_nt->tree)); + goto fail; + } + } + + { + /* + * Samba 3.0.23 has a bug that an existing file can be opened + * as a directory using ntcreate&x. Test this. + */ + + const char *fname = "\\test_dir.txt"; + + fnum = smbcli_open(cli_nt->tree, fname, O_RDWR|O_CREAT, + DENY_NONE); + if (fnum == -1) { + d_printf("(%s) smbcli_open failed: %s\n", __location__, + smbcli_errstr(cli_nt->tree)); + } + smbcli_close(cli_nt->tree, fnum); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.impersonation = + NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + + status = smb_raw_open(cli_nt->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { + torture_result(tctx, TORTURE_FAIL, "ntcreate as dir gave %s, " + "expected NT_STATUS_NOT_A_DIRECTORY\n", + nt_errstr(status)); + result = false; + } + + if (NT_STATUS_IS_OK(status)) { + smbcli_close(cli_nt->tree, io.ntcreatex.out.file.fnum); + } + + status = smb_raw_open(cli_dos->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, + ERRbaddirectory))) { + torture_result(tctx, TORTURE_FAIL, "ntcreate as dir gave %s, " + "expected NT_STATUS_NOT_A_DIRECTORY\n", + nt_errstr(status)); + result = false; + } + + if (NT_STATUS_IS_OK(status)) { + smbcli_close(cli_dos->tree, + io.ntcreatex.out.file.fnum); + } + + smbcli_unlink(cli_nt->tree, fname); + } + + if (!torture_setting_bool(tctx, "samba3", false)) { + goto done; + } + + fnum = smbcli_open(cli_dos->tree, os2_fname, + O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + if (fnum != -1) { + torture_result(tctx, TORTURE_FAIL, "Open(%s) succeeded -- expected failure\n", + os2_fname); + smbcli_close(cli_dos->tree, fnum); + goto fail; + } + + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli_dos->tree), + NT_STATUS_DOS(ERRDOS, ERRcannotopen))) { + torture_result(tctx, TORTURE_FAIL, "Expected DOS error ERRDOS/ERRcannotopen, got %s\n", + smbcli_errstr(cli_dos->tree)); + goto fail; + } + + fnum = smbcli_open(cli_nt->tree, os2_fname, + O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + if (fnum != -1) { + torture_result(tctx, TORTURE_FAIL, "Open(%s) succeeded -- expected failure\n", + os2_fname); + smbcli_close(cli_nt->tree, fnum); + goto fail; + } + + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli_nt->tree), + NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_result(tctx, TORTURE_FAIL, "Expected error NT_STATUS_OBJECT_NAME_NOT_FOUND, " + "got %s\n", smbcli_errstr(cli_nt->tree)); + goto fail; + } + + done: + result = true; + + fail: + if (cli_dos != NULL) { + torture_close_connection(cli_dos); + } + if (cli_nt != NULL) { + torture_close_connection(cli_nt); + } + + return result; +} + +/** + This checks file/dir birthtime +*/ +static void list_fn(struct clilist_file_info *finfo, const char *name, + void *state){ + + /* Just to change dir access time*/ + sleep(5); + +} + +static bool run_birthtimetest(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int fnum; + size_t size; + time_t c_time, a_time, m_time, w_time, c_time1; + const char *fname = "\\birthtime.tst"; + const char *dname = "\\birthtime"; + const char *fname2 = "\\birthtime\\birthtime.tst"; + bool correct = true; + uint8_t buf[16]; + + + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Testing Birthtime for File\n"); + + /* Save File birthtime/creationtime */ + fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + if (NT_STATUS_IS_ERR(smbcli_qfileinfo(cli->tree, fnum, NULL, &size, + &c_time, &a_time, &m_time, NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qfileinfo failed (%s)\n", + smbcli_errstr(cli->tree)); + correct = false; + } + smbcli_close(cli->tree, fnum); + + sleep(10); + + /* Change in File attribute changes file change time*/ + smbcli_setatr(cli->tree, fname, FILE_ATTRIBUTE_SYSTEM, 0); + + fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT , DENY_NONE); + /* Writing updates modification time*/ + smbcli_smbwrite(cli->tree, fnum, &fname, 0, sizeof(fname)); + /*Reading updates access time */ + smbcli_read(cli->tree, fnum, buf, 0, 13); + smbcli_close(cli->tree, fnum); + + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, fname, &c_time1, + &a_time, &m_time, &w_time, &size, NULL, NULL))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", + smbcli_errstr(cli->tree)); + correct = false; + } else { + fprintf(stdout, "c_time = %li, c_time1 = %li\n", + (long) c_time, (long) c_time1); + if (c_time1 != c_time) { + torture_result(tctx, TORTURE_FAIL, "This system updated file \ + birth times! Not expected!\n"); + correct = false; + } + } + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Testing Birthtime for Directory\n"); + + /* check if the server does not update the directory birth time + when creating a new file */ + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) { + torture_result(tctx, TORTURE_FAIL, "ERROR: mkdir failed (%s)\n", + smbcli_errstr(cli->tree)); + correct = false; + } + sleep(3); + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, "\\birthtime\\", + &c_time,&a_time,&m_time,&w_time, &size, NULL, NULL))){ + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", + smbcli_errstr(cli->tree)); + correct = false; + } + + /* Creating a new file changes dir modification time and change time*/ + smbcli_unlink(cli->tree, fname2); + fnum = smbcli_open(cli->tree, fname2, O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + smbcli_smbwrite(cli->tree, fnum, &fnum, 0, sizeof(fnum)); + smbcli_read(cli->tree, fnum, buf, 0, 13); + smbcli_close(cli->tree, fnum); + + /* dir listing changes dir access time*/ + smbcli_list(cli->tree, "\\birthtime\\*", 0, list_fn, cli ); + + if (NT_STATUS_IS_ERR(smbcli_qpathinfo2(cli->tree, "\\birthtime\\", + &c_time1, &a_time, &m_time,&w_time,&size,NULL,NULL))){ + torture_result(tctx, TORTURE_FAIL, "ERROR: qpathinfo2 failed (%s)\n", + smbcli_errstr(cli->tree)); + correct = false; + } else { + fprintf(stdout, "c_time = %li, c_time1 = %li\n", + (long) c_time, (long) c_time1); + if (c_time1 != c_time) { + torture_result(tctx, TORTURE_FAIL, "This system updated directory \ + birth times! Not Expected!\n"); + correct = false; + } + } + smbcli_unlink(cli->tree, fname2); + smbcli_rmdir(cli->tree, dname); + + return correct; +} + +/** + SMB1 TWRP open on root of share. + */ +static bool torture_smb1_twrp_openroot(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *snapshot = NULL; + const char *p = NULL; + NTSTATUS status; + struct tm tm = {}; + bool ret = true; + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_skip(tctx, "missing 'twrp_snapshot' option\n"); + } + + torture_comment(tctx, "Testing open of root of " + "share with timewarp (%s)\n", + snapshot); + + setenv("TZ", "GMT", 1); + + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + cli->session->flags2 |= FLAGS2_REPARSE_PATH; + status = smbcli_chkpath(cli->tree, snapshot); + cli->session->flags2 &= ~FLAGS2_REPARSE_PATH; + + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, + TORTURE_FAIL, + "smbcli_chkpath on %s : %s\n", + snapshot, + smbcli_errstr(cli->tree)); + return false; + } + + done: + + return ret; +} + +static void torture_smb1_find_gmt_mask_list_fn(struct clilist_file_info *finfo, + const char *name, + void *state) +{ +} + +/** + * SMB1 @GMT token as search mask is valid + */ +static bool torture_smb1_find_gmt_mask(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *dname = "\\torture_smb1_find_gmt_mask"; + const char *path = "\\torture_smb1_find_gmt_mask\\@GMT-2022.11.24-16.24.00"; + int fnum; + int n; + NTSTATUS status; + bool ret = true; + + smbcli_unlink(cli->tree, path); + smbcli_rmdir(cli->tree, dname); + + status = smbcli_mkdir(cli->tree, dname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smbcli_mkdir() failed\n"); + fnum = smbcli_open(cli->tree, path, O_RDWR | O_CREAT, DENY_NONE); + smbcli_close(cli->tree, fnum); + + /* Note: we don't set FLAGS2_REPARSE_PATH, so this is just a path */ + n = smbcli_list(cli->tree, path, 0, torture_smb1_find_gmt_mask_list_fn, cli); + torture_assert_int_equal_goto(tctx, n, 1, ret, done, "Wrong count\n"); + +done: + smbcli_unlink(cli->tree, path); + smbcli_rmdir(cli->tree, dname); + return ret; +} + +NTSTATUS torture_base_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "base"); + + torture_suite_add_2smb_test(suite, "fdpass", run_fdpasstest); + torture_suite_add_suite(suite, torture_base_locktest(suite)); + torture_suite_add_1smb_test(suite, "unlink", torture_unlinktest); + torture_suite_add_1smb_test(suite, "attr", run_attrtest); + torture_suite_add_1smb_test(suite, "trans2", run_trans2test); + torture_suite_add_1smb_test(suite, "birthtime", run_birthtimetest); + torture_suite_add_simple_test(suite, "negnowait", run_negprot_nowait); + torture_suite_add_1smb_test(suite, "dir1", torture_dirtest1); + torture_suite_add_1smb_test(suite, "dir2", torture_dirtest2); + torture_suite_add_1smb_test(suite, "deny1", torture_denytest1); + torture_suite_add_2smb_test(suite, "deny2", torture_denytest2); + torture_suite_add_2smb_test(suite, "deny3", torture_denytest3); + torture_suite_add_1smb_test(suite, "denydos", torture_denydos_sharing); + torture_suite_add_smb_multi_test(suite, "ntdeny1", torture_ntdenytest1); + torture_suite_add_2smb_test(suite, "ntdeny2", torture_ntdenytest2); + torture_suite_add_1smb_test(suite, "tcon", run_tcon_test); + torture_suite_add_1smb_test(suite, "tcondev", run_tcon_devtype_test); + torture_suite_add_1smb_test(suite, "vuid", run_vuidtest); + torture_suite_add_2smb_test(suite, "rw1", run_readwritetest); + torture_suite_add_2smb_test(suite, "open", run_opentest); + torture_suite_add_smb_multi_test(suite, "defer_open", run_deferopen); + torture_suite_add_1smb_test(suite, "xcopy", run_xcopy); + torture_suite_add_1smb_test(suite, "iometer", run_iometer); + torture_suite_add_1smb_test(suite, "rename", torture_test_rename); + torture_suite_add_suite(suite, torture_test_delete(suite)); + torture_suite_add_1smb_test(suite, "properties", torture_test_properties); + torture_suite_add_1smb_test(suite, "mangle", torture_mangle); + torture_suite_add_1smb_test(suite, "openattr", torture_openattrtest); + torture_suite_add_1smb_test(suite, "winattr", torture_winattrtest); + torture_suite_add_suite(suite, torture_charset(suite)); + torture_suite_add_1smb_test(suite, "chkpath", torture_chkpath_test); + torture_suite_add_1smb_test(suite, "secleak", torture_sec_leak); + torture_suite_add_simple_test(suite, "disconnect", torture_disconnect); + torture_suite_add_suite(suite, torture_delay_write(suite)); + torture_suite_add_simple_test(suite, "samba3error", torture_samba3_errorpaths); + torture_suite_add_1smb_test(suite, "casetable", torture_casetable); + torture_suite_add_1smb_test(suite, "utable", torture_utable); + torture_suite_add_simple_test(suite, "smb", torture_smb_scan); + torture_suite_add_suite(suite, torture_trans2_aliases(suite)); + torture_suite_add_1smb_test(suite, "trans2-scan", torture_trans2_scan); + torture_suite_add_1smb_test(suite, "nttrans", torture_nttrans_scan); + torture_suite_add_1smb_test(suite, "createx_access", torture_createx_access); + torture_suite_add_2smb_test(suite, "createx_sharemodes_file", torture_createx_sharemodes_file); + torture_suite_add_2smb_test(suite, "createx_sharemodes_dir", torture_createx_sharemodes_dir); + torture_suite_add_1smb_test(suite, "maximum_allowed", torture_maximum_allowed); + + torture_suite_add_simple_test(suite, "bench-holdcon", torture_holdcon); + torture_suite_add_1smb_test(suite, "bench-holdopen", torture_holdopen); + torture_suite_add_simple_test(suite, "bench-readwrite", run_benchrw); + torture_suite_add_smb_multi_test(suite, "bench-torture", run_torture); + torture_suite_add_1smb_test(suite, "scan-pipe_number", run_pipe_number); + torture_suite_add_1smb_test(suite, "scan-ioctl", torture_ioctl_test); + torture_suite_add_1smb_test(suite, "scan-maxfid", torture_maxfid_test); + torture_suite_add_1smb_test(suite, + "smb1-twrp-openroot", + torture_smb1_twrp_openroot); + torture_suite_add_1smb_test(suite, + "smb1-find-gmt-mask", + torture_smb1_find_gmt_mask); + + suite->description = talloc_strdup(suite, + "Basic SMB tests (imported from the original smbtorture)"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/basic/charset.c b/source4/torture/basic/charset.c new file mode 100644 index 0000000..e497489 --- /dev/null +++ b/source4/torture/basic/charset.c @@ -0,0 +1,209 @@ +/* + Unix SMB/CIFS implementation. + + SMB torture tester - charset test routines + + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "param/param.h" +#include "torture/basic/proto.h" + +#define BASEDIR "\\chartest\\" + +/* + open a file using a set of unicode code points for the name + + the prefix BASEDIR is added before the name +*/ +static NTSTATUS unicode_open(struct torture_context *tctx, + struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + uint32_t open_disposition, + const uint32_t *u_name, + size_t u_name_len) +{ + union smb_open io; + char *fname, *fname2=NULL, *ucs_name; + size_t i; + NTSTATUS status; + + ucs_name = talloc_size(mem_ctx, (1+u_name_len)*2); + if (!ucs_name) { + printf("Failed to create UCS2 Name - talloc() failure\n"); + return NT_STATUS_NO_MEMORY; + } + + for (i=0;ilp_ctx), CH_UTF16, CH_UNIX, ucs_name, (1+u_name_len)*2, (void **)&fname, &i)) { + torture_comment(tctx, "Failed to convert UCS2 Name into unix - convert_string_talloc() failure\n"); + talloc_free(ucs_name); + return NT_STATUS_NO_MEMORY; + } + + fname2 = talloc_asprintf(ucs_name, "%s%s", BASEDIR, fname); + if (!fname2) { + talloc_free(ucs_name); + return NT_STATUS_NO_MEMORY; + } + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname2; + io.ntcreatex.in.open_disposition = open_disposition; + + status = smb_raw_open(tree, tctx, &io); + + talloc_free(ucs_name); + + return status; +} + + +/* + see if the server recognises composed characters +*/ +static bool test_composed(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const uint32_t name1[] = {0x61, 0x308}; + const uint32_t name2[] = {0xe4}; + NTSTATUS status1, status2; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "setting up basedir"); + + status1 = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name1, 2); + torture_assert_ntstatus_ok(tctx, status1, "Failed to create composed name"); + + status2 = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name2, 1); + + torture_assert_ntstatus_ok(tctx, status2, "Failed to create accented character"); + + return true; +} + +/* + see if the server recognises a naked diacritical +*/ +static bool test_diacritical(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const uint32_t name1[] = {0x308}; + const uint32_t name2[] = {0x308, 0x308}; + NTSTATUS status1, status2; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "setting up basedir"); + + status1 = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name1, 1); + + torture_assert_ntstatus_ok(tctx, status1, "Failed to create naked diacritical"); + + /* try a double diacritical */ + status2 = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name2, 2); + + torture_assert_ntstatus_ok(tctx, status2, "Failed to create double naked diacritical"); + + return true; +} + +/* + see if the server recognises a partial surrogate pair +*/ +static bool test_surrogate(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const uint32_t name1[] = {0xd800}; + const uint32_t name2[] = {0xdc00}; + const uint32_t name3[] = {0xd800, 0xdc00}; + NTSTATUS status; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "setting up basedir"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name1, 1); + + torture_assert_ntstatus_ok(tctx, status, "Failed to create partial surrogate 1"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name2, 1); + + torture_assert_ntstatus_ok(tctx, status, "Failed to create partial surrogate 2"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name3, 2); + + torture_assert_ntstatus_ok(tctx, status, "Failed to create full surrogate"); + + return true; +} + +/* + see if the server recognises wide-a characters +*/ +static bool test_widea(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const uint32_t name1[] = {'a'}; + const uint32_t name2[] = {0xff41}; + const uint32_t name3[] = {0xff21}; + NTSTATUS status; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "setting up basedir"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name1, 1); + + torture_assert_ntstatus_ok(tctx, status, "Failed to create 'a'"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name2, 1); + + torture_assert_ntstatus_ok(tctx, status, "Failed to create wide-a"); + + status = unicode_open(tctx, cli->tree, tctx, NTCREATEX_DISP_CREATE, name3, 1); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_COLLISION, + "Failed to create wide-A"); + + return true; +} + +struct torture_suite *torture_charset(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "charset"); + + torture_suite_add_1smb_test(suite, "Testing composite character (a umlaut)", test_composed); + torture_suite_add_1smb_test(suite, "Testing naked diacritical (umlaut)", test_diacritical); + torture_suite_add_1smb_test(suite, "Testing partial surrogate", test_surrogate); + torture_suite_add_1smb_test(suite, "Testing wide-a", test_widea); + + return suite; +} diff --git a/source4/torture/basic/cxd_known.h b/source4/torture/basic/cxd_known.h new file mode 100644 index 0000000..2fc0928 --- /dev/null +++ b/source4/torture/basic/cxd_known.h @@ -0,0 +1,8670 @@ +/** + * Results file used for BASE-CREATEX_* TESTS. + */ + +enum { + CXD_CREATEX = 0, + CXD_FILE_READ = 1, + CXD_DIR_ENUMERATE = 1, + CXD_FILE_WRITE = 2, + CXD_DIR_CREATE_CHILD = 2, + CXD_FILE_EXECUTE = 3, + CXD_DIR_TRAVERSE = 3, + CXD_MAX, +} cxd_results; + +enum cxd_test { + CXD_TEST_CREATEX_ACCESS = 0, + CXD_TEST_CREATEX_ACCESS_EXHAUSTIVE = 1, + CXD_TEST_CREATEX_SHAREMODE = 2, + CXD_TEST_CREATEX_SHAREMODE_EXTENDED = 3, +}; + +enum cxd_flags { + CXD_FLAGS_DIRECTORY = 0x1, + CXD_FLAGS_MAKE_BEFORE_CREATEX = 0x2, + + CXD_FLAGS_MASK = 0x3, + CXD_FLAGS_COUNT = CXD_FLAGS_MASK + 1, +}; + +/** + * CXD. + */ +struct createx_data { + /* In. */ + enum cxd_test cxd_test; + enum cxd_flags cxd_flags; + uint32_t cxd_access1; + uint32_t cxd_sharemode1; + uint32_t cxd_access2; + uint32_t cxd_sharemode2; + + /* Out. */ + NTSTATUS cxd_result[CXD_MAX]; + NTSTATUS cxd_result2[CXD_MAX]; +}; + +/** + * Known CXD results, for CREATEX_ACCESS and CREATEX_SHAREMODE. + * Taken by running against a Windows XP Pro 2002 Edition, Service Pack 2. + */ +static const struct createx_data cxd_known[] = { +/** + * CXD_TEST_CREATEX_ACCESS data. + */ + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x4, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x8, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x10, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x20, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x40, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x80, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x4000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x8000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x10000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x20000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x40000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x80000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x1000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x4000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x6000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x7000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x8000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0xa000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0xb000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x10000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x12000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x13000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x20000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x22000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x23000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x40000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x42000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x43000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x80000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x82000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0, .cxd_access1 = 0x83000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x4, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x8, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x10, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x20, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x40, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x80, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x4000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x8000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x10000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x20000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x40000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x80000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x1000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x4000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x6000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x7000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x8000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0xa000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0xb000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x10000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x12000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x13000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x20000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x22000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x23000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x40000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x42000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x43000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x80000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x82000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x1, .cxd_access1 = 0x83000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x4, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x8, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x10, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x20, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x40, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x80, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x4000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x8000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x10000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x20000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x40000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x80000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x1000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x4000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x6000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x7000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x8000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0xa000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0xb000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x10000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x12000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x13000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x20000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x22000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x23000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x40000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x42000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x43000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x80000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x82000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x2, .cxd_access1 = 0x83000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000001, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000002, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x4, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000004, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x8, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000008, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x10, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000010, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x20, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000020, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x40, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000040, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x80, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000080, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000100, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000200, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000400, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000800, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3001000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3002000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x4000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3004000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x8000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3008000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x10000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3010000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x20000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3020000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x40000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3040000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x80000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3080000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3100000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3200000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3400000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3800000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x1000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x2000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x3000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x4000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x6000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x7000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x8000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0xa000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0xb000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x10000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x12000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x13000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x20000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x22000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x23000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x40000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x42000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x43000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x80000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x82000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 0, .cxd_flags = 0x3, .cxd_access1 = 0x83000000, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, +/** + * CXD_TEST_CREATEX_SHAREMODE (file, non extended) + */ + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, +/** + * CXD_TEST_CREATEX_SHAREMODE (dir, non extended) + */ + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=0, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=1, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=2, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=3, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=4, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=5, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=6, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=0, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=1, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=2, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=3, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=4, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=5, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=6, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120089, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x120116, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x12019f, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a0, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1200a9, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201b6, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120089, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x120116, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x12019f, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a0, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1200a9, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201b6, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, + { .cxd_test = 2, .cxd_flags = 0x1, .cxd_access1 = 0x1201bf, .cxd_sharemode1=7, .cxd_access2= 0x1201bf, .cxd_sharemode2=7, .cxd_result = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }, .cxd_result2 = { NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, NT_STATUS_OK, }}, +}; diff --git a/source4/torture/basic/delaywrite.c b/source4/torture/basic/delaywrite.c new file mode 100644 index 0000000..b9d4a06 --- /dev/null +++ b/source4/torture/basic/delaywrite.c @@ -0,0 +1,3095 @@ +/* + Unix SMB/CIFS implementation. + + test suite for delayed write update + + Copyright (C) Volker Lendecke 2004 + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Jeremy Allison 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +#define BASEDIR "\\delaywrite" + +static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo finfo1, finfo2; + const char *fname = BASEDIR "\\torture_file.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx, + "Failed to open %s", fname)); + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time)); + + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + torture_assert_int_equal(tctx, written, 1, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (120 * sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + + if (finfo1.basic_info.out.write_time != + finfo2.basic_info.out.write_time) + { + double diff = timeval_elapsed(&start); + + torture_assert(tctx, + diff >= (used_delay / (double)1000000), + talloc_asprintf(tctx, + "Server updated write_time after %.2f " + "seconds (expected >= %.2f)\n", + diff, used_delay/(double)1000000)); + + torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n", + diff); + break; + } + fflush(stdout); + smb_msleep(1 * msec); + } + + torture_assert_u64_not_equal(tctx, + finfo2.basic_info.out.write_time, + finfo1.basic_info.out.write_time, + "Server did not update write time within " + "120 seconds"); + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file1.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + char buf[2048]; + bool first; + bool updated; + + torture_comment(tctx, "\nRunning test_delayed_write_update1\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx, + "Failed to open %s", fname)); + + memset(buf, 'x', 2048); + written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo1.all_info.in.file.fnum = fnum1; + finfo2 = finfo1; + finfo3 = finfo1; + pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; + pinfo4.all_info.in.file.path = fname; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048, + "file size not as expected after write(2048)"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.all_info.out.write_time)); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + /* Do a zero length SMBwrite call to truncate. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0); + torture_assert_int_equal(tctx, written, 0, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (120 * sec), 0); + first = true; + updated = false; + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 1024, + "file not truncated to expected size " + "(1024)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.all_info.out.write_time)); + + if (finfo1.all_info.out.write_time != + finfo2.all_info.out.write_time) + { + updated = true; + break; + } + + fflush(stdout); + smb_msleep(1 * msec); + first = false; + } + + torture_assert(tctx, updated, + "Server did not update write time within 120 seconds"); + + torture_assert(tctx, first, talloc_asprintf(tctx, + "Server did not update write time immediately but only " + "after %.2f seconds!", timeval_elapsed(&start))); + + torture_comment(tctx, "Server updated write time immediately. Good!\n"); + + fflush(stdout); + smb_msleep(2 * msec); + + /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); + torture_assert_int_equal(tctx, written, 1, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (10*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1024, + "file not truncated to expected size " + "(1024)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo3.all_info.out.write_time)); + + torture_assert_u64_equal(tctx, + finfo3.all_info.out.write_time, + finfo2.all_info.out.write_time, + talloc_asprintf(tctx, + "Server updated write time " + "after %.2f seconds (wrong!)", + timeval_elapsed(&start))); + + fflush(stdout); + smb_msleep(1 * msec); + } + + torture_comment(tctx, "Server did not update write time within 10 " + "seconds. Good!\n"); + + fflush(stdout); + smb_msleep(2 * msec); + + /* the close should trigger an write time update */ + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); + torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); + + torture_assert_u64_not_equal(tctx, + pinfo4.all_info.out.write_time, + finfo3.all_info.out.write_time, + "Server did not update write time on " + "close (wrong!)"); + torture_assert(tctx, + pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time, + "Server updated write time on close, but to an earlier point " + "in time"); + + torture_comment(tctx, "Server updated write time on close (correct)\n"); + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* Updating with a SMBwrite of zero length + * changes the write time immediately - even on expand. */ + +static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file1a.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + char buf[2048]; + bool first; + bool updated; + + torture_comment(tctx, "\nRunning test_delayed_write_update1a\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx, + "Failed to open %s", fname)); + + memset(buf, 'x', 2048); + written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo1.all_info.in.file.fnum = fnum1; + finfo2 = finfo1; + finfo3 = finfo1; + pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; + pinfo4.all_info.in.file.path = fname; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048, + "file size not as expected after write(2048)"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.all_info.out.write_time)); + + /* Do a zero length SMBwrite call to truncate. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0); + + torture_assert_int_equal(tctx, written, 0, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (120*sec), 0); + first = true; + updated = false; + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240, + "file not truncated to expected size " + "(10240)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.all_info.out.write_time)); + + if (finfo1.all_info.out.write_time != + finfo2.all_info.out.write_time) + { + updated = true; + break; + } + + fflush(stdout); + smb_msleep(1 * msec); + first = false; + } + + torture_assert(tctx, updated, + "Server did not update write time within 120 seconds"); + + torture_assert(tctx, first, talloc_asprintf(tctx, + "Server did not update write time immediately but only " + "after %.2f seconds!", timeval_elapsed(&start))); + + torture_comment(tctx, "Server updated write time immediately. Good!\n"); + + fflush(stdout); + smb_msleep(2 * msec); + + /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); + + torture_assert_int_equal(tctx, written, 1, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (10*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240, + "file not truncated to expected size " + "(10240)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo3.all_info.out.write_time)); + + torture_assert_u64_equal(tctx, + finfo3.all_info.out.write_time, + finfo2.all_info.out.write_time, + talloc_asprintf(tctx, + "Server updated write time " + "after %.2f seconds (wrong!)", + timeval_elapsed(&start))); + + fflush(stdout); + smb_msleep(1 * msec); + } + + torture_comment(tctx, "Server did not update write time within 10 " + "seconds. Good!\n"); + + /* the close should trigger an write time update */ + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); + torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); + + torture_assert_u64_not_equal(tctx, + pinfo4.all_info.out.write_time, + finfo3.all_info.out.write_time, + "Server did not update write time on " + "close (wrong!)"); + torture_assert(tctx, + pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time, + "Server updated write time on close, but to an earlier point " + "in time"); + + torture_comment(tctx, "Server updated write time on close (correct)\n"); + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* Updating with a SET_FILE_END_OF_FILE_INFO + * changes the write time immediately - even on expand. */ + +static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file1b.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + char buf[2048]; + bool first; + bool updated; + + torture_comment(tctx, "\nRunning test_delayed_write_update1b\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx, + "Failed to open %s", fname)); + + memset(buf, 'x', 2048); + written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo1.all_info.in.file.fnum = fnum1; + finfo2 = finfo1; + finfo3 = finfo1; + pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; + pinfo4.all_info.in.file.path = fname; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048, + "file size not as expected after write(2048)"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.all_info.out.write_time)); + + /* Do a SET_END_OF_FILE_INFO call to truncate. */ + status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240); + + torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed"); + + start = timeval_current(); + end = timeval_add(&start, (120*sec), 0); + first = true; + updated = false; + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240, + "file not truncated to expected size " + "(10240)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.all_info.out.write_time)); + + if (finfo1.all_info.out.write_time != + finfo2.all_info.out.write_time) + { + updated = true; + break; + } + + fflush(stdout); + smb_msleep(1 * msec); + first = false; + } + + torture_assert(tctx, updated, + "Server did not update write time within 120 seconds"); + + torture_assert(tctx, first, talloc_asprintf(tctx, + "Server did not update write time immediately but only " + "after %.2f seconds!", timeval_elapsed(&start))); + + torture_comment(tctx, "Server updated write time immediately. Good!\n"); + + fflush(stdout); + smb_msleep(2 * msec); + + /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); + + torture_assert_int_equal(tctx, written, 1, + "unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (10*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240, + "file not truncated to expected size " + "(10240)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo3.all_info.out.write_time)); + + torture_assert_u64_equal(tctx, + finfo3.all_info.out.write_time, + finfo2.all_info.out.write_time, + talloc_asprintf(tctx, + "Server updated write time " + "after %.2f seconds (wrong!)", + timeval_elapsed(&start))); + + fflush(stdout); + smb_msleep(1 * msec); + } + + torture_comment(tctx, "Server did not update write time within 10 " + "seconds. Good!\n"); + + /* the close should trigger an write time update */ + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); + torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); + + torture_assert_u64_not_equal(tctx, + pinfo4.all_info.out.write_time, + finfo3.all_info.out.write_time, + "Server did not update write time on " + "close (wrong!)"); + torture_assert(tctx, + pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time, + "Server updated write time on close, but to an earlier point " + "in time"); + + torture_comment(tctx, "Server updated write time on close (correct)\n"); + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */ + +static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_setfileinfo parms; + union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file1c.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + char buf[2048]; + bool first; + bool updated; + + torture_comment(tctx, "\nRunning test_delayed_write_update1c\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx, + "Failed to open %s", fname)); + + memset(buf, 'x', 2048); + written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo1.all_info.in.file.fnum = fnum1; + finfo2 = finfo1; + finfo3 = finfo1; + pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; + pinfo4.all_info.in.file.path = fname; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048, + "file size not as expected after write(2048)"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.all_info.out.write_time)); + + /* Do a SET_ALLOCATION_SIZE call to truncate. */ + parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO; + parms.allocation_info.in.file.fnum = fnum1; + parms.allocation_info.in.alloc_size = 0; + + status = smb_raw_setfileinfo(cli->tree, &parms); + + torture_assert_ntstatus_ok(tctx, status, + "RAW_SFILEINFO_ALLOCATION_INFO failed"); + + start = timeval_current(); + end = timeval_add(&start, (120*sec), 0); + first = true; + updated = false; + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 0, + "file not truncated to expected size " + "(0)"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.all_info.out.write_time)); + + if (finfo1.all_info.out.write_time != + finfo2.all_info.out.write_time) + { + updated = true; + break; + } + + fflush(stdout); + smb_msleep(1 * msec); + first = false; + } + + torture_assert(tctx, updated, + "Server did not update write time within 120 seconds"); + + torture_assert(tctx, first, talloc_asprintf(tctx, + "Server did not update write time immediately but only " + "after %.2f seconds!", timeval_elapsed(&start))); + + torture_comment(tctx, "Server updated write time immediately. Good!\n"); + + fflush(stdout); + smb_msleep(2 * msec); + + /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ + written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); + torture_assert_int_equal(tctx, written, 1, + "Unexpected number of bytes written"); + + start = timeval_current(); + end = timeval_add(&start, (10*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1, + "file not expaneded"); + + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo3.all_info.out.write_time)); + + torture_assert_u64_equal(tctx, + finfo3.all_info.out.write_time, + finfo2.all_info.out.write_time, + talloc_asprintf(tctx, + "Server updated write time " + "after %.2f seconds (wrong!)", + timeval_elapsed(&start))); + + fflush(stdout); + smb_msleep(1 * msec); + } + + torture_comment(tctx, "Server did not update write time within 10 " + "seconds. Good!\n"); + + /* the close should trigger an write time update */ + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); + torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); + + torture_assert_u64_not_equal(tctx, + pinfo4.all_info.out.write_time, + finfo3.all_info.out.write_time, + "Server did not update write time on " + "close (wrong!)"); + torture_assert(tctx, + pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time, + "Server updated write time on close, but to an earlier point " + "in time"); + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Do as above, but using 2 connections. + */ + +static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo1, finfo2; + const char *fname = BASEDIR "\\torture_file.txt"; + NTSTATUS status; + int fnum1 = -1; + int fnum2 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + union smb_flush flsh; + + torture_comment(tctx, "\nRunning test_delayed_write_update2\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + torture_comment(tctx, "Failed to open %s\n", fname); + return false; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time)); + + /* 3 second delay to ensure we get past any 2 second time + granularity (older systems may have that) */ + smb_msleep(3 * msec); + + { + /* Try using setfileinfo instead of write to update write time. */ + union smb_setfileinfo sfinfo; + time_t t_set = time(NULL); + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; + sfinfo.basic_info.in.file.fnum = fnum1; + sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time; + sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time; + + /* I tried this with both + and - ve to see if it makes a different. + It doesn't - once the filetime is set via setfileinfo it stays that way. */ +#if 1 + unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000); +#else + unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000); +#endif + sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time; + sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; + + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + + torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed"); + } + + finfo2.basic_info.in.file.path = fname; + + status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server updated write_time (correct)\n"); + } else { + torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n"); + ret = false; + } + + /* Now try a write to see if the write time gets reset. */ + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + + torture_comment(tctx, "Modified write time %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time)); + + + torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n"); + + written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10); + + if (written != 10) { + torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n", + (int)written, __location__); + return false; + } + + /* Just to prove to tridge that the an smbflush has no effect on + the write time :-). The setfileinfo IS STICKY. JRA. */ + + torture_comment(tctx, "Doing flush after write\n"); + + flsh.flush.level = RAW_FLUSH_FLUSH; + flsh.flush.in.file.fnum = fnum1; + status = smb_raw_flush(cli->tree, &flsh); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status))); + return false; + } + + /* Once the time was set using setfileinfo then it stays set - writes + don't have any effect. But make sure. */ + start = timeval_current(); + end = timeval_add(&start, (15*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + ret = false; + break; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds" + "(wrong!)\n", + diff); + ret = false; + break; + } + fflush(stdout); + smb_msleep(1 * msec); + } + + if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write time (correct)\n"); + } + + fflush(stdout); + smb_msleep(2 * msec); + + fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname); + return false; + } + + torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n"); + + written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10); + + if (written != 10) { + torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n", + (int)written, __location__); + return false; + } + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n"); + ret = false; + } + + torture_comment(tctx, "Closing the first fd to see if write time updated.\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n"); + + written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10); + + if (written != 10) { + torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n", + (int)written, __location__); + return false; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum2; + finfo2 = finfo1; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n"); + ret = false; + } + + /* Once the time was set using setfileinfo then it stays set - writes + don't have any effect. But make sure. */ + start = timeval_current(); + end = timeval_add(&start, (15*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + ret = false; + break; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + fflush(stdout); + smb_msleep(1 * msec); + } + + if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write time (correct)\n"); + } + + torture_comment(tctx, "Closing second fd to see if write time updated.\n"); + + smbcli_close(cli->tree, fnum2); + fnum2 = -1; + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + if (fnum1 == -1) { + torture_comment(tctx, "Failed to open %s\n", fname); + return false; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + + torture_comment(tctx, "Second open initial write time %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time)); + + smb_msleep(10 * msec); + torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n"); + + written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10); + + if (written != 10) { + torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n", + (int)written, __location__); + return false; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + return false; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n"); + ret = false; + } + + /* Now the write time should be updated again */ + start = timeval_current(); + end = timeval_add(&start, (15*sec), 0); + while (!timeval_expired(&end)) { + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); + ret = false; + break; + } + torture_comment(tctx, "write time %s\n", + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + if (diff < (used_delay / (double)1000000)) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds" + "(expected > %.2f) (wrong!)\n", + diff, used_delay / (double)1000000); + ret = false; + break; + } + + torture_comment(tctx, "Server updated write_time after %.2f seconds" + "(correct)\n", + diff); + break; + } + fflush(stdout); + smb_msleep(1*msec); + } + + if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n"); + ret = false; + } + + + /* One more test to do. We should read the filetime via findfirst on the + second connection to ensure it's the same. This is very easy for a Windows + server but a bastard to get right on a POSIX server. JRA. */ + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* Windows does obviously not update the stat info during a write call. I + * *think* this is the problem causing a spurious Excel 2003 on XP error + * message when saving a file. Excel does a setfileinfo, writes, and then does + * a getpath(!)info. Or so... For Samba sometimes it displays an error message + * that the file might have been changed in between. What i've been able to + * trace down is that this happens if the getpathinfo after the write shows a + * different last write time than the setfileinfo showed. This is really + * nasty.... + */ + +static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo1, finfo2; + const char *fname = BASEDIR "\\torture_file.txt"; + NTSTATUS status; + int fnum1 = -1; + int fnum2; + bool ret = true; + ssize_t written; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_finfo_after_write\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + if (!NT_STATUS_IS_OK(status)) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); + goto done; + } + + smb_msleep(1 * msec); + + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", + smbcli_errstr(cli2->tree)); + ret = false; + goto done; + } + + written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1); + + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", + (int)written); + ret = false; + goto done; + } + + finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo2.basic_info.in.file.path = fname; + + status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", + nt_errstr(status)); + ret = false; + goto done; + } + + if (finfo1.basic_info.out.create_time != + finfo2.basic_info.out.create_time) { + torture_result(tctx, TORTURE_FAIL, __location__": create_time changed"); + ret = false; + goto done; + } + + if (finfo1.basic_info.out.access_time != + finfo2.basic_info.out.access_time) { + torture_result(tctx, TORTURE_FAIL, __location__": access_time changed"); + ret = false; + goto done; + } + + if (finfo1.basic_info.out.write_time != + finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n" + "write time conn 1 = %s, conn 2 = %s", + nt_time_string(tctx, finfo1.basic_info.out.write_time), + nt_time_string(tctx, finfo2.basic_info.out.write_time)); + ret = false; + goto done; + } + + if (finfo1.basic_info.out.change_time != + finfo2.basic_info.out.change_time) { + torture_result(tctx, TORTURE_FAIL, __location__": change_time changed"); + ret = false; + goto done; + } + + /* One of the two following calls updates the qpathinfo. */ + + /* If you had skipped the smbcli_write on fnum2, it would + * *not* have updated the stat on disk */ + + smbcli_close(cli2->tree, fnum2); + cli2 = NULL; + + /* This call is only for the people looking at ethereal :-) */ + finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo2.basic_info.in.file.path = fname; + + status = smb_raw_pathinfo(cli->tree, tctx, &finfo2); + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); + ret = false; + goto done; + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +#define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \ + uint64_t r = 10*1000*1000; \ + NTTIME g = (given).basic_info.out.write_time; \ + NTTIME gr = (g / r) * r; \ + NTTIME c = (correct).basic_info.out.write_time; \ + NTTIME cr = (c / r) * r; \ + bool strict = torture_setting_bool(tctx, "strict mode", false); \ + bool err = false; \ + if (strict && (g cmp c)) { \ + err = true; \ + } else if ((g cmp c) && (gr cmp cr)) { \ + /* handle filesystem without high resolution timestamps */ \ + err = true; \ + } \ + if (err) { \ + torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \ + #given, nt_time_string(tctx, g), (unsigned long long)g, \ + #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \ + ret = false; \ + goto done; \ + } \ +} while (0) +#define COMPARE_WRITE_TIME_EQUAL(given,correct) \ + COMPARE_WRITE_TIME_CMP(given,correct,!=) +#define COMPARE_WRITE_TIME_GREATER(given,correct) \ + COMPARE_WRITE_TIME_CMP(given,correct,<=) +#define COMPARE_WRITE_TIME_LESS(given,correct) \ + COMPARE_WRITE_TIME_CMP(given,correct,>=) + +#define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \ + NTTIME g = (given).basic_info.out.access_time; \ + NTTIME c = (correct).basic_info.out.access_time; \ + if (g cmp c) { \ + torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \ + #given, nt_time_string(tctx, g), \ + #cmp, #correct, nt_time_string(tctx, c)); \ + ret = false; \ + goto done; \ + } \ +} while (0) +#define COMPARE_ACCESS_TIME_EQUAL(given,correct) \ + COMPARE_ACCESS_TIME_CMP(given,correct,!=) + +#define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \ + COMPARE_ACCESS_TIME_EQUAL(given,correct); \ + COMPARE_WRITE_TIME_EQUAL(given,correct); \ +} while (0) + +#define GET_INFO_FILE(finfo) do { \ + NTSTATUS _status; \ + _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \ + if (!NT_STATUS_IS_OK(_status)) { \ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \ + nt_errstr(_status)); \ + goto done; \ + } \ + torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \ + nt_time_string(tctx, finfo.basic_info.out.access_time), \ + nt_time_string(tctx, finfo.basic_info.out.write_time)); \ +} while (0) +#define GET_INFO_FILE2(finfo) do { \ + NTSTATUS _status; \ + _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \ + if (!NT_STATUS_IS_OK(_status)) { \ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \ + nt_errstr(_status)); \ + goto done; \ + } \ + torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \ + nt_time_string(tctx, finfo.basic_info.out.access_time), \ + nt_time_string(tctx, finfo.basic_info.out.write_time)); \ +} while (0) +#define GET_INFO_PATH(pinfo) do { \ + NTSTATUS _status; \ + _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \ + if (!NT_STATUS_IS_OK(_status)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \ + nt_errstr(_status)); \ + ret = false; \ + goto done; \ + } \ + torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \ + nt_time_string(tctx, pinfo.basic_info.out.access_time), \ + nt_time_string(tctx, pinfo.basic_info.out.write_time)); \ +} while (0) +#define GET_INFO_BOTH(finfo,pinfo) do { \ + GET_INFO_FILE(finfo); \ + GET_INFO_PATH(pinfo); \ + COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \ +} while (0) + +#define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \ + NTSTATUS _status; \ + union smb_setfileinfo sfinfo; \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \ + sfinfo.basic_info.in.file.fnum = tfnum; \ + sfinfo.basic_info.in.create_time = 0; \ + sfinfo.basic_info.in.access_time = 0; \ + unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \ + sfinfo.basic_info.in.change_time = 0; \ + sfinfo.basic_info.in.attrib = finfo.basic_info.out.attrib; \ + _status = smb_raw_setfileinfo(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(_status)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \ + nt_errstr(_status)); \ + ret = false; \ + goto done; \ + } \ +} while (0) +#define SET_INFO_FILE(finfo, wrtime) \ + SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1) + +#define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \ + NTSTATUS _status; \ + union smb_setfileinfo sfinfo; \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \ + sfinfo.basic_info.in.file.fnum = tfnum; \ + sfinfo.basic_info.in.create_time = 0; \ + sfinfo.basic_info.in.access_time = 0; \ + unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \ + sfinfo.basic_info.in.write_time += (ns); \ + sfinfo.basic_info.in.change_time = 0; \ + sfinfo.basic_info.in.attrib = finfo.basic_info.out.attrib; \ + _status = smb_raw_setfileinfo(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(_status)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \ + nt_errstr(_status)); \ + ret = false; \ + goto done; \ + } \ +} while (0) + +static bool test_delayed_write_update3(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file3.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update3\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* + * make sure the write time is updated 2 seconds later + * calculated from the first write + * (but expect up to 5 seconds extra time for a busy server) + */ + start = timeval_current(); + end = timeval_add(&start, 7 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE(finfo1); + + if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + if (diff < (used_delay / (double)1000000)) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(write time update delay == %.2f) (wrong!)\n", + diff, used_delay / (double)1000000); + ret = false; + break; + } + + torture_comment(tctx, "Server updated write_time after %.2f seconds " + "(correct)\n", + diff); + break; + } + smb_msleep(0.5 * msec); + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo4); + COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); + + if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Show that a truncate write always updates the write time even + * if an initial write has already updated the write time. + */ + +static bool test_delayed_write_update3a(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file3a.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + int i; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update3a\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* + * sleep some time, to demonstrate the handling of write times + * doesn't depend on the time since the open + */ + smb_msleep(5 * msec); + + /* get the initial times */ + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + /* + * make sure the write time is updated 2 seconds later + * calculated from the first write + * (but expect up to 5 seconds extra time for a busy server) + */ + start = timeval_current(); + end = timeval_add(&start, 7 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE(finfo1); + + if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + if (diff < (used_delay / (double)1000000)) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(1sec == %.2f) (wrong!)\n", + diff, sec); + ret = false; + break; + } + + torture_comment(tctx, "Server updated write_time after %.2f seconds " + "(correct)\n", + diff); + break; + } + smb_msleep(0.5 * msec); + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); + + smb_msleep(3 * msec); + + /* + * demonstrate that a truncate write always + * updates the write time immediately + */ + for (i=0; i < 3; i++) { + smb_msleep(2 * msec); + /* do a write */ + torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); + written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + finfo1 = finfo2; + } + + smb_msleep(3 * msec); + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(3 * msec); + + /* get the initial times */ + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2); + + /* + * demonstrate that a truncate write always + * updates the write time immediately + */ + for (i=0; i < 3; i++) { + smb_msleep(2 * msec); + /* do a write */ + torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); + written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + finfo1 = finfo2; + } + + /* sleep */ + smb_msleep(3 * msec); + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + + /* + * the close doesn't update the write time + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo4); + COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3); + + if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Show a close after write updates the write timestamp to + * the close time, not the last write time. + */ + +static bool test_delayed_write_update3b(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file3b.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update3b\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* + * sleep some time, to demonstrate the handling of write times + * doesn't depend on the time since the open + */ + smb_msleep(5 * msec); + + /* get the initial times */ + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + /* + * make sure the write time is updated 2 seconds later + * calculated from the first write + * (but expect up to 5 seconds extra time for a busy server) + */ + start = timeval_current(); + end = timeval_add(&start, 7 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE(finfo1); + + if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + if (diff < (used_delay / (double)1000000)) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds" + "(expected > %.2f) (wrong!)\n", + diff, used_delay / (double)1000000); + ret = false; + break; + } + + torture_comment(tctx, "Server updated write_time after %.2f seconds " + "(write time update delay == %.2f) (correct)\n", + diff, used_delay / (double)1000000); + break; + } + smb_msleep(0.5 * msec); + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo4); + COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); + + if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Check that a write after a truncate write doesn't update + * the timestamp, but a truncate write after a write does. + * Also prove that a close after a truncate write updates the + * timestamp to current, not the time of last write. + */ + +static bool test_delayed_write_update3c(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file3c.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + int i; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update3c\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* + * sleep some time, to demonstrate the handling of write times + * doesn't depend on the time since the open + */ + smb_msleep(5 * msec); + + /* get the initial times */ + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + /* + * demonstrate that a truncate write always + * updates the write time immediately + */ + for (i=0; i < 3; i++) { + smb_msleep(2 * msec); + /* do a write */ + torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); + written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + finfo1 = finfo2; + } + + start = timeval_current(); + end = timeval_add(&start, 7 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE(finfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + /* get the initial times */ + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2); + + /* + * demonstrate that a truncate write always + * updates the write time immediately + */ + for (i=0; i < 3; i++) { + smb_msleep(2 * msec); + /* do a write */ + torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i); + written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + finfo1 = finfo2; + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo4); + COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); + + if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Show only the first write updates the timestamp, and a close + * after writes updates to current (I think this is the same + * as test 3b. JRA). + */ + +static bool test_delayed_write_update4(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4; + const char *fname = BASEDIR "\\torture_file4.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update4\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* sleep a bit */ + smb_msleep(5 * msec); + + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0); + + /* + * make sure the write time is updated 2 seconds later + * calculated from the first write + * (but expect up to 3 seconds extra time for a busy server) + */ + start = timeval_current(); + end = timeval_add(&start, 5 * sec, 0); + while (!timeval_expired(&end)) { + /* get the times after the first write */ + GET_INFO_FILE(finfo1); + + if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + if (diff < (used_delay / (double)1000000)) { + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds" + "(expected > %.2f) (wrong!)\n", + diff, used_delay / (double)1000000); + ret = false; + break; + } + + torture_comment(tctx, "Server updated write_time after %.2f seconds " + "(write time update delay == %.2f) (correct)\n", + diff, used_delay / (double)1000000); + break; + } + smb_msleep(0.5 * msec); + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo2,pinfo2); + + if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); + if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { + torture_comment(tctx, "Server did not updatewrite_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo4); + COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); + + if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Show writes and closes have no effect on updating times once a SETWRITETIME is done. + */ + +static bool test_delayed_write_update5(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6; + const char *fname = BASEDIR "\\torture_file5.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update5\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + finfo4 = finfo0; + finfo5 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + pinfo5 = pinfo0; + pinfo6 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + torture_comment(tctx, "Set write time in the future on the file handle\n"); + SET_INFO_FILE(finfo0, time(NULL) + 86400); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + + torture_comment(tctx, "Set write time in the past on the file handle\n"); + SET_INFO_FILE(finfo0, time(NULL) - 86400); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_LESS(finfo2, finfo1); + + /* make sure the 2 second delay from the first write are canceled */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + + /* get the times after the first write */ + GET_INFO_BOTH(finfo3,pinfo3); + + if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo4,pinfo4); + + if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo4,pinfo4); + COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); + if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo5,pinfo5); + COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); + + /* + * the close doesn't update the write time + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo6); + COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5); + + if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done. + */ + +static bool test_delayed_write_update5b(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6; + const char *fname = BASEDIR "\\torture_fileb.txt"; + int fnum1 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + + torture_comment(tctx, "\nRunning test_delayed_write_update5b\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + finfo4 = finfo0; + finfo5 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + pinfo5 = pinfo0; + pinfo6 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + torture_comment(tctx, "Set write time in the future on the file handle\n"); + SET_INFO_FILE(finfo0, time(NULL) + 86400); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + + torture_comment(tctx, "Set write time in the past on the file handle\n"); + SET_INFO_FILE(finfo0, time(NULL) - 86400); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_LESS(finfo2, finfo1); + + /* make sure the 2 second delay from the first write are canceled */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + + /* get the times after the first write */ + GET_INFO_BOTH(finfo3,pinfo3); + + if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* Do any further write (truncates) update the write time ? */ + start = timeval_current(); + end = timeval_add(&start, 15 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a truncate write on the file handle\n"); + written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo4,pinfo4); + + if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo4,pinfo4); + COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); + if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo5,pinfo5); + COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); + + /* + * the close doesn't update the write time + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo6); + COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5); + + if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + * Open 2 handles on a file. Write one one and then set the + * WRITE TIME explicitly on the other. Ensure the write time + * update is cancelled. Ensure the write time is updated to + * the close time when the non-explicit set handle is closed. + * + */ + +static bool test_delayed_write_update6(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; + union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7; + const char *fname = BASEDIR "\\torture_file6.txt"; + int fnum1 = -1; + int fnum2 = -1; + bool ret = true; + ssize_t written; + struct timeval start; + struct timeval end; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + bool first = true; + + torture_comment(tctx, "\nRunning test_delayed_write_update6\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); +again: + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + if (fnum2 == -1) { + torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n"); + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum2 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + } + + finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo0.basic_info.in.file.fnum = fnum1; + finfo1 = finfo0; + finfo2 = finfo0; + finfo3 = finfo0; + finfo4 = finfo0; + finfo5 = finfo0; + pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + pinfo0.basic_info.in.file.path = fname; + pinfo1 = pinfo0; + pinfo2 = pinfo0; + pinfo3 = pinfo0; + pinfo4 = pinfo0; + pinfo5 = pinfo0; + pinfo6 = pinfo0; + pinfo7 = pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(finfo0,pinfo0); + + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + + GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); + + torture_comment(tctx, "Set write time in the future on the 2nd file handle\n"); + SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); + + torture_comment(tctx, "Set write time in the past on the 2nd file handle\n"); + SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2); + GET_INFO_BOTH(finfo2,pinfo2); + COMPARE_WRITE_TIME_LESS(finfo2, finfo1); + + /* make sure the 2 second delay from the first write are canceled */ + start = timeval_current(); + end = timeval_add(&start, 10 * sec, 0); + while (!timeval_expired(&end)) { + + /* get the times after the first write */ + GET_INFO_BOTH(finfo3,pinfo3); + + if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo3,pinfo3); + COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); + if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sure any further write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 10 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the file handle\n"); + written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_BOTH(finfo4,pinfo4); + + if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + GET_INFO_BOTH(finfo4,pinfo4); + COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); + if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update write_time (correct)\n"); + } + + /* sleep */ + smb_msleep(5 * msec); + + GET_INFO_BOTH(finfo5,pinfo5); + COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); + + /* + * the close updates the write time to the time of the close + * as the write time was set on the 2nd handle + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(pinfo6); + COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5); + + if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + /* See what the second write handle thinks the time is ? */ + finfo5.basic_info.in.file.fnum = fnum2; + GET_INFO_FILE2(finfo5); + COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6); + + /* See if we have lost the sticky write time on handle2 */ + smb_msleep(3 * msec); + torture_comment(tctx, "Have we lost the sticky write time ?\n"); + + /* Make sure any further normal write doesn't update the write time */ + start = timeval_current(); + end = timeval_add(&start, 10 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a write on the second file handle\n"); + written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1); + if (written != 1) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE2(finfo5); + GET_INFO_PATH(pinfo6); + + if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + /* What about a truncate write ? */ + start = timeval_current(); + end = timeval_add(&start, 10 * sec, 0); + while (!timeval_expired(&end)) { + /* do a write */ + torture_comment(tctx, "Do a truncate write on the second file handle\n"); + written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0); + if (written != 0) { + torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); + ret = false; + goto done; + } + /* get the times after the write */ + GET_INFO_FILE2(finfo5); + GET_INFO_PATH(pinfo6); + + if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) { + double diff = timeval_elapsed(&start); + torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds " + "(wrong!)\n", + diff); + ret = false; + break; + } + smb_msleep(1 * msec); + } + + + /* keep the 2nd handle open and rerun tests */ + if (first) { + first = false; + goto again; + } + + /* + * closing the 2nd handle will cause no write time update + * as the write time was explicit set on this handle + */ + torture_comment(tctx, "Close the 2nd file handle\n"); + smbcli_close(cli2->tree, fnum2); + fnum2 = -1; + + GET_INFO_PATH(pinfo7); + COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6); + + if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) { + torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + if (fnum2 != -1) + smbcli_close(cli2->tree, fnum2); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open open_parms; + union smb_fileinfo finfo1, finfo2, finfo3; + const char *fname = BASEDIR "\\torture_file7.txt"; + NTSTATUS status; + int fnum1 = -1; + bool ret = true; + TALLOC_CTX *mem_ctx; + + torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n"); + + mem_ctx = talloc_init("test_delayed_write_update7"); + if (!mem_ctx) return false; + + ZERO_STRUCT(finfo1); + ZERO_STRUCT(finfo2); + ZERO_STRUCT(finfo3); + ZERO_STRUCT(open_parms); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* Create the file. */ + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); + return false; + } + + finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; + finfo1.basic_info.in.file.fnum = fnum1; + finfo2 = finfo1; + finfo3 = finfo1; + + /* Get the initial timestamps. */ + status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + /* Set the pending write time to a value with non zero msec. */ + SET_INFO_FILE_NS(finfo1, time(NULL) + 86400, 103 * NTTIME_MSEC, + cli->tree, fnum1); + + /* Get the current pending write time by fnum. */ + status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + /* Ensure the time is actually different. */ + if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, + "setfileinfo time matches original fileinfo time"); + ret = false; + } + + /* Get the current pending write time by path. */ + finfo3.basic_info.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo3); + + if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, + "qpathinfo time doesn't match fileinfo time"); + ret = false; + } + + /* Now close the file. Re-open and check that the write + time is identical to the one we wrote. */ + + smbcli_close(cli->tree, fnum1); + + open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ; + open_parms.ntcreatex.in.file_attr = 0; + open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + open_parms.ntcreatex.in.create_options = 0; + open_parms.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, mem_ctx, &open_parms); + talloc_free(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "setfileinfo time matches original fileinfo time"); + ret = false; + } + + fnum1 = open_parms.ntcreatex.out.file.fnum; + + /* Check the returned time matches. */ + if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) { + torture_result(tctx, TORTURE_FAIL, + "final open time does not match set time"); + ret = false; + } + + done: + + smbcli_close(cli->tree, fnum1); + + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + Test if creating a file in a directory with an open handle updates the + write timestamp (it should). +*/ +static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo dir_info1, dir_info2; + union smb_open open_parms; + const char *fname = BASEDIR "\\torture_file.txt"; + NTSTATUS status; + int fnum1 = -1; + int fnum2 = -1; + bool ret = true; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8"); + + if (!mem_ctx) return false; + + torture_comment(tctx, "\nRunning test directory write update\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* Open a handle on the directory - and leave it open. */ + ZERO_STRUCT(open_parms); + open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ; + open_parms.ntcreatex.in.file_attr = 0; + open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + open_parms.ntcreatex.in.fname = BASEDIR; + + status = smb_raw_open(cli->tree, mem_ctx, &open_parms); + talloc_free(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "failed to open directory handle"); + ret = false; + goto done; + } + + fnum1 = open_parms.ntcreatex.out.file.fnum; + + /* Store the returned write time. */ + ZERO_STRUCT(dir_info1); + dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time; + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, dir_info1.basic_info.out.write_time)); + + /* sleep */ + smb_msleep(3 * msec); + + /* Now create a file within the directory. */ + fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum2); + + /* Read the directory write time again. */ + ZERO_STRUCT(dir_info2); + dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO; + dir_info2.basic_info.in.file.fnum = fnum1; + + status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + /* Ensure it's been incremented. */ + COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1); + + torture_comment(tctx, "Updated write time %s\n", + nt_time_string(tctx, dir_info2.basic_info.out.write_time)); + + done: + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + testing of delayed update of write_time +*/ +struct torture_suite *torture_delay_write(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "delaywrite"); + + torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write); + torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update); + torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1); + torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a); + torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b); + torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c); + torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2); + torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3); + torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a); + torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b); + torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c); + torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4); + torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5); + torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b); + torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6); + torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); + torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8); + + return suite; +} diff --git a/source4/torture/basic/delete.c b/source4/torture/basic/delete.c new file mode 100644 index 0000000..647f5e0 --- /dev/null +++ b/source4/torture/basic/delete.c @@ -0,0 +1,2624 @@ +/* + Unix SMB/CIFS implementation. + + delete on close testing + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/filesys.h" +#include "libcli/raw/raw_proto.h" + +#include "torture/raw/proto.h" +#include "torture/basic/proto.h" + +static bool check_delete_on_close(struct torture_context *tctx, + struct smbcli_state *cli, int fnum, + const char *fname, bool expect_it, + const char *where) +{ + union smb_search_data data; + NTSTATUS status; + + time_t c_time, a_time, m_time; + size_t size; + uint16_t mode; + + status = torture_single_search(cli, tctx, + fname, + RAW_SEARCH_TRANS2, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, + FILE_ATTRIBUTE_DIRECTORY, + &data); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "single_search failed (%s)", where)); + + if (fnum != -1) { + union smb_fileinfo io; + int nlink = expect_it ? 0 : 1; + + io.all_info.level = RAW_FILEINFO_ALL_INFO; + io.all_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "qfileinfo failed (%s)", where)); + + torture_assert(tctx, expect_it == io.all_info.out.delete_pending, + talloc_asprintf(tctx, + "%s - Expected del_on_close flag %d, qfileinfo/all_info gave %d", + where, expect_it, io.all_info.out.delete_pending)); + + torture_assert(tctx, nlink == io.all_info.out.nlink, + talloc_asprintf(tctx, + "%s - Expected nlink %d, qfileinfo/all_info gave %d", + where, nlink, io.all_info.out.nlink)); + + io.standard_info.level = RAW_FILEINFO_STANDARD_INFO; + io.standard_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "qpathinfo failed (%s)", where)); + + torture_assert(tctx, expect_it == io.standard_info.out.delete_pending, + talloc_asprintf(tctx, "%s - Expected del_on_close flag %d, qfileinfo/standard_info gave %d\n", + where, expect_it, io.standard_info.out.delete_pending)); + + torture_assert(tctx, nlink == io.standard_info.out.nlink, + talloc_asprintf(tctx, "%s - Expected nlink %d, qfileinfo/standard_info gave %d", + where, nlink, io.all_info.out.nlink)); + } + + status = smbcli_qpathinfo(cli->tree, fname, + &c_time, &a_time, &m_time, + &size, &mode); + + if (expect_it) { + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_DELETE_PENDING, + "qpathinfo did not give correct error code"); + } else { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "qpathinfo failed (%s)", where)); + } + + return true; +} + +#define CHECK_STATUS(_cli, _expected) \ + torture_assert_ntstatus_equal(tctx, _cli->tree->session->transport->error.e.nt_status, _expected, \ + "Incorrect status") + +static const char *fname = "\\delete.file"; +static const char *fname_new = "\\delete.new"; +static const char *dname = "\\delete.dir"; + +static void del_clean_area(struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + + smbcli_deltree(cli1->tree, dname); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + smbcli_setatr(cli1->tree, fname_new, 0, 0); + smbcli_unlink(cli1->tree, fname_new); +} + +/* Test 1 - this should delete the file on close. */ + +static bool deltest1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close failed (%s)", smbcli_errstr(cli1->tree))); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail)", + fname)); + + return true; +} + +/* Test 2 - this should delete the file on close. */ +static bool deltest2(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + talloc_asprintf(tctx, "setting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close failed (%s)", + smbcli_errstr(cli1->tree))); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + if (fnum1 != -1) { + printf("(%s) open of %s succeeded should have been deleted on close !\n", + __location__, fname); + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + printf("(%s) close failed (%s)\n", + __location__, smbcli_errstr(cli1->tree)); + return false; + } + smbcli_unlink(cli1->tree, fname); + } + return true; +} + +/* Test 3 - ... */ +static bool deltest3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* This should fail with a sharing violation - open for delete is only compatible + with SHARE_DELETE. */ + + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN, 0, 0); + + torture_assert(tctx, fnum2 == -1, + talloc_asprintf(tctx, "open - 2 of %s succeeded - should have failed.", + fname)); + + /* This should succeed. */ + + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + talloc_asprintf(tctx, "setting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close 1 failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2), + talloc_asprintf(tctx, "close 2 failed (%s)", + smbcli_errstr(cli1->tree))); + + /* This should fail - file should no longer be there. */ + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + if (fnum1 != -1) { + printf("(%s) open of %s succeeded should have been deleted on close !\n", + __location__, fname); + if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) { + printf("(%s) close failed (%s)\n", + __location__, smbcli_errstr(cli1->tree)); + } + smbcli_unlink(cli1->tree, fname); + return false; + } + return true; +} + +/* Test 4 ... */ +static bool deltest4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* This should succeed. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum2), + talloc_asprintf(tctx, "close - 1 failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + talloc_asprintf(tctx, "setting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + /* This should fail - no more opens once delete on close set. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + torture_assert(tctx, fnum2 == -1, + talloc_asprintf(tctx, "open - 3 of %s succeeded ! Should have failed.", + fname )); + + CHECK_STATUS(cli1, NT_STATUS_DELETE_PENDING); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 2 failed (%s)", + smbcli_errstr(cli1->tree))); + + return correct; +} + +/* Test 5 ... */ +static bool deltest5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* This should fail - only allowed on NT opens with DELETE access. */ + + torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_nt_delete_on_close(cli1->tree, fnum1, true)), + "setting delete_on_close on OpenX file succeeded - should fail !"); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 2 failed (%s)", smbcli_errstr(cli1->tree))); + + return true; +} + +/* Test 6 ... */ +static bool deltest6(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* This should fail - only allowed on NT opens with DELETE access. */ + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_nt_delete_on_close(cli1->tree, fnum1, true)), + "setting delete_on_close on file with no delete access succeeded - should fail !"); + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close - 2 failed (%s)", + smbcli_errstr(cli1->tree))); + + return true; +} + +/* Test 7 ... */ +static bool deltest7(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, 0, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + "setting delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, true, __location__); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, false), + "unsetting delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 2 failed (%s)", smbcli_errstr(cli1->tree))); + + /* This next open should succeed - we reset the flag. */ + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 2 failed (%s)", + smbcli_errstr(cli1->tree))); + + return correct; +} + +/* Test 8 ... */ +static bool deltest8(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + "setting delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, true, __location__); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 1 failed (%s)", + smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, -1, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, true, __location__); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), + talloc_asprintf(tctx, "close - 2 failed (%s)", smbcli_errstr(cli2->tree))); + + /* This should fail.. */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded should have been deleted on close !\n", fname)); + + return correct; +} + +/* Test 9 ... */ +static bool deltest9(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + uint32_t disps[4] = { + NTCREATEX_DISP_SUPERSEDE, + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_DISP_CREATE, + NTCREATEX_DISP_OPEN_IF}; + unsigned int i; + + del_clean_area(cli1, cli2); + + for (i = 0; i < sizeof(disps)/sizeof(disps[0]); i++) { + /* This should fail - we need to set DELETE_ACCESS. */ + + /* + * A file or directory create with DELETE_ON_CLOSE but + * without DELETE_ACCESS should fail with + * NT_STATUS_INVALID_PARAMETER. + */ + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + disps[i], + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded " + "should have failed!", + fname)); + + /* Must fail with NT_STATUS_INVALID_PARAMETER. */ + status = smbcli_nt_error(cli1->tree); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_INVALID_PARAMETER, + talloc_asprintf(tctx, "create of %s should return " + "NT_STATUS_INVALID_PARAMETER, got %s", + fname, + smbcli_errstr(cli1->tree))); + + /* This should fail - the file should not have been created. */ + status = smbcli_getatr(cli1->tree, fname, NULL, NULL, NULL); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + talloc_asprintf(tctx, "getattr of %s succeeded should " + "not have been created !", + fname)); + } + + return true; +} + +/* Test 9a ... */ +static bool deltest9a(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + uint32_t disps[4] = { + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_DISP_OPEN, + NTCREATEX_DISP_OVERWRITE, + NTCREATEX_DISP_OPEN_IF}; + + unsigned int i; + + del_clean_area(cli1, cli2); + + /* Create the file, and try with open calls. */ + fnum1 = smbcli_open(cli1->tree, fname, O_CREAT|O_RDWR, DENY_NONE); + torture_assert(tctx, + fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)", + fname, + smbcli_errstr(cli1->tree))); + status = smbcli_close(cli1->tree, fnum1); + torture_assert_ntstatus_ok(tctx, + status, + talloc_asprintf(tctx, "close failed")); + + for (i = 0; i < sizeof(disps)/sizeof(disps[0]); i++) { + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + disps[i], + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded " + "should have failed!", + fname)); + + /* Must fail with NT_STATUS_INVALID_PARAMETER. */ + status = smbcli_nt_error(cli1->tree); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_INVALID_PARAMETER, + talloc_asprintf(tctx, "create of %s should return " + "NT_STATUS_INVALID_PARAMETER, got %s", + fname, + smbcli_errstr(cli1->tree))); + + /* + * This should succeed - the file should not have been deleted. + */ + status = smbcli_getatr(cli1->tree, fname, NULL, NULL, NULL); + torture_assert_ntstatus_ok(tctx, + status, + talloc_asprintf(tctx, "getattr of %s failed %s", + fname, + smbcli_errstr(cli1->tree))); + } + + del_clean_area(cli1, cli2); + return true; +} + +/* Test 10 ... */ +static bool deltest10(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + + del_clean_area(cli1, cli2); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* This should delete the file. */ + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close failed (%s)", + smbcli_errstr(cli1->tree))); + + /* This should fail.. */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded should have been deleted on close !", + fname)); + return true; +} + +/* Test 11 ... */ +static bool deltest11(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + + del_clean_area(cli1, cli2); + + /* test 11 - does having read only attribute still allow delete on close. */ + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_READONLY, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + status = smbcli_nt_delete_on_close(cli1->tree, fnum1, true); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_CANNOT_DELETE, + talloc_asprintf(tctx, "setting delete_on_close should fail with NT_STATUS_CANNOT_DELETE. Got %s instead)", smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close failed (%s)", + smbcli_errstr(cli1->tree))); + + return true; +} + +/* Test 12 ... */ +static bool deltest12(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + + del_clean_area(cli1, cli2); + + /* test 12 - does having read only attribute still allow delete on + * close at time of open. */ + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_READONLY, + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded. Should fail with " + "NT_STATUS_CANNOT_DELETE.\n", fname)); + + status = smbcli_nt_error(cli1->tree); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_CANNOT_DELETE, + talloc_asprintf(tctx, "setting delete_on_close on open should " + "fail with NT_STATUS_CANNOT_DELETE. Got %s " + "instead)", + smbcli_errstr(cli1->tree))); + + return true; +} + +/* Test 13 ... */ +static bool deltest13(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 13: Does resetting the delete on close flag affect a second + * fd? */ + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, + "open of %s failed (%s)", + fname, smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, + true), + "setting delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, true, __location__); + + torture_assert_ntstatus_ok(tctx, smbcli_nt_delete_on_close(cli2->tree, fnum2, + false), + "unsetting delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 1 failed (%s)", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), + talloc_asprintf(tctx, "close - 2 failed (%s)", + smbcli_errstr(cli2->tree))); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDONLY, DENY_NONE); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed!", + fname)); + + smbcli_close(cli1->tree, fnum1); + + return correct; +} + +/* Test 14 ... */ +static bool deltest14(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int dnum1 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 14 -- directory */ + + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, 0, 0); + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s!", + dname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, dnum1, dname, false, __location__); + torture_assert_ntstatus_ok(tctx, smbcli_nt_delete_on_close(cli1->tree, dnum1, true), + "setting delete_on_close on file failed !"); + correct &= check_delete_on_close(tctx, cli1, dnum1, dname, true, __location__); + smbcli_close(cli1->tree, dnum1); + + /* Now it should be gone... */ + + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + torture_assert(tctx, dnum1 == -1, "setting delete_on_close on file succeeded !"); + + return correct; +} + +/* Test 15 ... */ +static bool deltest15(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + bool correct = true; + int fnum2 = -1; + NTSTATUS status; + + del_clean_area(cli1, cli2); + + /* Test 15: delete on close under rename */ + + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + smbcli_unlink(cli1->tree, fname_new); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open - 1 of %s failed (%s)", fname, smbcli_errstr(cli1->tree))); + + status = smbcli_rename(cli2->tree, fname, fname_new); + + torture_assert_ntstatus_ok(tctx, status, "renaming failed!"); + + fnum2 = smbcli_nt_create_full(cli2->tree, fname_new, 0, + SEC_GENERIC_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + + torture_assert(tctx, fnum2 != -1, + talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname_new, smbcli_errstr(cli1->tree))); + + status = smbcli_nt_delete_on_close(cli2->tree, fnum2, true); + + torture_assert_ntstatus_ok(tctx, status, + "setting delete_on_close on file failed !"); + + smbcli_close(cli2->tree, fnum2); + + /* The file should be around under the new name, there's a second + * handle open */ + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname_new, true, __location__); + + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_GENERIC_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + smbcli_close(cli2->tree, fnum2); + smbcli_close(cli1->tree, fnum1); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_EA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, fnum1); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname_new, 0, + SEC_FILE_READ_EA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + torture_assert(tctx, fnum1 == -1, + "smbcli_open succeeded, should have " + "failed"); + + return correct; +} + +/* Test 16 ... */ +static bool deltest16(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 16. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly create with all access, but delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert (tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, -1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, -1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, true, __location__); + + smbcli_close(cli2->tree, fnum2); + + /* And the file should be deleted ! */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 16 ... */ +static bool deltest16a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 16. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Firstly create with all access, but delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert (tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, -1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, -1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, -1, fname, false, __location__); + + smbcli_close(cli2->tree, fnum2); + + /* And the file should be deleted ! */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, fnum1); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + return correct; +} + +/* Test 17 ... */ +static bool deltest17(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 17. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + /* After the first close, the files has the delete on close bit set. */ + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, true, __location__); + + smbcli_close(cli1->tree, fnum2); + + /* Make sure the file has been deleted */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s failed (should succeed) - %s", + fname, smbcli_errstr(cli1->tree))); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 17a - like 17, but the delete on close handle is closed last */ +static bool deltest17a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum2); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + /* + * The file is still there: + * The second open seems to have removed the initial + * delete on close flag from the first handle + */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 3 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, fnum1); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + return correct; +} + +/* Test 17b - like 17a, but the initial delete on close is set on the second handle */ +static bool deltest17b(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum2); + + /* Make sure the file has been deleted */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 3 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 17c - like 17, but the initial delete on close is set on the second handle */ +static bool deltest17c(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum2); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, true, __location__); + + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 == -1, talloc_asprintf(tctx, "open - 3 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_DELETE_PENDING); + + smbcli_close(cli1->tree, fnum1); + + /* Make sure the file has been deleted */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 4 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 17d - like 17a, but the first delete-on-close opener creates the file */ +static bool deltest17d(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + + /* Create the file with delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum2); + + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + /* + * The file is still there: + * The second open seems to have removed the initial + * delete on close flag from the first handle + */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 3 of %s succeed (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +static bool deltest17e(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + int fnum3 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum3 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 3 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + /* + * closing the handle that has delete_on_close set + * inherits the flag to the global context + */ + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, true, __location__); + + smbcli_close(cli1->tree, fnum2); + + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, true, __location__); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 4 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_DELETE_PENDING); + + smbcli_close(cli1->tree, fnum3); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 5 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +static bool deltest17f(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + int fnum3 = -1; + bool correct = true; + NTSTATUS status; + + del_clean_area(cli1, cli2); + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + + /* Next open with all access, but add delete on close. */ + fnum2 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + + /* Now try opening again for read-only. */ + fnum3 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + /* Should work. */ + torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx, "open - 3 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* still not reported as being set on either */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + /* + * closing the handle that has delete_on_close set + * inherits the flag to the global context + */ + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, true, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, true, __location__); + + + status = smbcli_nt_delete_on_close(cli1->tree, fnum2, false); + torture_assert_ntstatus_ok(tctx, status, + "clearing delete_on_close on file failed !"); + + correct &= check_delete_on_close(tctx, cli1, fnum2, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, false, __location__); + + smbcli_close(cli1->tree, fnum2); + + correct &= check_delete_on_close(tctx, cli1, fnum3, fname, true, __location__); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 4 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_DELETE_PENDING); + + smbcli_close(cli1->tree, fnum3); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open - 5 of %s succeeded (should fail)", + fname)); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 18 ... */ +static bool deltest18(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 18. With directories. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + + smbcli_deltree(cli1->tree, dname); + + /* Firstly create with all access, but delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY|NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + + /* + * The delete on close bit is *not* reported as being set. + * Win2k3/win2k8 should pass this check, but WinXPsp2 reports delete on + * close as being set. This causes the subsequent create to fail with + * NT_STATUS_DELETE_PENDING. + */ + correct &= check_delete_on_close(tctx, cli1, fnum1, dname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, fnum1, dname, false, __location__); + correct &= check_delete_on_close(tctx, cli1, fnum2, dname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli1, fnum2, dname, true, __location__); + + smbcli_close(cli1->tree, fnum2); + + /* And the directory should be deleted ! */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail)", + dname)); + + return correct; +} + +/* Test 19 ... */ +static bool deltest19(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 19. */ + + smbcli_deltree(cli1->tree, dname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the directory. */ + smbcli_close(cli1->tree, fnum1); + + /* Next open with all access, but add delete on close. */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY|NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open - 1 of %s failed (%s)", fname, smbcli_errstr(cli1->tree))); + + /* + * The delete on close bit is *not* reported as being set. + * Win2k3/win2k8 should pass this check, but WinXPsp2 reports delete on + * close as being set. This causes the subsequent create to fail with + * NT_STATUS_DELETE_PENDING. + */ + correct &= check_delete_on_close(tctx, cli1, fnum1, dname, false, __location__); + + /* Now try opening again for read-only. */ + fnum2 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + /* Should work. */ + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + dname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli1, fnum2, dname, true, __location__); + + smbcli_close(cli1->tree, fnum2); + + /* See if the file is deleted - for a directory this seems to be true ! */ + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded (should fail)", dname)); + + return correct; +} + +/* Test 20 ... */ +static bool deltest20(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int dnum1 = -1; + bool correct = true; + NTSTATUS status; + int ret; + + if (geteuid() == 0) { + torture_skip(tctx, "This test doesn't work as user root."); + } + + del_clean_area(cli1, cli2); + + /* Test 20 -- non-empty directory hardest to get right... */ + + smbcli_deltree(cli1->tree, dname); + + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY, 0); + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s!", + dname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, dnum1, dname, false, __location__); + status = smbcli_nt_delete_on_close(cli1->tree, dnum1, true); + + { + char *fullname; + ret = asprintf(&fullname, "\\%s%s", dname, fname); + torture_assert(tctx, ret != -1, "asprintf failed"); + fnum1 = smbcli_open(cli1->tree, fullname, O_CREAT|O_RDWR, + DENY_NONE); + torture_assert(tctx, fnum1 == -1, + "smbcli_open succeeded, should have " + "failed with NT_STATUS_DELETE_PENDING" + ); + + torture_assert_ntstatus_equal(tctx, + smbcli_nt_error(cli1->tree), + NT_STATUS_DELETE_PENDING, + "smbcli_open failed"); + } + + status = smbcli_nt_delete_on_close(cli1->tree, dnum1, false); + torture_assert_ntstatus_ok(tctx, status, + "unsetting delete_on_close on file failed !"); + + { + char *fullname; + ret = asprintf(&fullname, "\\%s%s", dname, fname); + torture_assert(tctx, ret != -1, "asprintf failed"); + fnum1 = smbcli_open(cli1->tree, fullname, O_CREAT|O_RDWR, + DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "smbcli_open failed: %s\n", + smbcli_errstr(cli1->tree))); + smbcli_close(cli1->tree, fnum1); + } + + status = smbcli_nt_delete_on_close(cli1->tree, dnum1, true); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_DIRECTORY_NOT_EMPTY, + "setting delete_on_close failed"); + smbcli_close(cli1->tree, dnum1); + + return correct; +} + +/* Test 20a ... */ +static bool deltest20a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 20a. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* Next open with all access, but add delete on close. */ + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli2->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + smbcli_close(cli2->tree, fnum2); + + /* See if the file is deleted - should be.... */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail) - %s", + fname, smbcli_errstr(cli1->tree))); + + return correct; +} + +/* Test 20b ... */ +/* This is the delete semantics that the cifsfs client depends on when + * trying to delete an open file on a Windows server. It + * opens a file with initial delete on close set, renames it then closes + * all open handles. The file goes away on Windows. + */ + +static bool deltest20b(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int fnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 20b. */ + + /* Ensure the file doesn't already exist. */ + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_setatr(cli1->tree, fname, 0, 0); + smbcli_unlink(cli1->tree, fname); + smbcli_setatr(cli1->tree, fname_new, 0, 0); + smbcli_unlink(cli1->tree, fname_new); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Firstly open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open - 1 of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + /* Next open with all access, but add delete on close. */ + fnum2 = smbcli_nt_create_full(cli2->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open - 2 of %s failed (%s)", + fname, smbcli_errstr(cli2->tree))); + + /* The delete on close bit is *not* reported as being set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, false, __location__); + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + smbcli_close(cli1->tree, fnum1); + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname, false, __location__); + + /* Rename the file by handle. */ + + { + union smb_setfileinfo sfinfo; + NTSTATUS status; + + memset(&sfinfo, '\0', sizeof(sfinfo)); + sfinfo.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.generic.in.file.fnum = fnum2; + sfinfo.rename_information.in.root_fid = 0; + /* Don't start the filename with '\\', we get NT_STATUS_NOT_SUPPORTED if so. */ + sfinfo.rename_information.in.new_name = fname_new + 1; + sfinfo.rename_information.in.overwrite = 1; + + status = smb_raw_setfileinfo(cli2->tree, &sfinfo); + + torture_assert_ntstatus_equal(tctx,status,NT_STATUS_OK,talloc_asprintf(tctx, "rename of %s to %s failed (%s)", + fname, fname_new, smbcli_errstr(cli2->tree))); + } + + correct &= check_delete_on_close(tctx, cli2, fnum2, fname_new, false, __location__); + + smbcli_close(cli2->tree, fnum2); + + /* See if the file is deleted - should be.... */ + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail) - %s", + fname, smbcli_errstr(cli1->tree))); + fnum1 = smbcli_open(cli1->tree, fname_new, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum1 == -1, talloc_asprintf(tctx, "open of %s succeeded (should fail) - %s", + fname_new, smbcli_errstr(cli1->tree))); + + return correct; +} + +/* Test 20c */ +/* Along the lines of deltest20 we try to open a non-empty directory with delete + * on close set and subsequent close to verify its presence in the tree. + */ +static bool deltest20c(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + int fnum1 = -1; + int dnum1 = -1; + int ret; + char *fullname; + + del_clean_area(cli1, cli2); + + smbcli_deltree(cli1->tree, dname); + + /* Firstly open and create with all access */ + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY, 0); + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s", + dname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the directory */ + smbcli_close(cli1->tree, dnum1); + + ret = asprintf(&fullname, "\\%s%s", dname, fname); + torture_assert(tctx, ret != -1, "asprintf failed"); + + /* Open and create with all access */ + fnum1 = smbcli_nt_create_full(cli1->tree, fullname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + 0, 0); + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s", + fname, smbcli_errstr(cli1->tree))); + + /* And close - just to create the file. */ + smbcli_close(cli1->tree, fnum1); + + /* Open with all access, but add delete on close */ + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY|NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + /* Should work */ + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s", + dname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, dnum1); + + /* Try to open again */ + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + /* Directory should be still present*/ + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, "open of %s failed: %s", + dname, smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, dnum1); + + return true; +} + +/* Test 21 ... */ +static bool deltest21(struct torture_context *tctx) +{ + int fnum1 = -1; + struct smbcli_state *cli1; + struct smbcli_state *cli2; + bool correct = true; + + if (!torture_open_connection(&cli1, tctx, 0)) + return false; + + if (!torture_open_connection(&cli2, tctx, 1)) + return false; + + del_clean_area(cli1, cli2); + + /* Test 21 -- Test removal of file after socket close. */ + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", + fname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_nt_delete_on_close(cli1->tree, fnum1, true), + talloc_asprintf(tctx, "setting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + /* Ensure delete on close is set. */ + correct &= check_delete_on_close(tctx, cli1, fnum1, fname, true, __location__); + + /* Now yank the rug from under cli1. */ + smbcli_transport_dead(cli1->transport, NT_STATUS_LOCAL_DISCONNECT); + + fnum1 = -1; + + if (!torture_open_connection(&cli1, tctx, 0)) { + return false; + } + + /* On slow build farm machines it might happen that they are not fast + * enough to delete the file for this test */ + smb_msleep(200); + + /* File should not be there. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 22 ... */ + +/* + * Test whether a second *directory* handle inhibits delete if the first has + * del-on-close set and is closed + */ +static bool deltest22(struct torture_context *tctx) +{ + int dnum1 = -1; + int dnum2 = -1; + struct smbcli_state *cli1; + bool correct = true; + + if (!torture_open_connection(&cli1, tctx, 0)) + return false; + + smbcli_deltree(cli1->tree, dname); + + torture_assert_ntstatus_ok( + tctx, smbcli_mkdir(cli1->tree, dname), + talloc_asprintf(tctx, "smbcli_mdir failed: (%s)\n", + smbcli_errstr(cli1->tree))); + + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, dnum1 != -1, + talloc_asprintf(tctx, "open of %s failed: %s!", + dname, smbcli_errstr(cli1->tree))); + + dnum2 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, dnum2 != -1, + talloc_asprintf(tctx, "open of %s failed: %s!", + dname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok( + tctx, smbcli_nt_delete_on_close(cli1->tree, dnum1, true), + talloc_asprintf(tctx, "setting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, dnum1); + + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, dnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded!\n", + dname)); + + CHECK_STATUS(cli1, NT_STATUS_DELETE_PENDING); + + smbcli_close(cli1->tree, dnum2); + CHECK_STATUS(cli1, NT_STATUS_OK); + + return correct; +} + +/* Test 23 - Second directory open fails when delete is pending. */ +static bool deltest23(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int dnum1 = -1; + int dnum2 = -1; + bool correct = true; + + del_clean_area(cli1, cli2); + + /* Test 23 -- Basic delete on close for directories. */ + + /* Open a directory */ + dnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, dnum1 != -1, talloc_asprintf(tctx, + "open of %s failed: %s!", + dname, smbcli_errstr(cli1->tree))); + + correct &= check_delete_on_close(tctx, cli1, dnum1, dname, false, + __location__); + + /* Set delete on close */ + (void)smbcli_nt_delete_on_close(cli1->tree, dnum1, true); + + /* Attempt opening the directory again. It should fail. */ + dnum2 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + + torture_assert(tctx, dnum2 == -1, talloc_asprintf(tctx, + "open of %s succeeded: %s. It should have failed " + "with NT_STATUS_DELETE_PENDING", + dname, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), + NT_STATUS_DELETE_PENDING, "smbcli_open failed"); + + return correct; +} + +/* Test 24 ... */ + +/* + * Test whether unsetting delete-on-close before the close has any effect. + * It should be ignored. + */ +static bool deltest24(struct torture_context *tctx) +{ + int fnum1 = -1; + struct smbcli_state *cli1; + bool correct = true; + + if (!torture_open_connection(&cli1, tctx, 0)) + return false; + + smbcli_deltree(cli1->tree, fname); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA| + SEC_STD_DELETE, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed: %s!", + fname, smbcli_errstr(cli1->tree))); + + /* Now, unset Delete-On-Close, but it should have no effect */ + torture_assert_ntstatus_ok( + tctx, smbcli_nt_delete_on_close(cli1->tree, fnum1, false), + talloc_asprintf(tctx, "unsetting delete_on_close failed (%s)", + smbcli_errstr(cli1->tree))); + + smbcli_close(cli1->tree, fnum1); + + /* File should not be there. */ + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, + 0, 0); + + CHECK_STATUS(cli1, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return correct; +} + +/* Test 25 ... */ +static bool deltest25(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + uint32_t disps[4] = { + NTCREATEX_DISP_SUPERSEDE, + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_DISP_CREATE, + NTCREATEX_DISP_OPEN_IF}; + unsigned int i; + + del_clean_area(cli1, cli2); + + for (i = 0; i < sizeof(disps)/sizeof(disps[0]); i++) { + /* This should fail - we need to set DELETE_ACCESS. */ + + /* + * A file or directory create with DELETE_ON_CLOSE but + * without DELETE_ACCESS should fail with + * NT_STATUS_INVALID_PARAMETER. + */ + + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_NONE, + disps[i], + NTCREATEX_OPTIONS_DIRECTORY| + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded " + "should have failed!", + dname)); + + /* Must fail with NT_STATUS_INVALID_PARAMETER. */ + status = smbcli_nt_error(cli1->tree); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_INVALID_PARAMETER, + talloc_asprintf(tctx, "create of %s should return " + "NT_STATUS_INVALID_PARAMETER, got %s", + dname, + smbcli_errstr(cli1->tree))); + + /* + * This should fail - the directory + * should not have been created. + */ + status = smbcli_getatr(cli1->tree, dname, NULL, NULL, NULL); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + talloc_asprintf(tctx, "getattr of %s succeeded should " + "not have been created !", + dname)); + } + + return true; +} + +/* Test 25a... */ +static bool deltest25a(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int fnum1 = -1; + NTSTATUS status; + uint32_t disps[4] = { + NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_DISP_OPEN, + NTCREATEX_DISP_OVERWRITE, + NTCREATEX_DISP_OPEN_IF}; + + unsigned int i; + + del_clean_area(cli1, cli2); + + /* Create the directory, and try with open calls. */ + status = smbcli_mkdir(cli1->tree, dname); + torture_assert_ntstatus_ok(tctx, + status, + talloc_asprintf(tctx, "mkdir of %s failed %s", + dname, + smbcli_errstr(cli1->tree))); + + for (i = 0; i < sizeof(disps)/sizeof(disps[0]); i++) { + fnum1 = smbcli_nt_create_full(cli1->tree, dname, 0, + SEC_FILE_READ_DATA, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_NONE, + disps[i], + NTCREATEX_OPTIONS_DIRECTORY| + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + torture_assert(tctx, fnum1 == -1, + talloc_asprintf(tctx, "open of %s succeeded " + "should have failed!", + dname)); + + /* Must fail with NT_STATUS_INVALID_PARAMETER. */ + status = smbcli_nt_error(cli1->tree); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_INVALID_PARAMETER, + talloc_asprintf(tctx, "create of %s should return " + "NT_STATUS_INVALID_PARAMETER, got %s", + dname, + smbcli_errstr(cli1->tree))); + + /* + * This should succeed - the directory + * should not have been deleted. + */ + status = smbcli_getatr(cli1->tree, dname, NULL, NULL, NULL); + torture_assert_ntstatus_ok(tctx, + status, + talloc_asprintf(tctx, "getattr of %s failed %s", + fname, + smbcli_errstr(cli1->tree))); + } + + del_clean_area(cli1, cli2); + return true; +} + +/* + Test delete on close semantics. + */ +struct torture_suite *torture_test_delete(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "delete"); + + torture_suite_add_2smb_test(suite, "deltest1", deltest1); + torture_suite_add_2smb_test(suite, "deltest2", deltest2); + torture_suite_add_2smb_test(suite, "deltest3", deltest3); + torture_suite_add_2smb_test(suite, "deltest4", deltest4); + torture_suite_add_2smb_test(suite, "deltest5", deltest5); + torture_suite_add_2smb_test(suite, "deltest6", deltest6); + torture_suite_add_2smb_test(suite, "deltest7", deltest7); + torture_suite_add_2smb_test(suite, "deltest8", deltest8); + torture_suite_add_2smb_test(suite, "deltest9", deltest9); + torture_suite_add_2smb_test(suite, "deltest9a", deltest9a); + torture_suite_add_2smb_test(suite, "deltest10", deltest10); + torture_suite_add_2smb_test(suite, "deltest11", deltest11); + torture_suite_add_2smb_test(suite, "deltest12", deltest12); + torture_suite_add_2smb_test(suite, "deltest13", deltest13); + torture_suite_add_2smb_test(suite, "deltest14", deltest14); + torture_suite_add_2smb_test(suite, "deltest15", deltest15); + torture_suite_add_2smb_test(suite, "deltest16", deltest16); + torture_suite_add_2smb_test(suite, "deltest16a", deltest16a); + torture_suite_add_2smb_test(suite, "deltest17", deltest17); + torture_suite_add_2smb_test(suite, "deltest17a", deltest17a); + torture_suite_add_2smb_test(suite, "deltest17b", deltest17b); + torture_suite_add_2smb_test(suite, "deltest17c", deltest17c); + torture_suite_add_2smb_test(suite, "deltest17d", deltest17d); + torture_suite_add_2smb_test(suite, "deltest17e", deltest17e); + torture_suite_add_2smb_test(suite, "deltest17f", deltest17f); + torture_suite_add_2smb_test(suite, "deltest18", deltest18); + torture_suite_add_2smb_test(suite, "deltest19", deltest19); + torture_suite_add_2smb_test(suite, "deltest20", deltest20); + torture_suite_add_2smb_test(suite, "deltest20a", deltest20a); + torture_suite_add_2smb_test(suite, "deltest20b", deltest20b); + torture_suite_add_2smb_test(suite, "deltest20c", deltest20c); + torture_suite_add_simple_test(suite, "deltest21", deltest21); + torture_suite_add_simple_test(suite, "deltest22", deltest22); + torture_suite_add_2smb_test(suite, "deltest23", deltest23); + torture_suite_add_simple_test(suite, "deltest24", deltest24); + torture_suite_add_2smb_test(suite, "deltest25", deltest25); + torture_suite_add_2smb_test(suite, "deltest25a", deltest25a); + + return suite; +} diff --git a/source4/torture/basic/denytest.c b/source4/torture/basic/denytest.c new file mode 100644 index 0000000..c9f4a97 --- /dev/null +++ b/source4/torture/basic/denytest.c @@ -0,0 +1,2819 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - deny mode scanning functions + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "libcli/security/security.h" +#include "torture/util.h" +#include "cxd_known.h" +#include "torture/basic/proto.h" + +extern int torture_failures; + +#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0) + +enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5}; + +static const char *denystr(int denymode) +{ + const struct { + int v; + const char *name; + } deny_modes[] = { + {DENY_DOS, "DENY_DOS"}, + {DENY_ALL, "DENY_ALL"}, + {DENY_WRITE, "DENY_WRITE"}, + {DENY_READ, "DENY_READ"}, + {DENY_NONE, "DENY_NONE"}, + {DENY_FCB, "DENY_FCB"}, + {-1, NULL}}; + int i; + for (i=0;deny_modes[i].name;i++) { + if (deny_modes[i].v == denymode) return deny_modes[i].name; + } + return "DENY_XXX"; +} + +static const char *openstr(int mode) +{ + const struct { + int v; + const char *name; + } open_modes[] = { + {O_RDWR, "O_RDWR"}, + {O_RDONLY, "O_RDONLY"}, + {O_WRONLY, "O_WRONLY"}, + {-1, NULL}}; + int i; + for (i=0;open_modes[i].name;i++) { + if (open_modes[i].v == mode) return open_modes[i].name; + } + return "O_XXX"; +} + +static const char *resultstr(enum deny_result res) +{ + const struct { + enum deny_result res; + const char *name; + } results[] = { + {A_X, "X"}, + {A_0, "-"}, + {A_R, "R"}, + {A_W, "W"}, + {A_RW,"RW"}}; + int i; + for (i=0;itree, fnames[i]); + fnum1 = smbcli_open(cli1->tree, fnames[i], O_RDWR|O_CREAT, DENY_NONE); + smbcli_write(cli1->tree, fnum1, 0, fnames[i], 0, strlen(fnames[i])); + smbcli_close(cli1->tree, fnum1); + } + + torture_comment(tctx, "Testing %d entries\n", (int)ARRAY_SIZE(denytable1)); + + clock_gettime_mono(&tv_start); + + for (i=0; itree, fname, + denytable1[i].mode1, + denytable1[i].deny1); + fnum2 = smbcli_open(cli1->tree, fname, + denytable1[i].mode2, + denytable1[i].deny2); + + if (fnum1 == -1) { + res = A_X; + } else if (fnum2 == -1) { + res = A_0; + } else { + uint8_t x = 1; + res = A_0; + if (smbcli_read(cli1->tree, fnum2, &x, 0, 1) == 1) { + res += A_R; + } + if (smbcli_write(cli1->tree, fnum2, 0, &x, 0, 1) == 1) { + res += A_W; + } + } + + if (torture_setting_bool(tctx, "showall", false) || + res != denytable1[i].result) { + int64_t tdif; + clock_gettime_mono(&tv); + tdif = nsec_time_diff(&tv, &tv_start); + tdif /= 1000000; + torture_comment(tctx, "%lld: %s %8s %10s %8s %10s %s (correct=%s)\n", + (long long)tdif, + fname, + denystr(denytable1[i].deny1), + openstr(denytable1[i].mode1), + denystr(denytable1[i].deny2), + openstr(denytable1[i].mode2), + resultstr(res), + resultstr(denytable1[i].result)); + } + + if (res != denytable1[i].result) { + correct = false; + CHECK_MAX_FAILURES(failed); + } + + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + } + +failed: + for (i=0;i<2;i++) { + smbcli_unlink(cli1->tree, fnames[i]); + } + + torture_comment(tctx, "finished denytest1 (%d failures)\n", failures); + return correct; +} + + +/* + this produces a matrix of deny mode behaviour with 2 connections + */ +bool torture_denytest2(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int fnum1, fnum2; + int i; + bool correct = true; + const char *fnames[2] = {"\\denytest2.dat", "\\denytest2.exe"}; + struct timespec tv, tv_start; + int failures=0; + + for (i=0;i<2;i++) { + smbcli_unlink(cli1->tree, fnames[i]); + fnum1 = smbcli_open(cli1->tree, fnames[i], O_RDWR|O_CREAT, DENY_NONE); + smbcli_write(cli1->tree, fnum1, 0, fnames[i], 0, strlen(fnames[i])); + smbcli_close(cli1->tree, fnum1); + } + + clock_gettime_mono(&tv_start); + + for (i=0; itree, fname, + denytable2[i].mode1, + denytable2[i].deny1); + fnum2 = smbcli_open(cli2->tree, fname, + denytable2[i].mode2, + denytable2[i].deny2); + + if (fnum1 == -1) { + res = A_X; + } else if (fnum2 == -1) { + res = A_0; + } else { + uint8_t x = 1; + res = A_0; + if (smbcli_read(cli2->tree, fnum2, &x, 0, 1) == 1) { + res += A_R; + } + if (smbcli_write(cli2->tree, fnum2, 0, &x, 0, 1) == 1) { + res += A_W; + } + } + + if (torture_setting_bool(tctx, "showall", false) || + res != denytable2[i].result) { + int64_t tdif; + clock_gettime_mono(&tv); + tdif = nsec_time_diff(&tv, &tv_start); + tdif /= 1000000; + torture_comment(tctx, "%lld: %s %8s %10s %8s %10s %s (correct=%s)\n", + (long long)tdif, + fname, + denystr(denytable2[i].deny1), + openstr(denytable2[i].mode1), + denystr(denytable2[i].deny2), + openstr(denytable2[i].mode2), + resultstr(res), + resultstr(denytable2[i].result)); + } + + if (res != denytable2[i].result) { + correct = false; + CHECK_MAX_FAILURES(failed); + } + + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli2->tree, fnum2); + } + +failed: + for (i=0;i<2;i++) { + smbcli_unlink(cli1->tree, fnames[i]); + } + + torture_comment(tctx, "finished denytest2 (%d failures)\n", failures); + return correct; +} + + + +/* + simple test harness for playing with deny modes + */ +bool torture_denytest3(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + int fnum1, fnum2; + const char *fname; + + fname = "\\deny_dos1.dat"; + + smbcli_unlink(cli1->tree, fname); + fnum1 = smbcli_open(cli1->tree, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS); + fnum2 = smbcli_open(cli1->tree, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS); + if (fnum1 != -1) smbcli_close(cli1->tree, fnum1); + if (fnum2 != -1) smbcli_close(cli1->tree, fnum2); + smbcli_unlink(cli1->tree, fname); + torture_comment(tctx, "fnum1=%d fnum2=%d\n", fnum1, fnum2); + + + fname = "\\deny_dos2.dat"; + + smbcli_unlink(cli1->tree, fname); + fnum1 = smbcli_open(cli1->tree, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS); + fnum2 = smbcli_open(cli2->tree, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS); + if (fnum1 != -1) smbcli_close(cli1->tree, fnum1); + if (fnum2 != -1) smbcli_close(cli2->tree, fnum2); + smbcli_unlink(cli1->tree, fname); + torture_comment(tctx, "fnum1=%d fnum2=%d\n", fnum1, fnum2); + + return true; +} + +struct bit_value { + uint32_t value; + const char *name; +}; + +static uint32_t map_bits(const struct bit_value *bv, int b, int nbits) +{ + int i; + uint32_t ret = 0; + for (i=0;itree, fname); + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)); + smbcli_close(cli1->tree, fnum1); + + clock_gettime_mono(&tv_start); + + io1.ntcreatex.level = RAW_OPEN_NTCREATEX; + io1.ntcreatex.in.root_fid.fnum = 0; + io1.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io1.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io1.ntcreatex.in.file_attr = 0; + io1.ntcreatex.in.alloc_size = 0; + io1.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io1.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io1.ntcreatex.in.security_flags = 0; + io1.ntcreatex.in.fname = fname; + io2 = io1; + + torture_comment(tctx, "Testing %d entries on %s\n", torture_numops, fname); + + for (i=0;itree, mem_ctx, &io1); + status2 = smb_raw_open(cli2->tree, mem_ctx, &io2); + + if (random() % 2 == 0) { + read_for_execute = true; + } else { + read_for_execute = false; + } + + if (!NT_STATUS_IS_OK(status1)) { + res = A_X; + } else if (!NT_STATUS_IS_OK(status2)) { + res = A_0; + } else { + union smb_read r; + NTSTATUS status; + + /* we can't use smbcli_read() as we need to + set read_for_execute */ + r.readx.level = RAW_READ_READX; + r.readx.in.file.fnum = io2.ntcreatex.out.file.fnum; + r.readx.in.offset = 0; + r.readx.in.mincnt = sizeof(buf); + r.readx.in.maxcnt = sizeof(buf); + r.readx.in.remaining = 0; + r.readx.in.read_for_execute = read_for_execute; + r.readx.out.data = buf; + + res = A_0; + status = smb_raw_read(cli2->tree, &r); + if (NT_STATUS_IS_OK(status)) { + res += A_R; + } + if (smbcli_write(cli2->tree, io2.ntcreatex.out.file.fnum, + 0, buf, 0, sizeof(buf)) >= 1) { + res += A_W; + } + } + + if (NT_STATUS_IS_OK(status1)) { + smbcli_close(cli1->tree, io1.ntcreatex.out.file.fnum); + } + if (NT_STATUS_IS_OK(status2)) { + smbcli_close(cli2->tree, io2.ntcreatex.out.file.fnum); + } + + status2_p = predict_share_conflict(io1.ntcreatex.in.share_access, + io1.ntcreatex.in.access_mask, + io2.ntcreatex.in.share_access, + io2.ntcreatex.in.access_mask, + read_for_execute, + &res2); + + clock_gettime_mono(&tv); + if (torture_setting_bool(tctx, "showall", false) || + !NT_STATUS_EQUAL(status2, status2_p) || + res != res2) { + torture_comment(tctx, "\n%-20s %-70s\n%-20s %-70s %4s %4s %s/%s\n", + bit_string(mem_ctx, share_access_bits, b_sa1, nbits1), + bit_string(mem_ctx, access_mask_bits, b_am1, nbits2), + bit_string(mem_ctx, share_access_bits, b_sa2, nbits1), + bit_string(mem_ctx, access_mask_bits, b_am2, nbits2), + resultstr(res), + resultstr(res2), + nt_errstr(status2), + nt_errstr(status2_p)); + fflush(stdout); + } + + if (res != res2 || + !NT_STATUS_EQUAL(status2, status2_p)) { + CHECK_MAX_FAILURES(failed); + correct = false; + } + + talloc_free(mem_ctx); + } + +failed: + smbcli_unlink(cli1->tree, fname); + + torture_comment(tctx, "finished ntdenytest (%d failures)\n", failures); + return correct; +} + + + +/* + a denytest for ntcreatex + */ +bool torture_ntdenytest1(struct torture_context *tctx, + struct smbcli_state *cli, int client) +{ + extern int torture_seed; + + srandom(torture_seed + client); + + torture_comment(tctx, "starting ntdenytest1 client %d\n", client); + + return torture_ntdenytest(tctx, cli, cli, client); +} + +/* + a denytest for ntcreatex + */ +bool torture_ntdenytest2(struct torture_context *torture, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + return torture_ntdenytest(torture, cli1, cli2, 0); +} + +#define COMPARE_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + failed = true; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)correct); \ + ret = false; \ + }} while (0) + +/* + test sharing of handles with DENY_DOS on a single connection +*/ +bool torture_denydos_sharing(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = "\\torture_denydos.txt"; + NTSTATUS status; + int fnum1 = -1, fnum2 = -1; + bool ret = true; + union smb_setfileinfo sfinfo; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(cli); + + torture_comment(tctx, "Checking DENY_DOS shared handle semantics\n"); + smbcli_unlink(cli->tree, fname); + + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_DOS; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 0; + io.openx.in.timeout = 0; + + torture_comment(tctx, "openx twice with RDWR/DENY_DOS\n"); + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.openx.out.file.fnum; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.openx.out.file.fnum; + + torture_comment(tctx, "fnum1=%d fnum2=%d\n", fnum1, fnum2); + + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.position_information.in.file.fnum = fnum1; + sfinfo.position_information.in.position = 1000; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "two handles should be same file handle\n"); + finfo.position_information.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.position_information.out.position, 1000); + + finfo.position_information.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.position_information.out.position, 1000); + + + smbcli_close(cli->tree, fnum1); + smbcli_close(cli->tree, fnum2); + + torture_comment(tctx, "openx twice with RDWR/DENY_NONE\n"); + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.openx.out.file.fnum; + + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.openx.out.file.fnum; + + torture_comment(tctx, "fnum1=%d fnum2=%d\n", fnum1, fnum2); + + torture_comment(tctx, "two handles should be separate\n"); + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.position_information.in.file.fnum = fnum1; + sfinfo.position_information.in.position = 1000; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.position_information.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.position_information.out.position, 1000); + + finfo.position_information.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.position_information.out.position, 0); + +done: + smbcli_close(cli->tree, fnum1); + smbcli_close(cli->tree, fnum2); + smbcli_unlink(cli->tree, fname); + + return ret; +} + +#define CXD_MATCHES(_cxd, i) \ + ((cxd_known[i].cxd_test == (_cxd)->cxd_test) && \ + (cxd_known[i].cxd_flags == (_cxd)->cxd_flags) && \ + (cxd_known[i].cxd_access1 == (_cxd)->cxd_access1) && \ + (cxd_known[i].cxd_sharemode1 == (_cxd)->cxd_sharemode1) && \ + (cxd_known[i].cxd_access2 == (_cxd)->cxd_access2) && \ + (cxd_known[i].cxd_sharemode2 == (_cxd)->cxd_sharemode2)) + +static int cxd_find_known(struct createx_data *cxd) +{ + static int i = -1; + + /* Optimization for tests which we don't have results saved for. */ + if ((cxd->cxd_test == CXD_TEST_CREATEX_ACCESS_EXHAUSTIVE) || + (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE_EXTENDED)) + return -1; + + /* Optimization: If our cxd_known table is too large, it hurts test + * performance to search through the entire table each time. If the + * caller can pass in the previous result, we can try the next entry. + * This works if results are taken directly from the same code. */ + i++; + if ((i >= 0) && (i < sizeof(cxd_known) / sizeof(cxd_known[0])) && + CXD_MATCHES(cxd, i)) + return i; + + for (i = 0; i < (sizeof(cxd_known) / sizeof(cxd_known[0])); i++) { + if (CXD_MATCHES(cxd, i)) + return i; + } + + return -1; +} + +#define CREATEX_NAME "\\createx_dir" + +static bool createx_make_dir(struct torture_context *tctx, + struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, const char *fname) +{ + bool ret = true; + NTSTATUS status; + + status = smbcli_mkdir(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + return ret; +} + +static bool createx_make_file(struct torture_context *tctx, + struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, const char *fname) +{ + union smb_open open_parms; + bool ret = true; + NTSTATUS status; + + ZERO_STRUCT(open_parms); + open_parms.generic.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + open_parms.ntcreatex.in.share_access = 0; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + open_parms.ntcreatex.in.create_options = 0; + open_parms.ntcreatex.in.fname = fname; + + status = smb_raw_open(tree, mem_ctx, &open_parms); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_close(tree, open_parms.ntcreatex.out.file.fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + return ret; +} + +static void createx_fill_dir(union smb_open *open_parms, int accessmode, + int sharemode, const char *fname) +{ + ZERO_STRUCTP(open_parms); + open_parms->generic.level = RAW_OPEN_NTCREATEX; + open_parms->ntcreatex.in.flags = 0; + open_parms->ntcreatex.in.access_mask = accessmode; + open_parms->ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + open_parms->ntcreatex.in.share_access = sharemode; + open_parms->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + open_parms->ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + open_parms->ntcreatex.in.fname = fname; +} + +static void createx_fill_file(union smb_open *open_parms, int accessmode, + int sharemode, const char *fname) +{ + ZERO_STRUCTP(open_parms); + open_parms->generic.level = RAW_OPEN_NTCREATEX; + open_parms->ntcreatex.in.flags = 0; + open_parms->ntcreatex.in.access_mask = accessmode; + open_parms->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + open_parms->ntcreatex.in.share_access = sharemode; + open_parms->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + open_parms->ntcreatex.in.create_options = 0; + open_parms->ntcreatex.in.fname = fname; + open_parms->ntcreatex.in.root_fid.fnum = 0; +} + +static int data_file_fd = -1; + +#define KNOWN "known" +#define CHILD "child" +static bool createx_test_dir(struct torture_context *tctx, + struct smbcli_tree *tree, int fnum, TALLOC_CTX *mem_ctx, NTSTATUS *result) +{ + bool ret = true; + NTSTATUS status; + union smb_open open_parms; + + /* bypass original handle to guarantee creation */ + ZERO_STRUCT(open_parms); + open_parms.generic.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + open_parms.ntcreatex.in.share_access = 0; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + open_parms.ntcreatex.in.create_options = 0; + open_parms.ntcreatex.in.fname = CREATEX_NAME "\\" KNOWN; + + status = smb_raw_open(tree, mem_ctx, &open_parms); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(tree, open_parms.ntcreatex.out.file.fnum); + + result[CXD_DIR_ENUMERATE] = NT_STATUS_OK; + + /* try to create a child */ + ZERO_STRUCT(open_parms); + open_parms.generic.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + open_parms.ntcreatex.in.share_access = 0; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + open_parms.ntcreatex.in.create_options = 0; + open_parms.ntcreatex.in.fname = CHILD; + open_parms.ntcreatex.in.root_fid.fnum = fnum; + + result[CXD_DIR_CREATE_CHILD] = + smb_raw_open(tree, mem_ctx, &open_parms); + smbcli_close(tree, open_parms.ntcreatex.out.file.fnum); + + /* try to traverse dir to known good file */ + ZERO_STRUCT(open_parms); + open_parms.generic.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + open_parms.ntcreatex.in.share_access = 0; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + open_parms.ntcreatex.in.create_options = 0; + open_parms.ntcreatex.in.fname = KNOWN; + open_parms.ntcreatex.in.root_fid.fnum = fnum; + + result[CXD_DIR_TRAVERSE] = + smb_raw_open(tree, mem_ctx, &open_parms); + + + smbcli_close(tree, open_parms.ntcreatex.out.file.fnum); + smbcli_unlink(tree, CREATEX_NAME "\\" KNOWN); + smbcli_unlink(tree, CREATEX_NAME "\\" CHILD); + + done: + return ret; +} + +static bool createx_test_file(struct torture_context *tctx, + struct smbcli_tree *tree, int fnum, TALLOC_CTX *mem_ctx, NTSTATUS *result) +{ + union smb_read rd; + union smb_write wr; + char buf[256] = ""; + + memset(&rd, 0, sizeof(rd)); + rd.readx.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = sizeof(buf); + rd.readx.in.maxcnt = sizeof(buf); + rd.readx.out.data = (uint8_t *)buf; + + result[CXD_FILE_READ] = smb_raw_read(tree, &rd); + + memset(&wr, 0, sizeof(wr)); + wr.writex.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.count = sizeof(buf); + wr.writex.in.data = (uint8_t *)buf; + + result[CXD_FILE_WRITE] = smb_raw_write(tree, &wr); + + memset(&rd, 0, sizeof(rd)); + rd.readx.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = sizeof(buf); + rd.readx.in.maxcnt = sizeof(buf); + rd.readx.in.read_for_execute = 1; + rd.readx.out.data = (uint8_t *)buf; + + result[CXD_FILE_EXECUTE] = smb_raw_read(tree, &rd); + + return true; +} + +/* TODO When redirecting stdout to a file, the progress bar really screws up + * the output. Could use a switch "--noprogress", or direct the progress bar to + * stderr? No other solution? */ +static void createx_progress_bar(struct torture_context *tctx, unsigned int i, + unsigned int total, unsigned int skipped) +{ + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%5d/%5d (%d skipped)\r", i, total, + skipped); + fflush(stdout); + } +} + +static bool torture_createx_specific(struct torture_context *tctx, struct + smbcli_state *cli, struct smbcli_state *cli2, TALLOC_CTX *mem_ctx, struct + createx_data *cxd, int estimated_count) +{ + static int call_count = 1; + static int unskipped_call_count = 1; + const char *fname = CREATEX_NAME; + int fnum = -1, fnum2 = -1, res, i; + union smb_open open_parms1, open_parms2; + bool ret = true; + bool is_dir = cxd->cxd_flags & CXD_FLAGS_DIRECTORY; + NTSTATUS *result = &cxd->cxd_result[0]; + NTSTATUS *result2 = &cxd->cxd_result2[0]; + bool found = false, failed = false; + + bool (*make_func)(struct torture_context *, + struct smbcli_tree *, TALLOC_CTX *, const char *); + void (*fill_func)(union smb_open *, int, int, const char *); + bool (*test_func)(struct torture_context *, + struct smbcli_tree *, int, TALLOC_CTX *, NTSTATUS *); + NTSTATUS (*destroy_func)(struct smbcli_tree *, const char *); + + if (is_dir) { + make_func = createx_make_dir; + fill_func = createx_fill_dir; + test_func = createx_test_dir; + destroy_func = smbcli_rmdir; + } else { + make_func = createx_make_file; + fill_func = createx_fill_file; + test_func = createx_test_file; + destroy_func = smbcli_unlink; + } + + /* Skip all SACL related tests. */ + if ((!torture_setting_bool(tctx, "sacl_support", true)) && + ((cxd->cxd_access1 & SEC_FLAG_SYSTEM_SECURITY) || + (cxd->cxd_access2 & SEC_FLAG_SYSTEM_SECURITY))) + goto done; + + if (cxd->cxd_flags & CXD_FLAGS_MAKE_BEFORE_CREATEX) { + ret = make_func(tctx, cli->tree, mem_ctx, fname); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, + "Initial creation failed\n"); + goto done; + } + } + + /* Initialize. */ + fill_func(&open_parms1, cxd->cxd_access1, cxd->cxd_sharemode1, fname); + + if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) { + fill_func(&open_parms2, cxd->cxd_access2, cxd->cxd_sharemode2, + fname); + } + + for (i = CXD_CREATEX + 1; i < CXD_MAX; i++) { + result[i] = NT_STATUS_UNSUCCESSFUL; + result2[i] = NT_STATUS_UNSUCCESSFUL; + } + + /* Perform open(s). */ + result[CXD_CREATEX] = smb_raw_open(cli->tree, mem_ctx, &open_parms1); + if (NT_STATUS_IS_OK(result[CXD_CREATEX])) { + fnum = open_parms1.ntcreatex.out.file.fnum; + ret = test_func(tctx, cli->tree, fnum, mem_ctx, result); + smbcli_close(cli->tree, fnum); + } + + if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) { + result2[CXD_CREATEX] = smb_raw_open(cli2->tree, mem_ctx, + &open_parms2); + if (NT_STATUS_IS_OK(result2[CXD_CREATEX])) { + fnum2 = open_parms2.ntcreatex.out.file.fnum; + ret = test_func(tctx, cli2->tree, fnum2, mem_ctx, + result2); + smbcli_close(cli2->tree, fnum2); + } + } + + if (data_file_fd >= 0) { + size_t cxd_len = sizeof(struct createx_data); + found = true; + res = write(data_file_fd, cxd, cxd_len); + if (res != cxd_len) { + torture_result(tctx, TORTURE_FAIL, + "(%s): write failed: %s!", + __location__, strerror(errno)); + ret = false; + } + } else if ((res = cxd_find_known(cxd)) >= 0) { + found = true; + for (i = 0; i < CXD_MAX; i++) { + /* Note: COMPARE_STATUS will set the "failed" bool. */ + COMPARE_STATUS(result[i], cxd_known[res].cxd_result[i]); + if (i == 0 && !NT_STATUS_IS_OK(result[i])) + break; + + if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) { + COMPARE_STATUS(result2[i], + cxd_known[res].cxd_result2[i]); + if (i == 0 && !NT_STATUS_IS_OK(result2[i])) + break; + } + } + } + + /* We print if its not in the "cxd_known" list or if we fail. */ + if (!found || failed) { + torture_comment(tctx, + " { .cxd_test = %d, .cxd_flags = %#3x, " + ".cxd_access1 = %#10x, .cxd_sharemode1=%1x, " + ".cxd_access2=%#10x, .cxd_sharemode2=%1x, " + ".cxd_result = { ", cxd->cxd_test, cxd->cxd_flags, + cxd->cxd_access1, cxd->cxd_sharemode1, cxd->cxd_access2, + cxd->cxd_sharemode2); + for (i = 0; i < CXD_MAX; i++) { + torture_comment(tctx, "%s, ", nt_errstr(result[i])); + if (i == 0 && !NT_STATUS_IS_OK(result[i])) + break; + } + torture_comment(tctx, "}"); + if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) { + torture_comment(tctx, ", .cxd_result2 = { "); + for (i = 0; i < CXD_MAX; i++) { + torture_comment(tctx, "%s, ", + nt_errstr(result2[i])); + if (i == 0 && !NT_STATUS_IS_OK(result2[i])) + break; + } + torture_comment(tctx, "}"); + } + torture_comment(tctx, "}, \n"); + } else { + createx_progress_bar(tctx, call_count, estimated_count, + call_count - unskipped_call_count); + } + /* Count tests that we didn't skip. */ + unskipped_call_count++; + done: + call_count++; + + destroy_func(cli->tree, fname); + return ret; +} + +uint32_t sec_access_bit_groups[] = { + SEC_RIGHTS_FILE_READ, + SEC_RIGHTS_FILE_WRITE, + SEC_RIGHTS_FILE_EXECUTE +}; +#define NUM_ACCESS_GROUPS (sizeof(sec_access_bit_groups) / sizeof(uint32_t)) +#define ACCESS_GROUPS_COUNT ((1 << NUM_ACCESS_GROUPS)) +#define BITSINBYTE 8 + +/* Note: See NTCREATEX_SHARE_ACCESS_{NONE,READ,WRITE,DELETE} for share mode + * declarations. */ +#define NUM_SHAREMODE_PERMUTATIONS 8 + +/** + * NTCREATEX and SHARE MODE test. + * + * Open with combinations of (access_mode, share_mode). + * - Check status + * Open 2nd time with combination of (access_mode2, share_mode2). + * - Check status + * Perform operations to verify? + * - Read + * - Write + * - Delete + */ +bool torture_createx_sharemodes(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2, + bool dir, + bool extended) +{ + TALLOC_CTX *mem_ctx; + bool ret = true; + int i, j, est; + int gp1, gp2; /* group permuters */ + struct createx_data cxd = {0}; + int num_access_bits1 = sizeof(cxd.cxd_access1) * BITSINBYTE; + int num_access_bits2 = sizeof(cxd.cxd_access2) * BITSINBYTE; + + mem_ctx = talloc_init("createx_sharemodes"); + if (!mem_ctx) + return false; + + if (!torture_setting_bool(tctx, "sacl_support", true)) + torture_warning(tctx, "Skipping SACL related tests!\n"); + + cxd.cxd_test = extended ? CXD_TEST_CREATEX_SHAREMODE_EXTENDED : + CXD_TEST_CREATEX_SHAREMODE; + cxd.cxd_flags = dir ? CXD_FLAGS_DIRECTORY: 0; + + /* HACK for progress bar: figure out estimated count. */ + est = (NUM_SHAREMODE_PERMUTATIONS * NUM_SHAREMODE_PERMUTATIONS) * + ((ACCESS_GROUPS_COUNT * ACCESS_GROUPS_COUNT) + + (extended ? num_access_bits1 * num_access_bits2 : 0)); + + /* Blank slate. */ + smbcli_deltree(cli->tree, CREATEX_NAME); + smbcli_unlink(cli->tree, CREATEX_NAME); + + /* Choose 2 random share modes. */ + for (cxd.cxd_sharemode1 = 0; + cxd.cxd_sharemode1 < NUM_SHAREMODE_PERMUTATIONS; + cxd.cxd_sharemode1++) { + for (cxd.cxd_sharemode2 = 0; + cxd.cxd_sharemode2 < NUM_SHAREMODE_PERMUTATIONS; + cxd.cxd_sharemode2++) { + + /* Permutate through our access_bit_groups. */ + for (gp1 = 0; gp1 < ACCESS_GROUPS_COUNT; gp1++) { + for (gp2 = 0; gp2 < ACCESS_GROUPS_COUNT; gp2++) + { + cxd.cxd_access1 = cxd.cxd_access2 = 0; + + for (i = 0; i < NUM_ACCESS_GROUPS; i++) + { + cxd.cxd_access1 |= + (gp1 & (1 << i)) ? + sec_access_bit_groups[i]:0; + cxd.cxd_access2 |= + (gp2 & (1 << i)) ? + sec_access_bit_groups[i]:0; + } + + torture_createx_specific(tctx, cli, + cli2, mem_ctx, &cxd, est); + } + } + + /* Only do the single access bits on an extended run. */ + if (!extended) + continue; + + for (i = 0; i < num_access_bits1; i++) { + for (j = 0; j < num_access_bits2; j++) { + cxd.cxd_access1 = 1ull << i; + cxd.cxd_access2 = 1ull << j; + + torture_createx_specific(tctx, cli, + cli2, mem_ctx, &cxd, est); + } + } + } + } + torture_comment(tctx, "\n"); + + talloc_free(mem_ctx); + return ret; +} + +bool torture_createx_sharemodes_file(struct torture_context *tctx, + struct smbcli_state *cli, struct smbcli_state *cli2) +{ + return torture_createx_sharemodes(tctx, cli, cli2, false, false); +} + +bool torture_createx_sharemodes_dir(struct torture_context *tctx, + struct smbcli_state *cli, struct smbcli_state *cli2) +{ + return torture_createx_sharemodes(tctx, cli, cli2, true, false); +} + +bool torture_createx_access(struct torture_context *tctx, + struct smbcli_state *cli) +{ + TALLOC_CTX *mem_ctx; + bool ret = true; + uint32_t group_permuter; + uint32_t i; + struct createx_data cxd = {0}; + int est; + int num_access_bits = sizeof(cxd.cxd_access1) * BITSINBYTE; + + mem_ctx = talloc_init("createx_dir"); + if (!mem_ctx) + return false; + + if (!torture_setting_bool(tctx, "sacl_support", true)) + torture_warning(tctx, "Skipping SACL related tests!\n"); + + cxd.cxd_test = CXD_TEST_CREATEX_ACCESS; + + /* HACK for progress bar: figure out estimated count. */ + est = CXD_FLAGS_COUNT * (ACCESS_GROUPS_COUNT + (num_access_bits * 3)); + + /* Blank slate. */ + smbcli_deltree(cli->tree, CREATEX_NAME); + smbcli_unlink(cli->tree, CREATEX_NAME); + + for (cxd.cxd_flags = 0; cxd.cxd_flags <= CXD_FLAGS_MASK; + cxd.cxd_flags++) { + /** + * This implements a basic permutation of all elements of + * 'bit_group'. group_permuter is a bit field representing + * which groups to turn on. + */ + for (group_permuter = 0; group_permuter < (1 << + NUM_ACCESS_GROUPS); group_permuter++) { + for (i = 0, cxd.cxd_access1 = 0; + i < NUM_ACCESS_GROUPS; i++) { + cxd.cxd_access1 |= (group_permuter & (1 << i)) + ? sec_access_bit_groups[i] : 0; + } + + torture_createx_specific(tctx, cli, NULL, mem_ctx, + &cxd, est); + } + for (i = 0; i < num_access_bits; i++) { + /* And now run through the single access bits. */ + cxd.cxd_access1 = 1 << i; + torture_createx_specific(tctx, cli, NULL, mem_ctx, + &cxd, est); + + /* Does SEC_FLAG_MAXIMUM_ALLOWED override? */ + cxd.cxd_access1 |= SEC_FLAG_MAXIMUM_ALLOWED; + torture_createx_specific(tctx, cli, NULL, mem_ctx, + &cxd, est); + + /* What about SEC_FLAG_SYSTEM_SECURITY? */ + cxd.cxd_access1 |= SEC_FLAG_SYSTEM_SECURITY; + torture_createx_specific(tctx, cli, NULL, mem_ctx, + &cxd, est); + } + } + + talloc_free(mem_ctx); + return ret; +} + +#define ACCESS_KNOWN_MASK 0xF31F01FFull + +bool torture_createx_access_exhaustive(struct torture_context *tctx, + struct smbcli_state *cli) +{ + char *data_file; + TALLOC_CTX *mem_ctx; + bool ret = true, first; + uint32_t i; + struct createx_data cxd = {0}; + + mem_ctx = talloc_init("createx_dir"); + if (!mem_ctx) + return false; + + if (!torture_setting_bool(tctx, "sacl_support", true)) + torture_warning(tctx, "Skipping SACL related tests!\n"); + + data_file = getenv("CREATEX_DATA"); + if (data_file) { + data_file_fd = open(data_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (data_file_fd < 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s): data file open failed: %s!", + __location__, strerror(errno)); + ret = false; + goto done; + } + } + + /* Blank slate. */ + smbcli_deltree(cli->tree, CREATEX_NAME); + smbcli_unlink(cli->tree, CREATEX_NAME); + + cxd.cxd_test = CXD_TEST_CREATEX_ACCESS_EXHAUSTIVE; + + for (cxd.cxd_flags = 0; cxd.cxd_flags <= CXD_FLAGS_MASK; + cxd.cxd_flags++) { + for (i = 0, first = true; (i != 0) || first; first = false, + i = ((i | ~ACCESS_KNOWN_MASK) + 1) & ACCESS_KNOWN_MASK) { + cxd.cxd_access1 = i; + ret = torture_createx_specific(tctx, cli, NULL, + mem_ctx, &cxd, 0); + if (!ret) + break; + } + } + + close(data_file_fd); + data_file_fd = -1; + + done: + talloc_free(mem_ctx); + return ret; +} + +#define MAXIMUM_ALLOWED_FILE "torture_maximum_allowed" +bool torture_maximum_allowed(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct security_descriptor *sd, *sd_orig; + union smb_open io; + static TALLOC_CTX *mem_ctx; + int fnum, i; + bool ret = true; + NTSTATUS status; + union smb_fileinfo q; + const char *owner_sid; + bool has_restore_privilege, has_backup_privilege, has_system_security_privilege; + + mem_ctx = talloc_init("torture_maximum_allowed"); + + if (!torture_setting_bool(tctx, "sacl_support", true)) + torture_warning(tctx, "Skipping SACL related tests!\n"); + + sd = security_descriptor_dacl_create(mem_ctx, + 0, NULL, NULL, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ, + 0, NULL); + + /* Blank slate */ + smbcli_unlink(cli->tree, MAXIMUM_ALLOWED_FILE); + + /* create initial file with restrictive SD */ + memset(&io, 0, sizeof(io)); + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = MAXIMUM_ALLOWED_FILE; + io.ntcreatex.in.sec_desc = sd; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* the correct answers for this test depends on whether the + user has restore privileges. To find that out we first need + to know our SID - get it from the owner_sid of the file we + just created */ + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_RESTORE for %s - %s\n", + owner_sid, + has_restore_privilege?"Yes":"No"); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_BACKUP)); + has_backup_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_BACKUP for %s - %s\n", + owner_sid, + has_backup_privilege?"Yes":"No"); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_SECURITY)); + has_system_security_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_SECURITY for %s - %s\n", + owner_sid, + has_system_security_privilege?"Yes":"No"); + + smbcli_close(cli->tree, fnum); + + for (i = 0; i < 32; i++) { + uint32_t mask = SEC_FLAG_MAXIMUM_ALLOWED | (1u << i); + /* + * SEC_GENERIC_EXECUTE is a complete subset of + * SEC_GENERIC_READ when mapped to specific bits, + * so we need to include it in the basic OK mask. + */ + uint32_t ok_mask = SEC_RIGHTS_FILE_READ | SEC_GENERIC_READ | SEC_GENERIC_EXECUTE | + SEC_STD_DELETE | SEC_STD_WRITE_DAC; + + /* + * Now SEC_RIGHTS_PRIV_RESTORE and SEC_RIGHTS_PRIV_BACKUP + * don't include any generic bits (they're used directly + * in the fileserver where the generic bits have already + * been mapped into file specific bits) we need to add the + * generic bits to the ok_mask when we have these privileges. + */ + if (has_restore_privilege) { + ok_mask |= SEC_RIGHTS_PRIV_RESTORE|SEC_GENERIC_WRITE; + } + if (has_backup_privilege) { + ok_mask |= SEC_RIGHTS_PRIV_BACKUP|SEC_GENERIC_READ; + } + if (has_system_security_privilege) { + ok_mask |= SEC_FLAG_SYSTEM_SECURITY; + } + + /* Skip all SACL related tests. */ + if ((!torture_setting_bool(tctx, "sacl_support", true)) && + (mask & SEC_FLAG_SYSTEM_SECURITY)) + continue; + + memset(&io, 0, sizeof(io)); + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.access_mask = mask; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = + NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = MAXIMUM_ALLOWED_FILE; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + if (mask & ok_mask || + mask == SEC_FLAG_MAXIMUM_ALLOWED) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + if (mask & SEC_FLAG_SYSTEM_SECURITY) { + CHECK_STATUS(status, NT_STATUS_PRIVILEGE_NOT_HELD); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + } + + fnum = io.ntcreatex.out.file.fnum; + + smbcli_close(cli->tree, fnum); + } + + done: + smbcli_unlink(cli->tree, MAXIMUM_ALLOWED_FILE); + return ret; +} diff --git a/source4/torture/basic/dir.c b/source4/torture/basic/dir.c new file mode 100644 index 0000000..2a3d136 --- /dev/null +++ b/source4/torture/basic/dir.c @@ -0,0 +1,171 @@ +/* + Unix SMB/CIFS implementation. + + directory scanning tests + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/filesys.h" +#include "torture/basic/proto.h" + +static void list_fn(struct clilist_file_info *finfo, const char *name, void *state) +{ + +} + +/* + test directory listing speed + */ +bool torture_dirtest1(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int i; + int fnum; + bool correct = true; + extern int torture_numops; + struct timeval tv; + int ret; + + torture_comment(tctx, "Creating %d random filenames\n", torture_numops); + + srandom(0); + tv = timeval_current(); + for (i=0;itree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + fprintf(stderr,"(%s) Failed to open %s\n", + __location__, fname); + return false; + } + smbcli_close(cli->tree, fnum); + free(fname); + } + + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "a*.*", 0, list_fn, NULL)); + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "b*.*", 0, list_fn, NULL)); + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "xyzabc", 0, list_fn, NULL)); + + torture_comment(tctx, "dirtest core %g seconds\n", timeval_elapsed(&tv)); + + srandom(0); + for (i=0;itree, fname); + free(fname); + } + + return correct; +} + +bool torture_dirtest2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int i; + int fnum, num_seen; + bool correct = true; + extern int torture_entries; + int ret; + + if (!torture_setup_dir(cli, "\\LISTDIR")) { + return false; + } + + torture_comment(tctx, "Creating %d files\n", torture_entries); + + /* Create torture_entries files and torture_entries directories. */ + for (i=0;itree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_ARCHIVE, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + if (fnum == -1) { + fprintf(stderr,"(%s) Failed to open %s, error=%s\n", + __location__, fname, smbcli_errstr(cli->tree)); + return false; + } + free(fname); + smbcli_close(cli->tree, fnum); + } + for (i=0;itree, fname))) { + fprintf(stderr,"(%s) Failed to open %s, error=%s\n", + __location__, fname, smbcli_errstr(cli->tree)); + return false; + } + free(fname); + } + + /* Now ensure that doing an old list sees both files and directories. */ + num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL); + torture_comment(tctx, "num_seen = %d\n", num_seen ); + /* We should see (torture_entries) each of files & directories + . and .. */ + if (num_seen != (2*torture_entries)+2) { + correct = false; + fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n", + __location__, (2*torture_entries)+2, num_seen); + } + + + /* Ensure if we have the "must have" bits we only see the + * relevant entries. + */ + num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", (FILE_ATTRIBUTE_DIRECTORY<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL); + torture_comment(tctx, "num_seen = %d\n", num_seen ); + if (num_seen != torture_entries+2) { + correct = false; + fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n", + __location__, torture_entries+2, num_seen); + } + + num_seen = smbcli_list_old(cli->tree, "\\LISTDIR\\*", (FILE_ATTRIBUTE_ARCHIVE<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL); + torture_comment(tctx, "num_seen = %d\n", num_seen ); + if (num_seen != torture_entries) { + correct = false; + fprintf(stderr,"(%s) entry count mismatch, should be %d, was %d\n", + __location__, torture_entries, num_seen); + } + + /* Delete everything. */ + if (smbcli_deltree(cli->tree, "\\LISTDIR") == -1) { + fprintf(stderr,"(%s) Failed to deltree %s, error=%s\n", "\\LISTDIR", + __location__, smbcli_errstr(cli->tree)); + return false; + } + +#if 0 + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "a*.*", 0, list_fn, NULL)); + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "b*.*", 0, list_fn, NULL)); + torture_comment(tctx, "Matched %d\n", smbcli_list(cli->tree, "xyzabc", 0, list_fn, NULL)); +#endif + + return correct; +} diff --git a/source4/torture/basic/disconnect.c b/source4/torture/basic/disconnect.c new file mode 100644 index 0000000..7fb87d8 --- /dev/null +++ b/source4/torture/basic/disconnect.c @@ -0,0 +1,182 @@ +/* + Unix SMB/CIFS implementation. + + test server handling of unexpected client disconnects + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +#define BASEDIR "\\test_disconnect" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + talloc_free(cli); \ + return false; \ + }} while (0) + +/* + test disconnect after async open +*/ +static bool test_disconnect_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + struct smbcli_request *req1, *req2; + NTSTATUS status; + + printf("trying open/disconnect\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR "\\open.dat"; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.ntcreatex.in.share_access = 0; + req1 = smb_raw_open_send(cli->tree, &io); + req2 = smb_raw_open_send(cli->tree, &io); + if (!req1 || !req2) { + printf("test_disconnect_open: smb_raw_open_send() " + "returned NULL\n"); + return false; + } + + status = smbcli_chkpath(cli->tree, "\\"); + CHECK_STATUS(status, NT_STATUS_OK); + + talloc_free(cli); + + return true; +} + + +/* + test disconnect with timed lock +*/ +static bool test_disconnect_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + int fnum; + struct smbcli_request *req; + struct smb_lock_entry lock[1]; + + printf("trying disconnect with async lock\n"); + + fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", + O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree)); + return false; + } + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.ulock_cnt = 0; + lock[0].pid = 1; + lock[0].offset = 0; + lock[0].count = 4; + io.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + lock[0].pid = 2; + io.lockx.in.timeout = 3000; + req = smb_raw_lock_send(cli->tree, &io); + if (!req) { + printf("test_disconnect_lock: smb_raw_lock_send() " + "returned NULL\n"); + return false; + } + + status = smbcli_chkpath(cli->tree, "\\"); + CHECK_STATUS(status, NT_STATUS_OK); + + talloc_free(cli); + + return true; +} + + + +/* + basic testing of disconnects +*/ +bool torture_disconnect(struct torture_context *torture) +{ + bool ret = true; + TALLOC_CTX *mem_ctx; + int i; + extern int torture_numops; + struct smbcli_state *cli; + + mem_ctx = talloc_init("torture_raw_mux"); + + if (!torture_open_connection(&cli, torture, 0)) { + return false; + } + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + for (i=0;isession); + smbcli_deltree(cli->tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/basic/locking.c b/source4/torture/basic/locking.c new file mode 100644 index 0000000..e0e2971 --- /dev/null +++ b/source4/torture/basic/locking.c @@ -0,0 +1,811 @@ +/* + Unix SMB/CIFS implementation. + + basic locking tests + + Copyright (C) Andrew Tridgell 2000-2004 + Copyright (C) Jeremy Allison 2000-2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/time.h" +#include "system/filesys.h" +#include "torture/basic/proto.h" + +#define BASEDIR "\\locktest" + +/* + This test checks for two things: + + 1) correct support for retaining locks over a close (ie. the server + must not use posix semantics) + 2) support for lock timeouts + */ +static bool torture_locktest1(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\lockt1.lck"; + int fnum1, fnum2, fnum3; + time_t t1, t2; + unsigned int lock_timeout; + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR), + talloc_asprintf(tctx, "Unable to set up %s", BASEDIR)); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, + "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, + "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); + fnum3 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx, + "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK), + talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock2 succeeded! This is a locking bug\n"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_LOCK_NOT_GRANTED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock2 succeeded! This is a locking bug\n"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_FILE_LOCK_CONFLICT)) return false; + + torture_assert_ntstatus_ok(tctx, + smbcli_lock(cli1->tree, fnum1, 5, 9, 0, WRITE_LOCK), + talloc_asprintf(tctx, + "lock1 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 5, 9, 0, WRITE_LOCK)), + "lock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_LOCK_NOT_GRANTED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_LOCK_NOT_GRANTED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_FILE_LOCK_CONFLICT)) return false; + + lock_timeout = (6 + (random() % 20)); + torture_comment(tctx, "Testing lock timeout with timeout=%u\n", + lock_timeout); + t1 = time_mono(NULL); + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK)), + "lock3 succeeded! This is a locking bug\n"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_FILE_LOCK_CONFLICT)) return false; + t2 = time_mono(NULL); + + if (t2 - t1 < 5) { + torture_fail(tctx, + "error: This server appears not to support timed lock requests"); + } + torture_comment(tctx, "server slept for %u seconds for a %u second timeout\n", + (unsigned int)(t2-t1), lock_timeout); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2), + talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock4 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli2, ERRDOS, ERRlock, + NT_STATUS_FILE_LOCK_CONFLICT)) return false; + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum3), + talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), + talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); + + return true; +} + + +/* + This test checks that + + 1) the server supports multiple locking contexts on the one SMB + connection, distinguished by PID. + + 2) the server correctly fails overlapping locks made by the same PID (this + goes against POSIX behaviour, which is why it is tricky to implement) + + 3) the server denies unlock requests by an incorrect client PID +*/ +static bool torture_locktest2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *fname = BASEDIR "\\lockt2.lck"; + int fnum1, fnum2, fnum3; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + talloc_asprintf(tctx, "Unable to set up %s", BASEDIR)); + + torture_comment(tctx, "Testing pid context\n"); + + cli->session->pid = 1; + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, + "open of %s failed (%s)", fname, smbcli_errstr(cli->tree))); + + fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 != -1, + talloc_asprintf(tctx, "open2 of %s failed (%s)", + fname, smbcli_errstr(cli->tree))); + + cli->session->pid = 2; + + fnum3 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum3 != -1, + talloc_asprintf(tctx, + "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli->tree))); + + cli->session->pid = 1; + + torture_assert_ntstatus_ok(tctx, + smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK), + talloc_asprintf(tctx, + "lock1 failed (%s)", smbcli_errstr(cli->tree))); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK)), + "WRITE lock1 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, ERRDOS, ERRlock, + NT_STATUS_LOCK_NOT_GRANTED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, WRITE_LOCK)), + "WRITE lock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, ERRDOS, ERRlock, + NT_STATUS_LOCK_NOT_GRANTED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, READ_LOCK)), + "READ lock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, ERRDOS, ERRlock, + NT_STATUS_FILE_LOCK_CONFLICT)) return false; + + torture_assert_ntstatus_ok(tctx, + smbcli_lock(cli->tree, fnum1, 100, 4, 0, WRITE_LOCK), + talloc_asprintf(tctx, + "lock at 100 failed (%s)", smbcli_errstr(cli->tree))); + + cli->session->pid = 2; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 100, 4)), + "unlock at 100 succeeded! This is a locking bug"); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 4)), + "unlock1 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, + ERRDOS, ERRnotlocked, + NT_STATUS_RANGE_NOT_LOCKED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 8)), + "unlock2 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, + ERRDOS, ERRnotlocked, + NT_STATUS_RANGE_NOT_LOCKED)) return false; + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum3, 0, 4, 0, WRITE_LOCK)), + "lock3 succeeded! This is a locking bug"); + + if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; + + cli->session->pid = 1; + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum1), + talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum2), + talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum3), + talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli->tree))); + + return true; +} + + +/* + This test checks that + + 1) the server supports the full offset range in lock requests +*/ +static bool torture_locktest3(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\lockt3.lck"; + int fnum1, fnum2, i; + uint32_t offset; + extern int torture_numops; + +#define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops + + torture_comment(tctx, "Testing 32 bit offset ranges"); + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR), + talloc_asprintf(tctx, "Unable to set up %s", BASEDIR)); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + torture_assert(tctx, fnum1 != -1, + talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 != -1, + talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree))); + + torture_comment(tctx, "Establishing %d locks\n", torture_numops); + + for (offset=i=0;itree, fnum1, offset-1, 1, 0, WRITE_LOCK), + talloc_asprintf(tctx, "lock1 %d failed (%s)", i, smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK), + talloc_asprintf(tctx, "lock2 %d failed (%s)", + i, smbcli_errstr(cli1->tree))); + } + + torture_comment(tctx, "Testing %d locks\n", torture_numops); + + for (offset=i=0;itree, fnum1, offset-2, 1, 0, WRITE_LOCK)), + talloc_asprintf(tctx, "error: lock1 %d succeeded!", i)); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-1, 1, 0, WRITE_LOCK)), + talloc_asprintf(tctx, "error: lock2 %d succeeded!", i)); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK)), + talloc_asprintf(tctx, "error: lock3 %d succeeded!", i)); + + torture_assert(tctx, + !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK)), + talloc_asprintf(tctx, "error: lock4 %d succeeded!", i)); + } + + torture_comment(tctx, "Removing %d locks\n", torture_numops); + + for (offset=i=0;itree, fnum1, offset-1, 1), + talloc_asprintf(tctx, "unlock1 %d failed (%s)", + i, + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, + smbcli_unlock(cli2->tree, fnum2, offset-2, 1), + talloc_asprintf(tctx, "unlock2 %d failed (%s)", + i, + smbcli_errstr(cli1->tree))); + } + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), + talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), + talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); + + return true; +} + +#define EXPECTED(ret, v) if ((ret) != (v)) { \ + torture_comment(tctx, "** "); correct = false; \ + } + +/* + looks at overlapping locks +*/ +static bool torture_locktest4(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\lockt4.lck"; + int fnum1, fnum2, f; + bool ret; + uint8_t buf[1000]; + bool correct = true; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); + + memset(buf, 0, sizeof(buf)); + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto fail; + } + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 2, 4, 0, WRITE_LOCK)); + EXPECTED(ret, false); + torture_comment(tctx, "the same process %s set overlapping write locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 10, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 12, 4, 0, READ_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s set overlapping read locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 20, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 22, 4, 0, WRITE_LOCK)); + EXPECTED(ret, false); + torture_comment(tctx, "a different connection %s set overlapping write locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 30, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 32, 4, 0, READ_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "a different connection %s set overlapping read locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 40, 4, 0, WRITE_LOCK))) && + NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 42, 4, 0, WRITE_LOCK))); + EXPECTED(ret, false); + torture_comment(tctx, "a different pid %s set overlapping write locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 50, 4, 0, READ_LOCK))) && + NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 52, 4, 0, READ_LOCK))); + EXPECTED(ret, true); + torture_comment(tctx, "a different pid %s set overlapping read locks\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s set the same read lock twice\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)); + EXPECTED(ret, false); + torture_comment(tctx, "the same process %s set the same write lock twice\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, WRITE_LOCK)); + EXPECTED(ret, false); + torture_comment(tctx, "the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, READ_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, WRITE_LOCK))) && + NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, READ_LOCK))); + EXPECTED(ret, false); + torture_comment(tctx, "a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 110, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 112, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 110, 6)); + EXPECTED(ret, false); + torture_comment(tctx, "the same process %s coalesce read locks\n", ret?"can":"cannot"); + + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 120, 4, 0, WRITE_LOCK)) && + (smbcli_read(cli2->tree, fnum2, buf, 120, 4) == 4); + EXPECTED(ret, false); + torture_comment(tctx, "this server %s strict write locking\n", ret?"doesn't do":"does"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK)) && + (smbcli_write(cli2->tree, fnum2, 0, buf, 130, 4) == 4); + EXPECTED(ret, false); + torture_comment(tctx, "this server %s strict read locking\n", ret?"doesn't do":"does"); + + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)); + EXPECTED(ret, true); + torture_comment(tctx, "this server %s do recursive read locking\n", ret?"does":"doesn't"); + + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)) && + (smbcli_read(cli2->tree, fnum2, buf, 150, 4) == 4) && + !(smbcli_write(cli2->tree, fnum2, 0, buf, 150, 4) == 4) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)); + EXPECTED(ret, true); + torture_comment(tctx, "this server %s do recursive lock overlays\n", ret?"does":"doesn't"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 160, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 160, 4)) && + (smbcli_write(cli2->tree, fnum2, 0, buf, 160, 4) == 4) && + (smbcli_read(cli2->tree, fnum2, buf, 160, 4) == 4); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s remove a read lock using write locking\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 170, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 170, 4)) && + (smbcli_write(cli2->tree, fnum2, 0, buf, 170, 4) == 4) && + (smbcli_read(cli2->tree, fnum2, buf, 170, 4) == 4); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s remove a write lock using read locking\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 190, 4)) && + !(smbcli_write(cli2->tree, fnum2, 0, buf, 190, 4) == 4) && + (smbcli_read(cli2->tree, fnum2, buf, 190, 4) == 4); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s remove the first lock first\n", ret?"does":"doesn't"); + + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli2->tree, fnum2); + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + f = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, f, 0, 1, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_close(cli1->tree, fnum1)) && + ((fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE)) != -1) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK)); + smbcli_close(cli1->tree, f); + smbcli_close(cli1->tree, fnum1); + EXPECTED(ret, true); + torture_comment(tctx, "the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't"); + + fail: + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli2->tree, fnum2); + smbcli_unlink(cli1->tree, fname); + + return correct; +} + +/* + looks at lock upgrade/downgrade. +*/ +static bool torture_locktest5(struct torture_context *tctx, struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\lockt5.lck"; + int fnum1, fnum2, fnum3; + bool ret; + uint8_t buf[1000]; + bool correct = true; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); + fnum3 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + + memset(buf, 0, sizeof(buf)); + + torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf), + "Failed to create file"); + + /* Check for NT bug... */ + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 1, 0, READ_LOCK)); + smbcli_close(cli1->tree, fnum1); + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "this server %s the NT locking bug\n", ret ? "doesn't have" : "has"); + smbcli_close(cli1->tree, fnum1); + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); + smbcli_unlock(cli1->tree, fnum3, 0, 1); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 1, 1, 0, READ_LOCK)); + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s overlay a write with a read lock\n", ret?"can":"cannot"); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK)); + EXPECTED(ret, false); + + torture_comment(tctx, "a different process %s get a read lock on the first process lock stack\n", ret?"can":"cannot"); + + /* Unlock the process 2 lock. */ + smbcli_unlock(cli2->tree, fnum2, 0, 4); + + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 4, 0, READ_LOCK)); + EXPECTED(ret, false); + + torture_comment(tctx, "the same process on a different fnum %s get a read lock\n", ret?"can":"cannot"); + + /* Unlock the process 1 fnum3 lock. */ + smbcli_unlock(cli1->tree, fnum3, 0, 4); + + /* Stack 2 more locks here. */ + ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)) && + NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)); + + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s stack read locks\n", ret?"can":"cannot"); + + /* Unlock the first process lock, then check this was the WRITE lock that was + removed. */ + +ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) && + NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK)); + + EXPECTED(ret, true); + torture_comment(tctx, "the first unlock removes the %s lock\n", ret?"WRITE":"READ"); + + /* Unlock the process 2 lock. */ + smbcli_unlock(cli2->tree, fnum2, 0, 4); + + /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */ + + ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 1, 1)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) && + NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)); + + EXPECTED(ret, true); + torture_comment(tctx, "the same process %s unlock the stack of 3 locks\n", ret?"can":"cannot"); + + /* Ensure the next unlock fails. */ + ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)); + EXPECTED(ret, false); + torture_comment(tctx, "the same process %s count the lock stack\n", !ret?"can":"cannot"); + + /* Ensure connection 2 can get a write lock. */ + ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, WRITE_LOCK)); + EXPECTED(ret, true); + + torture_comment(tctx, "a different process %s get a write lock on the unlocked stack\n", ret?"can":"cannot"); + + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), + talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum3), + talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), + talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); + + return correct; +} + +/* + tries the unusual lockingX locktype bits +*/ +static bool torture_locktest6(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *fname[1] = { "\\lock6.txt" }; + int i; + int fnum; + NTSTATUS status; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + for (i=0;i<1;i++) { + torture_comment(tctx, "Testing %s\n", fname[i]); + + smbcli_unlink(cli->tree, fname[i]); + + fnum = smbcli_open(cli->tree, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE); + smbcli_close(cli->tree, fnum); + torture_comment(tctx, "CHANGE_LOCKTYPE gave %s\n", nt_errstr(status)); + + fnum = smbcli_open(cli->tree, fname[i], O_RDWR, DENY_NONE); + status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK); + smbcli_close(cli->tree, fnum); + torture_comment(tctx, "CANCEL_LOCK gave %s\n", nt_errstr(status)); + + smbcli_unlink(cli->tree, fname[i]); + } + + return true; +} + +static bool torture_locktest7(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = BASEDIR "\\lockt7.lck"; + int fnum1; + int fnum2 = -1; + size_t size; + uint8_t buf[200]; + bool correct = false; + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR), + talloc_asprintf(tctx, "Unable to set up %s", BASEDIR)); + + fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + + memset(buf, 0, sizeof(buf)); + + torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf), + "Failed to create file"); + + cli1->session->pid = 1; + + torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK), + talloc_asprintf(tctx, "Unable to apply read lock on range 130:4, error was %s", + smbcli_errstr(cli1->tree))); + + torture_comment(tctx, "pid1 successfully locked range 130:4 for READ\n"); + + torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4, + talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s)", + smbcli_errstr(cli1->tree))); + + torture_comment(tctx, "pid1 successfully read the range 130:4\n"); + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { + torture_comment(tctx, "pid1 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); + torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, + "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); + } else { + torture_fail(tctx, "pid1 successfully wrote to the range 130:4 (should be denied)"); + } + + cli1->session->pid = 2; + + if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) { + torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); + } else { + torture_comment(tctx, "pid2 successfully read the range 130:4\n"); + } + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { + torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); + torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, + "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); + } else { + torture_fail(tctx, "pid2 successfully wrote to the range 130:4 (should be denied)"); + } + + cli1->session->pid = 1; + smbcli_unlock(cli1->tree, fnum1, 130, 4); + + torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, WRITE_LOCK), + talloc_asprintf(tctx, "Unable to apply write lock on range 130:4, error was %s", + smbcli_errstr(cli1->tree))); + torture_comment(tctx, "pid1 successfully locked range 130:4 for WRITE\n"); + + torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4, + talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s", + smbcli_errstr(cli1->tree))); + torture_comment(tctx, "pid1 successfully read the range 130:4\n"); + + torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) == 4, + talloc_asprintf(tctx, "pid1 unable to write to the range 130:4, error was %s", + smbcli_errstr(cli1->tree))); + torture_comment(tctx, "pid1 successfully wrote to the range 130:4\n"); + + cli1->session->pid = 2; + + if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) { + torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", + smbcli_errstr(cli1->tree)); + torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, + "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); + } else { + torture_fail(tctx, "pid2 successfully read the range 130:4 (should be denied)"); + } + + if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { + torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", + smbcli_errstr(cli1->tree)); + if (!NT_STATUS_EQUAL(smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT)) { + torture_comment(tctx, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT) (%s)\n", + __location__); + goto fail; + } + } else { + torture_comment(tctx, "pid2 successfully wrote to the range 130:4 (should be denied) (%s)\n", + __location__); + goto fail; + } + + torture_comment(tctx, "Testing truncate of locked file.\n"); + + fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE); + + torture_assert(tctx, fnum2 != -1, "Unable to truncate locked file"); + + torture_comment(tctx, "Truncated locked file.\n"); + + torture_assert_ntstatus_ok(tctx, smbcli_getatr(cli1->tree, fname, NULL, &size, NULL), + talloc_asprintf(tctx, "getatr failed (%s)", smbcli_errstr(cli1->tree))); + + torture_assert(tctx, size == 0, talloc_asprintf(tctx, "Unable to truncate locked file. Size was %u", (unsigned)size)); + + cli1->session->pid = 1; + + smbcli_unlock(cli1->tree, fnum1, 130, 4); + correct = true; + +fail: + smbcli_close(cli1->tree, fnum1); + smbcli_close(cli1->tree, fnum2); + smbcli_unlink(cli1->tree, fname); + + return correct; +} + +struct torture_suite *torture_base_locktest(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "lock"); + torture_suite_add_2smb_test(suite, "LOCK1", torture_locktest1); + torture_suite_add_1smb_test(suite, "LOCK2", torture_locktest2); + torture_suite_add_2smb_test(suite, "LOCK3", torture_locktest3); + torture_suite_add_2smb_test(suite, "LOCK4", torture_locktest4); + torture_suite_add_2smb_test(suite, "LOCK5", torture_locktest5); + torture_suite_add_1smb_test(suite, "LOCK6", torture_locktest6); + torture_suite_add_1smb_test(suite, "LOCK7", torture_locktest7); + + return suite; +} diff --git a/source4/torture/basic/mangle_test.c b/source4/torture/basic/mangle_test.c new file mode 100644 index 0000000..9bd3cf5 --- /dev/null +++ b/source4/torture/basic/mangle_test.c @@ -0,0 +1,208 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - mangling test + Copyright (C) Andrew Tridgell 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/dir.h" +#include +#include "../lib/util/util_tdb.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +#undef strcasecmp + +static TDB_CONTEXT *tdb; + +#define NAME_LENGTH 20 + +static unsigned int total, collisions, failures; + +static bool test_one(struct torture_context *tctx ,struct smbcli_state *cli, + const char *name) +{ + int fnum; + const char *shortname; + const char *name2; + NTSTATUS status; + TDB_DATA data; + + total++; + + fnum = smbcli_open(cli->tree, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum == -1) { + printf("open of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) { + printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); + return false; + } + + /* get the short name */ + status = smbcli_qpathinfo_alt_name(cli->tree, name, &shortname); + if (!NT_STATUS_IS_OK(status)) { + printf("query altname of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); + return false; + } + + name2 = talloc_asprintf(tctx, "\\mangle_test\\%s", shortname); + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name2))) { + printf("unlink of %s (%s) failed (%s)\n", + name2, name, smbcli_errstr(cli->tree)); + return false; + } + + /* recreate by short name */ + fnum = smbcli_open(cli->tree, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum == -1) { + printf("open2 of %s failed (%s)\n", name2, smbcli_errstr(cli->tree)); + return false; + } + if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) { + printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); + return false; + } + + /* and unlink by long name */ + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name))) { + printf("unlink2 of %s (%s) failed (%s)\n", + name, name2, smbcli_errstr(cli->tree)); + failures++; + smbcli_unlink(cli->tree, name2); + return true; + } + + /* see if the short name is already in the tdb */ + data = tdb_fetch_bystring(tdb, shortname); + if (data.dptr) { + /* maybe its a duplicate long name? */ + if (strcasecmp(name, (const char *)data.dptr) != 0) { + /* we have a collision */ + collisions++; + printf("Collision between %s and %s -> %s " + " (coll/tot: %u/%u)\n", + name, data.dptr, shortname, collisions, total); + } + free(data.dptr); + } else { + TDB_DATA namedata; + /* store it for later */ + namedata.dptr = discard_const_p(uint8_t, name); + namedata.dsize = strlen(name)+1; + tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE); + } + + return true; +} + + +static char *gen_name(TALLOC_CTX *mem_ctx) +{ + const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~..."; + unsigned int max_idx = strlen(chars); + unsigned int len; + int i; + char *p; + char *name; + + name = talloc_strdup(mem_ctx, "\\mangle_test\\"); + + len = 1 + random() % NAME_LENGTH; + + name = talloc_realloc(mem_ctx, name, char, strlen(name) + len + 6); + p = name + strlen(name); + + for (i=0;i 5) && (random() % 10 == 0)) { + strlcpy(p, "ABCDE", 6); + } + + /* and a high probability of a good extension length */ + if (random() % 2 == 0) { + char *s = strrchr(p, '.'); + if (s) { + s[4] = 0; + } + } + + return name; +} + + +bool torture_mangle(struct torture_context *torture, + struct smbcli_state *cli) +{ + extern int torture_numops; + int i; + + /* we will use an internal tdb to store the names we have used */ + tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0); + if (!tdb) { + printf("ERROR: Failed to open tdb\n"); + return false; + } + + if (!torture_setup_dir(cli, "\\mangle_test")) { + return false; + } + + for (i=0;itree, "\\mangle_test\\*"); + if (NT_STATUS_IS_ERR(smbcli_rmdir(cli->tree, "\\mangle_test"))) { + printf("ERROR: Failed to remove directory\n"); + return false; + } + + printf("\nTotal collisions %u/%u - %.2f%% (%u failures)\n", + collisions, total, (100.0*collisions) / total, failures); + + return (failures == 0); +} diff --git a/source4/torture/basic/misc.c b/source4/torture/basic/misc.c new file mode 100644 index 0000000..60af561 --- /dev/null +++ b/source4/torture/basic/misc.c @@ -0,0 +1,1003 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/wait.h" +#include "system/filesys.h" +#include "../libcli/smb/smb_constants.h" +#include "libcli/libcli.h" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/composite/composite.h" +#include "param/param.h" +#include "torture/basic/proto.h" +#include "lib/cmdline/cmdline.h" + +static bool wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len) +{ + while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) { + if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; + } + return true; +} + + +static bool rw_torture(struct torture_context *tctx, struct smbcli_state *c) +{ + const char *lockfname = "\\torture.lck"; + char *fname; + int fnum; + int fnum2; + pid_t pid2, pid = getpid(); + int i, j; + uint8_t buf[1024]; + bool correct = true; + + fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL, + DENY_NONE); + if (fnum2 == -1) + fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE); + if (fnum2 == -1) { + torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree)); + return false; + } + + generate_random_buffer(buf, sizeof(buf)); + + for (i=0;itree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL); + if (fnum == -1) { + torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + break; + } + + if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) { + torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + + for (j=0;j<50;j++) { + if (smbcli_write(c->tree, fnum, 0, buf, + sizeof(pid)+(j*sizeof(buf)), + sizeof(buf)) != sizeof(buf)) { + torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + } + + pid2 = 0; + + if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) { + torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + + if (pid2 != pid) { + torture_comment(tctx, "data corruption!\n"); + correct = false; + } + + if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) { + torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + + if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) { + torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + + if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) { + torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree)); + correct = false; + } + free(fname); + } + + smbcli_close(c->tree, fnum2); + smbcli_unlink(c->tree, lockfname); + + torture_comment(tctx, "%d\n", i); + + return correct; +} + +bool run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy) +{ + return rw_torture(tctx, cli); +} + + +/* + see how many RPC pipes we can open at once +*/ +bool run_pipe_number(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *pipe_name = "\\WKSSVC"; + int fnum; + int num_pipes = 0; + + while(1) { + fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum == -1) { + torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree)); + break; + } + num_pipes++; + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%d\r", num_pipes); + fflush(stdout); + } + } + + torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name ); + return true; +} + + + + +/* + open N connections to the server and just hold them open + used for testing performance when there are N idle users + already connected + */ +bool torture_holdcon(struct torture_context *tctx) +{ + int i; + struct smbcli_state **cli; + int num_dead = 0; + + torture_comment(tctx, "Opening %d connections\n", torture_numops); + + cli = malloc_array_p(struct smbcli_state *, torture_numops); + + for (i=0;itree, "\\"); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Connection %d is dead\n", i); + cli[i] = NULL; + num_dead++; + } + usleep(100); + } + } + + if (num_dead == torture_numops) { + torture_comment(tctx, "All connections dead - finishing\n"); + break; + } + + torture_comment(tctx, "."); + fflush(stdout); + } + + return true; +} + +/* + open a file N times on the server and just hold them open + used for testing performance when there are N file handles + open + */ +bool torture_holdopen(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int i, fnum; + const char *fname = "\\holdopen.dat"; + NTSTATUS status; + + smbcli_unlink(cli->tree, fname); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum == -1) { + torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + + smbcli_close(cli->tree, fnum); + + for (i=0;itree, tctx, &op); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "open %d failed\n", i); + continue; + } + + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "opened %d file\r", i); + fflush(stdout); + } + } + + torture_comment(tctx, "\nStarting pings\n"); + + while (1) { + struct smb_echo ec; + ZERO_STRUCT(ec); + status = smb_raw_echo(cli->transport, &ec); + torture_comment(tctx, "."); + fflush(stdout); + sleep(15); + } +} + +/* +test how many open files this server supports on the one socket +*/ +bool torture_maxfid_test(struct torture_context *tctx, struct smbcli_state *cli) +{ +#define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d" + char *fname; + int fnums[0x11000], i; + int retries=4, maxfid; + bool correct = true; + int ret; + + if (retries <= 0) { + torture_comment(tctx, "failed to connect\n"); + return false; + } + + if (smbcli_deltree(cli->tree, "\\maxfid") == -1) { + torture_comment(tctx, "Failed to deltree \\maxfid - %s\n", + smbcli_errstr(cli->tree)); + return false; + } + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) { + torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n", + smbcli_errstr(cli->tree)); + return false; + } + + torture_comment(tctx, "Testing maximum number of open files\n"); + + for (i=0; i<0x11000; i++) { + if (i % 1000 == 0) { + ret = asprintf(&fname, "\\maxfid\\fid%d", i/1000); + torture_assert(tctx, ret != -1, "asprintf failed"); + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) { + torture_comment(tctx, "Failed to mkdir %s, error=%s\n", + fname, smbcli_errstr(cli->tree)); + return false; + } + free(fname); + } + ret = asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid()); + torture_assert(tctx, ret != -1, "asprintf failed"); + if ((fnums[i] = smbcli_open(cli->tree, fname, + O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == + -1) { + torture_comment(tctx, "open of %s failed (%s)\n", + fname, smbcli_errstr(cli->tree)); + torture_comment(tctx, "maximum fnum is %d\n", i); + break; + } + free(fname); + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%6d\r", i); + fflush(stdout); + } + } + torture_comment(tctx, "%6d\n", i); + + maxfid = i; + + torture_comment(tctx, "cleaning up\n"); + for (i=0;itree, fnums[i]))) { + torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) { + torture_comment(tctx, "unlink of %s failed (%s)\n", + fname, smbcli_errstr(cli->tree)); + correct = false; + } + free(fname); + + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%6d\r", i); + fflush(stdout); + } + } + torture_comment(tctx, "%6d\n", 0); + + if (smbcli_deltree(cli->tree, "\\maxfid") == -1) { + torture_comment(tctx, "Failed to deltree \\maxfid - %s\n", + smbcli_errstr(cli->tree)); + return false; + } + + torture_comment(tctx, "maxfid test finished\n"); + + return correct; +#undef MAXFID_TEMPLATE +} + + + +/* + sees what IOCTLs are supported + */ +bool torture_ioctl_test(struct torture_context *tctx, + struct smbcli_state *cli) +{ + uint16_t device, function; + int fnum; + const char *fname = "\\ioctl.dat"; + NTSTATUS status; + union smb_ioctl parms; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named_const(tctx, 0, "ioctl_test"); + + smbcli_unlink(cli->tree, fname); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum == -1) { + torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + + parms.ioctl.level = RAW_IOCTL_IOCTL; + parms.ioctl.in.file.fnum = fnum; + parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO; + status = smb_raw_ioctl(cli->tree, mem_ctx, &parms); + torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree)); + + for (device=0;device<0x100;device++) { + torture_comment(tctx, "Testing device=0x%x\n", device); + for (function=0;function<0x100;function++) { + parms.ioctl.in.request = (device << 16) | function; + status = smb_raw_ioctl(cli->tree, mem_ctx, &parms); + + if (NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n", + device, function, (int)parms.ioctl.out.blob.length); + } + } + } + + return true; +} + +static void benchrw_callback(struct smbcli_request *req); +enum benchrw_stage { + START, + OPEN_CONNECTION, + CLEANUP_TESTDIR, + MK_TESTDIR, + OPEN_FILE, + INITIAL_WRITE, + READ_WRITE_DATA, + MAX_OPS_REACHED, + ERROR, + CLOSE_FILE, + CLEANUP, + FINISHED +}; + +struct bench_params { + struct unclist{ + const char *host; + const char *share; + } **unc; + const char *workgroup; + int retry; + unsigned int writeblocks; + unsigned int blocksize; + unsigned int writeratio; + int num_parallel_requests; +}; + +struct benchrw_state { + struct torture_context *tctx; + char *dname; + char *fname; + uint16_t fnum; + int nr; + struct smbcli_tree *cli; + uint8_t *buffer; + int writecnt; + int readcnt; + int completed; + int num_parallel_requests; + void *req_params; + enum benchrw_stage mode; + struct bench_params *lpcfg_params; +}; + +/* + init params using lpcfg_parm_xxx + return number of unclist entries +*/ +static int init_benchrw_params(struct torture_context *tctx, + struct bench_params *lpar) +{ + char **unc_list = NULL; + int num_unc_names = 0, conn_index=0, empty_lines=0; + const char *p; + lpar->retry = torture_setting_int(tctx, "retry",3); + lpar->blocksize = torture_setting_int(tctx, "blocksize",65535); + lpar->writeblocks = torture_setting_int(tctx, "writeblocks",15); + lpar->writeratio = torture_setting_int(tctx, "writeratio",5); + lpar->num_parallel_requests = torture_setting_int( + tctx, "parallel_requests", 5); + lpar->workgroup = lpcfg_workgroup(tctx->lp_ctx); + + p = torture_setting_string(tctx, "unclist", NULL); + if (p) { + char *h, *s; + unc_list = file_lines_load(p, &num_unc_names, 0, NULL); + if (!unc_list || num_unc_names <= 0) { + torture_comment(tctx, "Failed to load unc names list " + "from '%s'\n", p); + exit(1); + } + + lpar->unc = talloc_array(tctx, struct unclist *, + (num_unc_names-empty_lines)); + for(conn_index = 0; conn_index < num_unc_names; conn_index++) { + /* ignore empty lines */ + if(strlen(unc_list[conn_index % num_unc_names])==0){ + empty_lines++; + continue; + } + if (!smbcli_parse_unc( + unc_list[conn_index % num_unc_names], + NULL, &h, &s)) { + torture_comment( + tctx, "Failed to parse UNC " + "name %s\n", + unc_list[conn_index % num_unc_names]); + exit(1); + } + lpar->unc[conn_index-empty_lines] = + talloc(tctx, struct unclist); + lpar->unc[conn_index-empty_lines]->host = h; + lpar->unc[conn_index-empty_lines]->share = s; + } + return num_unc_names-empty_lines; + }else{ + lpar->unc = talloc_array(tctx, struct unclist *, 1); + lpar->unc[0] = talloc(tctx,struct unclist); + lpar->unc[0]->host = torture_setting_string(tctx, "host", + NULL); + lpar->unc[0]->share = torture_setting_string(tctx, "share", + NULL); + return 1; + } +} + +/* + Called when the reads & writes are finished. closes the file. +*/ +static NTSTATUS benchrw_close(struct torture_context *tctx, + struct smbcli_request *req, + struct benchrw_state *state) +{ + union smb_close close_parms; + + NT_STATUS_NOT_OK_RETURN(req->status); + + torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum); + close_parms.close.level = RAW_CLOSE_CLOSE; + close_parms.close.in.file.fnum = state->fnum ; + close_parms.close.in.write_time = 0; + state->mode=CLOSE_FILE; + + req = smb_raw_close_send(state->cli, &close_parms); + NT_STATUS_HAVE_NO_MEMORY(req); + /*register the callback function!*/ + req->async.fn = benchrw_callback; + req->async.private_data = state; + + return NT_STATUS_OK; +} + +static NTSTATUS benchrw_readwrite(struct torture_context *tctx, + struct benchrw_state *state); +static void benchrw_callback(struct smbcli_request *req); + +static void benchrw_rw_callback(struct smbcli_request *req) +{ + struct benchrw_state *state = req->async.private_data; + struct torture_context *tctx = state->tctx; + + if (!NT_STATUS_IS_OK(req->status)) { + state->mode = ERROR; + return; + } + + state->completed++; + state->num_parallel_requests--; + + if ((state->completed >= torture_numops) + && (state->num_parallel_requests == 0)) { + benchrw_callback(req); + talloc_free(req); + return; + } + + talloc_free(req); + + if (state->completed + state->num_parallel_requests + < torture_numops) { + benchrw_readwrite(tctx, state); + } +} + +/* + Called when the initial write is completed is done. write or read a file. +*/ +static NTSTATUS benchrw_readwrite(struct torture_context *tctx, + struct benchrw_state *state) +{ + struct smbcli_request *req; + union smb_read rd; + union smb_write wr; + + /* randomize between writes and reads*/ + if (random() % state->lpcfg_params->writeratio == 0) { + torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n", + state->nr,state->completed,torture_numops); + wr.generic.level = RAW_WRITE_WRITEX ; + wr.writex.in.file.fnum = state->fnum ; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0 ; + wr.writex.in.remaining = 0; + wr.writex.in.count = state->lpcfg_params->blocksize; + wr.writex.in.data = state->buffer; + state->readcnt=0; + req = smb_raw_write_send(state->cli,&wr); + } + else { + torture_comment(tctx, + "Callback READ file:%d (%d/%d) Offset:%d\n", + state->nr,state->completed,torture_numops, + (state->readcnt*state->lpcfg_params->blocksize)); + rd.generic.level = RAW_READ_READX; + rd.readx.in.file.fnum = state->fnum ; + rd.readx.in.offset = state->readcnt*state->lpcfg_params->blocksize; + rd.readx.in.mincnt = state->lpcfg_params->blocksize; + rd.readx.in.maxcnt = rd.readx.in.mincnt; + rd.readx.in.remaining = 0 ; + rd.readx.out.data = state->buffer; + rd.readx.in.read_for_execute = false; + if(state->readcnt < state->lpcfg_params->writeblocks){ + state->readcnt++; + }else{ + /*start reading from beginning of file*/ + state->readcnt=0; + } + req = smb_raw_read_send(state->cli,&rd); + } + state->num_parallel_requests += 1; + NT_STATUS_HAVE_NO_MEMORY(req); + /*register the callback function!*/ + req->async.fn = benchrw_rw_callback; + req->async.private_data = state; + + return NT_STATUS_OK; +} + +/* + Called when the open is done. writes to the file. +*/ +static NTSTATUS benchrw_open(struct torture_context *tctx, + struct smbcli_request *req, + struct benchrw_state *state) +{ + union smb_write wr; + if(state->mode == OPEN_FILE){ + NTSTATUS status; + status = smb_raw_open_recv(req,tctx,( + union smb_open*)state->req_params); + NT_STATUS_NOT_OK_RETURN(status); + + state->fnum = ((union smb_open*)state->req_params) + ->openx.out.file.fnum; + torture_comment(tctx, "File opened (%d)\n",state->fnum); + state->mode=INITIAL_WRITE; + } + + torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr, + (state->writecnt+1)*state->lpcfg_params->blocksize, + (state->lpcfg_params->writeblocks*state->lpcfg_params->blocksize)); + wr.generic.level = RAW_WRITE_WRITEX ; + wr.writex.in.file.fnum = state->fnum ; + wr.writex.in.offset = state->writecnt * + state->lpcfg_params->blocksize; + wr.writex.in.wmode = 0 ; + wr.writex.in.remaining = (state->lpcfg_params->writeblocks * + state->lpcfg_params->blocksize)- + ((state->writecnt+1)*state-> + lpcfg_params->blocksize); + wr.writex.in.count = state->lpcfg_params->blocksize; + wr.writex.in.data = state->buffer; + state->writecnt++; + if(state->writecnt == state->lpcfg_params->writeblocks){ + state->mode=READ_WRITE_DATA; + } + req = smb_raw_write_send(state->cli,&wr); + NT_STATUS_HAVE_NO_MEMORY(req); + + /*register the callback function!*/ + req->async.fn = benchrw_callback; + req->async.private_data = state; + return NT_STATUS_OK; +} + +/* + Called when the mkdir is done. Opens a file. +*/ +static NTSTATUS benchrw_mkdir(struct torture_context *tctx, + struct smbcli_request *req, + struct benchrw_state *state) +{ + union smb_open *open_parms; + uint8_t *writedata; + + NT_STATUS_NOT_OK_RETURN(req->status); + + /* open/create the files */ + torture_comment(tctx, "Open File %d/%d\n",state->nr+1, + torture_setting_int(tctx, "nprocs", 4)); + open_parms=talloc_zero(tctx, union smb_open); + NT_STATUS_HAVE_NO_MEMORY(open_parms); + open_parms->openx.level = RAW_OPEN_OPENX; + open_parms->openx.in.flags = 0; + open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + open_parms->openx.in.search_attrs = + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + open_parms->openx.in.file_attrs = 0; + open_parms->openx.in.write_time = 0; + open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE; + open_parms->openx.in.size = 0; + open_parms->openx.in.timeout = 0; + open_parms->openx.in.fname = state->fname; + + writedata = talloc_size(tctx,state->lpcfg_params->blocksize); + NT_STATUS_HAVE_NO_MEMORY(writedata); + generate_random_buffer(writedata,state->lpcfg_params->blocksize); + state->buffer=writedata; + state->writecnt=1; + state->readcnt=0; + state->req_params=open_parms; + state->mode=OPEN_FILE; + + req = smb_raw_open_send(state->cli,open_parms); + NT_STATUS_HAVE_NO_MEMORY(req); + + /*register the callback function!*/ + req->async.fn = benchrw_callback; + req->async.private_data = state; + + return NT_STATUS_OK; +} + +/* + handler for completion of a sub-request of the bench-rw test +*/ +static void benchrw_callback(struct smbcli_request *req) +{ + struct benchrw_state *state = req->async.private_data; + struct torture_context *tctx = state->tctx; + + /*don't send new requests when torture_numops is reached*/ + if ((state->mode == READ_WRITE_DATA) + && (state->completed >= torture_numops)) { + state->mode=MAX_OPS_REACHED; + } + + switch (state->mode) { + + case MK_TESTDIR: + if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) { + torture_comment(tctx, "Failed to create the test " + "directory - %s\n", + nt_errstr(req->status)); + state->mode=ERROR; + return; + } + break; + case OPEN_FILE: + case INITIAL_WRITE: + if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){ + torture_comment(tctx, "Failed to open/write the " + "file - %s\n", + nt_errstr(req->status)); + state->mode=ERROR; + state->readcnt=0; + return; + } + break; + case READ_WRITE_DATA: + while (state->num_parallel_requests + < state->lpcfg_params->num_parallel_requests) { + NTSTATUS status; + status = benchrw_readwrite(tctx,state); + if (!NT_STATUS_IS_OK(status)){ + torture_comment(tctx, "Failed to read/write " + "the file - %s\n", + nt_errstr(req->status)); + state->mode=ERROR; + return; + } + } + break; + case MAX_OPS_REACHED: + if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){ + torture_comment(tctx, "Failed to read/write/close " + "the file - %s\n", + nt_errstr(req->status)); + state->mode=ERROR; + return; + } + break; + case CLOSE_FILE: + torture_comment(tctx, "File %d closed\n",state->nr); + if (!NT_STATUS_IS_OK(req->status)) { + torture_comment(tctx, "Failed to close the " + "file - %s\n", + nt_errstr(req->status)); + state->mode=ERROR; + return; + } + state->mode=CLEANUP; + return; + default: + break; + } + +} + +/* open connection async callback function*/ +static void async_open_callback(struct composite_context *con) +{ + struct benchrw_state *state = con->async.private_data; + struct torture_context *tctx = state->tctx; + int retry = state->lpcfg_params->retry; + + if (NT_STATUS_IS_OK(con->status)) { + state->cli=((struct smb_composite_connect*) + state->req_params)->out.tree; + state->mode=CLEANUP_TESTDIR; + }else{ + if(state->writecnt < retry){ + torture_comment(tctx, "Failed to open connection: " + "%d, Retry (%d/%d)\n", + state->nr,state->writecnt,retry); + state->writecnt++; + state->mode=START; + usleep(1000); + }else{ + torture_comment(tctx, "Failed to open connection " + "(%d) - %s\n", + state->nr, nt_errstr(con->status)); + state->mode=ERROR; + } + return; + } +} + +/* + establishes a smbcli_tree from scratch (async) +*/ +static struct composite_context *torture_connect_async( + struct torture_context *tctx, + struct smb_composite_connect *smb, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *host, + const char *share, + const char *workgroup) +{ + torture_comment(tctx, "Open Connection to %s/%s\n",host,share); + smb->in.dest_host=talloc_strdup(mem_ctx,host); + smb->in.service=talloc_strdup(mem_ctx,share); + smb->in.dest_ports=lpcfg_smb_ports(tctx->lp_ctx); + smb->in.socket_options = lpcfg_socket_options(tctx->lp_ctx); + smb->in.called_name = strupper_talloc(mem_ctx, host); + smb->in.service_type=NULL; + smb->in.credentials = samba_cmdline_get_creds(); + smb->in.fallback_to_anonymous=false; + smb->in.gensec_settings = lpcfg_gensec_settings(mem_ctx, tctx->lp_ctx); + smb->in.workgroup=workgroup; + lpcfg_smbcli_options(tctx->lp_ctx, &smb->in.options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &smb->in.session_options); + + return smb_composite_connect_send(smb,mem_ctx, + lpcfg_resolve_context(tctx->lp_ctx),ev); +} + +bool run_benchrw(struct torture_context *tctx) +{ + struct smb_composite_connect *smb_con; + const char *fname = "\\rwtest.dat"; + struct smbcli_request *req; + struct benchrw_state **state; + int i , num_unc_names; + struct tevent_context *ev ; + struct composite_context *req1; + struct bench_params lpparams; + union smb_mkdir parms; + int finished = 0; + bool success=true; + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + + torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d " + "num_nprocs=%d\n", + torture_numops, torture_nprocs); + + /*init talloc context*/ + ev = tctx->ev; + state = talloc_array(tctx, struct benchrw_state *, torture_nprocs); + + /* init params using lpcfg_parm_xxx */ + num_unc_names = init_benchrw_params(tctx,&lpparams); + + /* init private data structs*/ + for(i = 0; itctx = tctx; + state[i]->completed=0; + state[i]->num_parallel_requests=0; + state[i]->lpcfg_params=&lpparams; + state[i]->nr=i; + state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i); + state[i]->fname=talloc_asprintf(tctx,"%s%s", + state[i]->dname,fname); + state[i]->mode=START; + state[i]->writecnt=0; + } + + torture_comment(tctx, "Starting async requests\n"); + while(finished != torture_nprocs){ + finished=0; + for(i = 0; imode){ + /*open multiple connections with the same userid */ + case START: + smb_con = talloc_zero( + tctx,struct smb_composite_connect); + state[i]->req_params=smb_con; + state[i]->mode=OPEN_CONNECTION; + req1 = torture_connect_async( + tctx, smb_con, tctx,ev, + lpparams.unc[i % num_unc_names]->host, + lpparams.unc[i % num_unc_names]->share, + lpparams.workgroup); + /* register callback fn + private data */ + req1->async.fn = async_open_callback; + req1->async.private_data=state[i]; + break; + /*setup test dirs (sync)*/ + case CLEANUP_TESTDIR: + torture_comment(tctx, "Setup test dir %d\n",i); + smb_raw_exit(state[i]->cli->session); + if (smbcli_deltree(state[i]->cli, + state[i]->dname) == -1) { + torture_comment( + tctx, + "Unable to delete %s - %s\n", + state[i]->dname, + smbcli_errstr(state[i]->cli)); + state[i]->mode=ERROR; + break; + } + state[i]->mode=MK_TESTDIR; + parms.mkdir.level = RAW_MKDIR_MKDIR; + parms.mkdir.in.path = state[i]->dname; + req = smb_raw_mkdir_send(state[i]->cli,&parms); + /* register callback fn + private data */ + req->async.fn = benchrw_callback; + req->async.private_data=state[i]; + break; + /* error occurred , finish */ + case ERROR: + finished++; + success=false; + break; + /* cleanup , close connection */ + case CLEANUP: + torture_comment(tctx, "Deleting test dir %s " + "%d/%d\n",state[i]->dname, + i+1,torture_nprocs); + smbcli_deltree(state[i]->cli,state[i]->dname); + if (NT_STATUS_IS_ERR(smb_tree_disconnect( + state[i]->cli))) { + torture_comment(tctx, "ERROR: Tree " + "disconnect failed"); + state[i]->mode=ERROR; + break; + } + state[i]->mode=FINISHED; + + FALL_THROUGH; + case FINISHED: + finished++; + break; + default: + tevent_loop_once(ev); + } + } + } + + return success; +} + diff --git a/source4/torture/basic/properties.c b/source4/torture/basic/properties.c new file mode 100644 index 0000000..b63acc7 --- /dev/null +++ b/source4/torture/basic/properties.c @@ -0,0 +1,118 @@ +/* + Unix SMB/CIFS implementation. + + show server properties + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +struct bitmapping { + const char *name; + uint32_t value; +}; + +#define BIT_NAME(x) { #x, x } + +const static struct bitmapping fs_attr_bits[] = { + BIT_NAME(FS_ATTR_CASE_SENSITIVE_SEARCH), + BIT_NAME(FS_ATTR_CASE_PRESERVED_NAMES), + BIT_NAME(FS_ATTR_UNICODE_ON_DISK), + BIT_NAME(FS_ATTR_PERSISTANT_ACLS), + BIT_NAME(FS_ATTR_COMPRESSION), + BIT_NAME(FS_ATTR_QUOTAS), + BIT_NAME(FS_ATTR_SPARSE_FILES), + BIT_NAME(FS_ATTR_REPARSE_POINTS), + BIT_NAME(FS_ATTR_REMOTE_STORAGE), + BIT_NAME(FS_ATTR_LFN_SUPPORT), + BIT_NAME(FS_ATTR_IS_COMPRESSED), + BIT_NAME(FS_ATTR_OBJECT_IDS), + BIT_NAME(FS_ATTR_ENCRYPTION), + BIT_NAME(FS_ATTR_NAMED_STREAMS), + { NULL, 0 } +}; + +const static struct bitmapping capability_bits[] = { + BIT_NAME(CAP_RAW_MODE), + BIT_NAME(CAP_MPX_MODE), + BIT_NAME(CAP_UNICODE), + BIT_NAME(CAP_LARGE_FILES), + BIT_NAME(CAP_NT_SMBS), + BIT_NAME(CAP_RPC_REMOTE_APIS), + BIT_NAME(CAP_STATUS32), + BIT_NAME(CAP_LEVEL_II_OPLOCKS), + BIT_NAME(CAP_LOCK_AND_READ), + BIT_NAME(CAP_NT_FIND), + BIT_NAME(CAP_DFS), + BIT_NAME(CAP_W2K_SMBS), + BIT_NAME(CAP_LARGE_READX), + BIT_NAME(CAP_LARGE_WRITEX), + BIT_NAME(CAP_UNIX), + BIT_NAME(CAP_EXTENDED_SECURITY), + { NULL, 0 } +}; + +static void show_bits(const struct bitmapping *bm, uint32_t value) +{ + int i; + for (i=0;bm[i].name;i++) { + if (value & bm[i].value) { + d_printf("\t%s\n", bm[i].name); + value &= ~bm[i].value; + } + } + if (value != 0) { + d_printf("\tunknown bits: 0x%08x\n", value); + } +} + + +/* + print out server properties + */ +bool torture_test_properties(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool correct = true; + union smb_fsinfo fs; + NTSTATUS status; + + d_printf("Capabilities: 0x%08x\n", cli->transport->negotiate.capabilities); + show_bits(capability_bits, cli->transport->negotiate.capabilities); + d_printf("\n"); + + fs.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO; + status = smb_raw_fsinfo(cli->tree, cli, &fs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("qfsinfo failed - %s\n", nt_errstr(status)); + correct = false; + } else { + d_printf("Filesystem attributes: 0x%08x\n", + fs.attribute_info.out.fs_attr); + show_bits(fs_attr_bits, fs.attribute_info.out.fs_attr); + d_printf("max_file_component_length: %d\n", + fs.attribute_info.out.max_file_component_length); + d_printf("fstype: %s\n", fs.attribute_info.out.fs_type.s); + } + + return correct; +} + + diff --git a/source4/torture/basic/rename.c b/source4/torture/basic/rename.c new file mode 100644 index 0000000..a80dd6e --- /dev/null +++ b/source4/torture/basic/rename.c @@ -0,0 +1,98 @@ +/* + Unix SMB/CIFS implementation. + + rename testing + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +/* + Test rename on files open with share delete and no share delete. + */ +bool torture_test_rename(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = "\\test.txt"; + const char *fname1 = "\\test1.txt"; + int fnum1; + + smbcli_unlink(cli1->tree, fname); + smbcli_unlink(cli1->tree, fname1); + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "First open failed - %s", + smbcli_errstr(cli1->tree))); + + torture_assert(tctx, NT_STATUS_IS_ERR(smbcli_rename(cli1->tree, fname, fname1)), + "First rename succeeded - this should have failed !"); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 1 failed (%s)", smbcli_errstr(cli1->tree))); + + smbcli_unlink(cli1->tree, fname); + smbcli_unlink(cli1->tree, fname1); + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_READ, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, + "Second open failed - %s", smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_rename(cli1->tree, fname, fname1), + talloc_asprintf(tctx, + "Second rename failed - this should have succeeded - %s", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, + "close - 2 failed (%s)", smbcli_errstr(cli1->tree))); + + smbcli_unlink(cli1->tree, fname); + smbcli_unlink(cli1->tree, fname1); + + fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0, + SEC_STD_READ_CONTROL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "Third open failed - %s", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_rename(cli1->tree, fname, fname1), + talloc_asprintf(tctx, "Third rename failed - this should have succeeded - %s", + smbcli_errstr(cli1->tree))); + + torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), + talloc_asprintf(tctx, "close - 3 failed (%s)", smbcli_errstr(cli1->tree))); + + smbcli_unlink(cli1->tree, fname); + smbcli_unlink(cli1->tree, fname1); + + return true; +} + diff --git a/source4/torture/basic/scanner.c b/source4/torture/basic/scanner.c new file mode 100644 index 0000000..144b7d0 --- /dev/null +++ b/source4/torture/basic/scanner.c @@ -0,0 +1,623 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - scanning functions + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "libcli/raw/raw_proto.h" +#include "system/filesys.h" +#include "param/param.h" +#include "torture/basic/proto.h" + +#define VERBOSE 0 +#define OP_MIN 0 +#define OP_MAX 100 +#define PARAM_SIZE 1024 + +/**************************************************************************** +look for a partial hit +****************************************************************************/ +static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status) +{ + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) || + NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + return; + } +#if VERBOSE + printf("possible %s hit op=%3d level=%5d status=%s\n", + format, op, level, nt_errstr(status)); +#endif +} + +/**************************************************************************** +check for existence of a trans2 call +****************************************************************************/ +static NTSTATUS try_trans2(struct smbcli_state *cli, + int op, + uint8_t *param, uint8_t *data, + int param_len, int data_len, + int *rparam_len, int *rdata_len) +{ + NTSTATUS status; + struct smb_trans2 t2; + uint16_t setup = op; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("try_trans2"); + + t2.in.max_param = UINT16_MAX; + t2.in.max_data = UINT16_MAX; + t2.in.max_setup = 10; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params.data = param; + t2.in.params.length = param_len; + t2.in.data.data = data; + t2.in.data.length = data_len; + + status = smb_raw_trans2(cli->tree, mem_ctx, &t2); + + *rparam_len = t2.out.params.length; + *rdata_len = t2.out.data.length; + + talloc_free(mem_ctx); + + return status; +} + + +static NTSTATUS try_trans2_len(struct smbcli_state *cli, + const char *format, + int op, int level, + uint8_t *param, uint8_t *data, + int param_len, int *data_len, + int *rparam_len, int *rdata_len) +{ + NTSTATUS ret=NT_STATUS_OK; + + ret = try_trans2(cli, op, param, data, param_len, + PARAM_SIZE, rparam_len, rdata_len); +#if VERBOSE + printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret)); +#endif + if (!NT_STATUS_IS_OK(ret)) return ret; + + *data_len = 0; + while (*data_len < PARAM_SIZE) { + ret = try_trans2(cli, op, param, data, param_len, + *data_len, rparam_len, rdata_len); + if (NT_STATUS_IS_OK(ret)) break; + *data_len += 2; + } + if (NT_STATUS_IS_OK(ret)) { + printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n", + format, level, *data_len, *rparam_len, *rdata_len); + } else { + trans2_check_hit(format, op, level, ret); + } + return ret; +} + + +/**************************************************************************** +check whether a trans2 opnum exists at all +****************************************************************************/ +static bool trans2_op_exists(struct smbcli_state *cli, int op) +{ + int data_len = PARAM_SIZE; + int param_len = PARAM_SIZE; + int rparam_len, rdata_len; + uint8_t *param, *data; + NTSTATUS status1, status2; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("trans2_op_exists"); + + /* try with a info level only */ + + param = talloc_array(mem_ctx, uint8_t, param_len); + data = talloc_array(mem_ctx, uint8_t, data_len); + + memset(param, 0xFF, param_len); + memset(data, 0xFF, data_len); + + status1 = try_trans2(cli, 0xFFFF, param, data, param_len, data_len, + &rparam_len, &rdata_len); + + status2 = try_trans2(cli, op, param, data, param_len, data_len, + &rparam_len, &rdata_len); + + if (NT_STATUS_EQUAL(status1, status2)) { + talloc_free(mem_ctx); + return false; + } + + printf("Found op %d (status=%s)\n", op, nt_errstr(status2)); + + talloc_free(mem_ctx); + return true; +} + +/**************************************************************************** +check for existence of a trans2 call +****************************************************************************/ +static bool scan_trans2( + struct smbcli_state *cli, int op, int level, + int fnum, int dnum, int qfnum, const char *fname) +{ + int data_len = 0; + int param_len = 0; + int rparam_len, rdata_len; + uint8_t *param, *data; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("scan_trans2"); + + data = talloc_array(mem_ctx, uint8_t, PARAM_SIZE); + param = talloc_array(mem_ctx, uint8_t, PARAM_SIZE); + + memset(data, 0, PARAM_SIZE); + data_len = 4; + + /* try with a info level only */ + param_len = 2; + SSVAL(param, 0, level); + status = try_trans2_len(cli, "void", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a file descriptor */ + param_len = 6; + SSVAL(param, 0, fnum); + SSVAL(param, 2, level); + SSVAL(param, 4, 0); + status = try_trans2_len(cli, "fnum", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a quota file descriptor */ + param_len = 6; + SSVAL(param, 0, qfnum); + SSVAL(param, 2, level); + SSVAL(param, 4, 0); + status = try_trans2_len(cli, "qfnum", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a notify style */ + param_len = 6; + SSVAL(param, 0, dnum); + SSVAL(param, 2, dnum); + SSVAL(param, 4, level); + status = try_trans2_len(cli, "notify", op, level, param, data, + param_len, &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string( + ¶m[6], fname, PARAM_SIZE-7, + STR_TERMINATE|STR_UNICODE); + + status = try_trans2_len(cli, "fname", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a new file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string( + ¶m[6], "\\newfile.dat", PARAM_SIZE-7, + STR_TERMINATE|STR_UNICODE); + + status = try_trans2_len(cli, "newfile", op, level, param, data, + param_len, &data_len, &rparam_len, &rdata_len); + smbcli_unlink(cli->tree, "\\newfile.dat"); + smbcli_rmdir(cli->tree, "\\newfile.dat"); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try dfs style */ + smbcli_mkdir(cli->tree, "\\testdir"); + param_len = 2; + SSVAL(param, 0, level); + param_len += push_string( + ¶m[2], "\\testdir", PARAM_SIZE-3, + STR_TERMINATE|STR_UNICODE); + + status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + smbcli_rmdir(cli->tree, "\\testdir"); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + talloc_free(mem_ctx); + return false; +} + +bool torture_trans2_scan(struct torture_context *torture, + struct smbcli_state *cli) +{ + int op, level; + const char *fname = "\\scanner.dat"; + int fnum, dnum, qfnum; + + fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE); + if (fnum == -1) { + printf("file open failed - %s\n", smbcli_errstr(cli->tree)); + } + dnum = smbcli_nt_create_full(cli->tree, "\\", + 0, + SEC_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + if (dnum == -1) { + printf("directory open failed - %s\n", smbcli_errstr(cli->tree)); + } + qfnum = smbcli_nt_create_full(cli->tree, "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION", + NTCREATEX_FLAGS_EXTENDED, + SEC_FLAG_MAXIMUM_ALLOWED, + 0, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN, + 0, 0); + if (qfnum == -1) { + printf("quota open failed - %s\n", smbcli_errstr(cli->tree)); + } + + for (op=OP_MIN; op<=OP_MAX; op++) { + + if (!trans2_op_exists(cli, op)) { + continue; + } + + for (level = 0; level <= 50; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + + for (level = 0x100; level <= 0x130; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + + for (level = 1000; level < 1050; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + } + + return true; +} + + + + +/**************************************************************************** +look for a partial hit +****************************************************************************/ +static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status) +{ + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) || + NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + return; + } +#if VERBOSE + printf("possible %s hit op=%3d level=%5d status=%s\n", + format, op, level, nt_errstr(status)); +#endif +} + +/**************************************************************************** +check for existence of a nttrans call +****************************************************************************/ +static NTSTATUS try_nttrans(struct smbcli_state *cli, + int op, + uint8_t *param, uint8_t *data, + int param_len, int data_len, + int *rparam_len, int *rdata_len) +{ + struct smb_nttrans parms; + DATA_BLOB ntparam_blob, ntdata_blob; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("try_nttrans"); + + ntparam_blob.length = param_len; + ntparam_blob.data = param; + ntdata_blob.length = data_len; + ntdata_blob.data = data; + + parms.in.max_param = UINT32_MAX; + parms.in.max_data = UINT32_MAX; + parms.in.max_setup = 0; + parms.in.setup_count = 0; + parms.in.function = op; + parms.in.params = ntparam_blob; + parms.in.data = ntdata_blob; + + status = smb_raw_nttrans(cli->tree, mem_ctx, &parms); + + if (NT_STATUS_IS_ERR(status)) { + DEBUG(1,("Failed to send NT_TRANS\n")); + talloc_free(mem_ctx); + return status; + } + *rparam_len = parms.out.params.length; + *rdata_len = parms.out.data.length; + + talloc_free(mem_ctx); + + return status; +} + + +static NTSTATUS try_nttrans_len(struct smbcli_state *cli, + const char *format, + int op, int level, + uint8_t *param, uint8_t *data, + int param_len, int *data_len, + int *rparam_len, int *rdata_len) +{ + NTSTATUS ret=NT_STATUS_OK; + + ret = try_nttrans(cli, op, param, data, param_len, + PARAM_SIZE, rparam_len, rdata_len); +#if VERBOSE + printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret)); +#endif + if (!NT_STATUS_IS_OK(ret)) return ret; + + *data_len = 0; + while (*data_len < PARAM_SIZE) { + ret = try_nttrans(cli, op, param, data, param_len, + *data_len, rparam_len, rdata_len); + if (NT_STATUS_IS_OK(ret)) break; + *data_len += 2; + } + if (NT_STATUS_IS_OK(ret)) { + printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n", + format, level, *data_len, *rparam_len, *rdata_len); + } else { + nttrans_check_hit(format, op, level, ret); + } + return ret; +} + +/**************************************************************************** +check for existence of a nttrans call +****************************************************************************/ +static bool scan_nttrans(struct smbcli_state *cli, int op, int level, + int fnum, int dnum, const char *fname) +{ + int data_len = 0; + int param_len = 0; + int rparam_len, rdata_len; + uint8_t *param, *data; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("scan_nttrans"); + + param = talloc_array(mem_ctx, uint8_t, PARAM_SIZE); + data = talloc_array(mem_ctx, uint8_t, PARAM_SIZE); + memset(data, 0, PARAM_SIZE); + data_len = 4; + + /* try with a info level only */ + param_len = 2; + SSVAL(param, 0, level); + status = try_nttrans_len(cli, "void", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a file descriptor */ + param_len = 6; + SSVAL(param, 0, fnum); + SSVAL(param, 2, level); + SSVAL(param, 4, 0); + status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a notify style */ + param_len = 6; + SSVAL(param, 0, dnum); + SSVAL(param, 2, dnum); + SSVAL(param, 4, level); + status = try_nttrans_len(cli, "notify", op, level, param, data, + param_len, &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string( + ¶m[6], fname, PARAM_SIZE, + STR_TERMINATE | STR_UNICODE); + + status = try_nttrans_len(cli, "fname", op, level, param, data, + param_len, &data_len, &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try with a new file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string( + ¶m[6], "\\newfile.dat", PARAM_SIZE, + STR_TERMINATE | STR_UNICODE); + + status = try_nttrans_len(cli, "newfile", op, level, param, data, + param_len, &data_len, &rparam_len, &rdata_len); + smbcli_unlink(cli->tree, "\\newfile.dat"); + smbcli_rmdir(cli->tree, "\\newfile.dat"); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + /* try dfs style */ + smbcli_mkdir(cli->tree, "\\testdir"); + param_len = 2; + SSVAL(param, 0, level); + param_len += push_string(¶m[2], "\\testdir", PARAM_SIZE, + STR_TERMINATE | STR_UNICODE); + + status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, + &data_len, &rparam_len, &rdata_len); + smbcli_rmdir(cli->tree, "\\testdir"); + if (NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return true; + } + + talloc_free(mem_ctx); + return false; +} + + +bool torture_nttrans_scan(struct torture_context *torture, + struct smbcli_state *cli) +{ + int op, level; + const char *fname = "\\scanner.dat"; + int fnum, dnum; + + fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + dnum = smbcli_open(cli->tree, "\\", O_RDONLY, DENY_NONE); + + for (op=OP_MIN; op<=OP_MAX; op++) { + printf("Scanning op=%d\n", op); + for (level = 0; level <= 50; level++) { + scan_nttrans(cli, op, level, fnum, dnum, fname); + } + + for (level = 0x100; level <= 0x130; level++) { + scan_nttrans(cli, op, level, fnum, dnum, fname); + } + + for (level = 1000; level < 1050; level++) { + scan_nttrans(cli, op, level, fnum, dnum, fname); + } + } + + printf("nttrans scan finished\n"); + return true; +} + + +/* scan for valid base SMB requests */ +bool torture_smb_scan(struct torture_context *torture) +{ + static struct smbcli_state *cli; + int op; + struct smbcli_request *req; + NTSTATUS status; + + for (op=0x0;op<=0xFF;op++) { + if (op == SMBreadbraw) continue; + + if (!torture_open_connection(&cli, torture, 0)) { + return false; + } + + req = smbcli_request_setup(cli->tree, op, 0, 0); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + break; + } + + usleep(10000); + smbcli_transport_process(cli->transport); + if (req->state > SMBCLI_REQUEST_RECV) { + status = smbcli_request_simple_recv(req); + printf("op=0x%x status=%s\n", op, nt_errstr(status)); + torture_close_connection(cli); + continue; + } + + sleep(1); + smbcli_transport_process(cli->transport); + if (req->state > SMBCLI_REQUEST_RECV) { + status = smbcli_request_simple_recv(req); + printf("op=0x%x status=%s\n", op, nt_errstr(status)); + } else { + printf("op=0x%x no reply\n", op); + smbcli_request_destroy(req); + continue; /* don't attempt close! */ + } + + torture_close_connection(cli); + } + + + printf("smb scan finished\n"); + return true; +} diff --git a/source4/torture/basic/secleak.c b/source4/torture/basic/secleak.c new file mode 100644 index 0000000..9db9f54 --- /dev/null +++ b/source4/torture/basic/secleak.c @@ -0,0 +1,77 @@ +/* + Unix SMB/CIFS implementation. + + find security related memory leaks + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/time.h" +#include "libcli/smb_composite/smb_composite.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" +#include "torture/basic/proto.h" + +static bool try_failed_login(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + struct smb_composite_sesssetup setup; + struct smbcli_session *session; + struct smbcli_session_options options; + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + session = smbcli_session_init(cli->transport, cli, false, options); + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + setup.in.credentials = cli_credentials_init(session); + setup.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + + cli_credentials_set_conf(setup.in.credentials, tctx->lp_ctx); + cli_credentials_set_domain(setup.in.credentials, "INVALID-DOMAIN", CRED_SPECIFIED); + cli_credentials_set_username(setup.in.credentials, "INVALID-USERNAME", CRED_SPECIFIED); + cli_credentials_set_password(setup.in.credentials, "INVALID-PASSWORD", CRED_SPECIFIED); + + status = smb_composite_sesssetup(session, &setup); + talloc_free(session); + if (NT_STATUS_IS_OK(status)) { + printf("Allowed session setup with invalid credentials?!\n"); + return false; + } + + return true; +} + +bool torture_sec_leak(struct torture_context *tctx, struct smbcli_state *cli) +{ + time_t t1 = time_mono(NULL); + int timelimit = torture_setting_int(tctx, "timelimit", 20); + + while (time_mono(NULL) < t1+timelimit) { + if (!try_failed_login(tctx, cli)) { + return false; + } + talloc_report(NULL, stdout); + } + + return true; +} diff --git a/source4/torture/basic/unlink.c b/source4/torture/basic/unlink.c new file mode 100644 index 0000000..dee71bd --- /dev/null +++ b/source4/torture/basic/unlink.c @@ -0,0 +1,91 @@ +/* + Unix SMB/CIFS implementation. + + unlink tester + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" + +#define BASEDIR "\\unlinktest" + +/* + This test checks that + + 1) the server does not allow an unlink on a file that is open +*/ +bool torture_unlinktest(struct torture_context *tctx, struct smbcli_state *cli) +{ + const char *fname = BASEDIR "\\unlink.tst"; + int fnum; + bool correct = true; + union smb_open io; + NTSTATUS status; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + talloc_asprintf(tctx, "Failed setting up %s", BASEDIR)); + + cli->session->pid = 1; + + torture_comment(tctx, "Opening a file\n"); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, "open of %s failed (%s)", fname, smbcli_errstr(cli->tree))); + + torture_comment(tctx, "Unlinking a open file\n"); + + torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlink(cli->tree, fname)), + "server allowed unlink on an open file"); + + correct = check_error(__location__, cli, ERRDOS, ERRbadshare, + NT_STATUS_SHARING_VIOLATION); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Testing unlink after ntcreatex with DELETE access\n"); + + io.ntcreatex.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + + status = smb_raw_open(cli->tree, cli, &io); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "failed to open %s", fname)); + + torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlink(cli->tree, fname)), + "server allowed unlink on an open file"); + + correct = check_error(__location__, cli, ERRDOS, ERRbadshare, + NT_STATUS_SHARING_VIOLATION); + + return correct; +} + + diff --git a/source4/torture/basic/utable.c b/source4/torture/basic/utable.c new file mode 100644 index 0000000..a3ddf1a --- /dev/null +++ b/source4/torture/basic/utable.c @@ -0,0 +1,202 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - unicode table dumper + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "param/param.h" +#include "torture/basic/proto.h" +#include "lib/util/sys_rw.h" + +bool torture_utable(struct torture_context *tctx, + struct smbcli_state *cli) +{ + char fname[256]; + const char *alt_name; + int fnum; + uint8_t c2[4]; + int c, fd; + size_t len; + int chars_allowed=0, alt_allowed=0; + uint8_t valid[0x10000]; + + torture_comment(tctx, "Generating valid character table\n"); + + memset(valid, 0, sizeof(valid)); + + torture_assert(tctx, torture_setup_dir(cli, "\\utable"), + "Setting up dir \\utable failed"); + + for (c=1; c < 0x10000; c++) { + char *p; + + SSVAL(c2, 0, c); + strncpy(fname, "\\utable\\x", sizeof(fname)-1); + p = fname+strlen(fname); + len = 0; + if (!convert_string(CH_UTF16, CH_UNIX, + c2, 2, + p, sizeof(fname)-strlen(fname), &len)) { + torture_comment(tctx, "convert_string failed [%s]\n", + fname); + continue; + } + + p[len] = 0; + strncat(fname,"_a_long_extension",sizeof(fname)-1); + + fnum = smbcli_open(cli->tree, fname, O_RDWR | O_CREAT | O_TRUNC, + DENY_NONE); + if (fnum == -1) continue; + + chars_allowed++; + + smbcli_qpathinfo_alt_name(cli->tree, fname, &alt_name); + + if (strncmp(alt_name, "X_A_L", 5) != 0) { + alt_allowed++; + valid[c] = 1; + torture_comment(tctx, "fname=[%s] alt_name=[%s]\n", fname, alt_name); + } + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + if (c % 100 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%d (%d/%d)\r", c, chars_allowed, alt_allowed); + fflush(stdout); + } + } + } + torture_comment(tctx, "%d (%d/%d)\n", c, chars_allowed, alt_allowed); + + smbcli_rmdir(cli->tree, "\\utable"); + + torture_comment(tctx, "%d chars allowed %d alt chars allowed\n", chars_allowed, alt_allowed); + + fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644); + torture_assert(tctx, fd != -1, + talloc_asprintf(tctx, + "Failed to create valid.dat - %s", strerror(errno))); + sys_write_v(fd, valid, 0x10000); + close(fd); + torture_comment(tctx, "wrote valid.dat\n"); + + return true; +} + + +static char *form_name(int c) +{ + static char fname[256]; + uint8_t c2[4]; + char *p; + size_t len = 0; + + strncpy(fname, "\\utable\\", sizeof(fname)-1); + p = fname+strlen(fname); + SSVAL(c2, 0, c); + + if (!convert_string(CH_UTF16, CH_UNIX, + c2, 2, + p, sizeof(fname)-strlen(fname), &len)) { + return NULL; + } + p[len] = 0; + return fname; +} + +bool torture_casetable(struct torture_context *tctx, + struct smbcli_state *cli) +{ + char *fname; + int fnum; + int c, i; +#define MAX_EQUIVALENCE 8 + codepoint_t equiv[0x10000][MAX_EQUIVALENCE]; + + torture_comment(tctx, "Determining upper/lower case table\n"); + + memset(equiv, 0, sizeof(equiv)); + + torture_assert(tctx, torture_setup_dir(cli, "\\utable"), + "Error setting up dir \\utable"); + + for (c=1; c < 0x10000; c++) { + size_t size; + + if (c == '.' || c == '\\') continue; + + torture_comment(tctx, "%04x (%c)\n", c, isprint(c)?c:'.'); + + fname = form_name(c); + if (fname == NULL) continue; + fnum = smbcli_nt_create_full(cli->tree, fname, 0, +#if 0 + SEC_RIGHT_MAXIMUM_ALLOWED, +#else + SEC_RIGHTS_FILE_ALL, +#endif + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum == -1) { + torture_comment(tctx, "Failed to create file with char %04x\n", c); + continue; + } + + size = 0; + + if (NT_STATUS_IS_ERR(smbcli_qfileinfo(cli->tree, fnum, NULL, &size, + NULL, NULL, NULL, NULL, NULL))) continue; + + if (size > 0) { + /* found a character equivalence! */ + int c2[MAX_EQUIVALENCE]; + + if (size/sizeof(int) >= MAX_EQUIVALENCE) { + torture_comment(tctx, "too many chars match?? size=%d c=0x%04x\n", + (int)size, c); + smbcli_close(cli->tree, fnum); + return false; + } + + smbcli_read(cli->tree, fnum, c2, 0, size); + torture_comment(tctx, "%04x: ", c); + equiv[c][0] = c; + for (i=0; itree, fnum, 0, &c, size, sizeof(c)); + smbcli_close(cli->tree, fnum); + } + + smbcli_unlink_wcard(cli->tree, "\\utable\\*"); + smbcli_rmdir(cli->tree, "\\utable"); + + return true; +} diff --git a/source4/torture/dfs/common.c b/source4/torture/dfs/common.c new file mode 100644 index 0000000..5772c0d --- /dev/null +++ b/source4/torture/dfs/common.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + test suite for various Domain DFS + Copyright (C) Matthieu Patou 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "../librpc/gen_ndr/ndr_dfsblobs.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" +#include "torture/torture.h" +#include "torture/dfs/proto.h" +#include "libcli/raw/raw_proto.h" + +NTSTATUS dfs_cli_do_call(struct smbcli_tree *tree, + struct dfs_GetDFSReferral *ref) +{ + NTSTATUS result; + enum ndr_err_code ndr_err; + uint16_t setup = TRANSACT2_GET_DFS_REFERRAL; + struct smb_trans2 trans; + + ZERO_STRUCT(trans); + trans.in.max_param = 0; + trans.in.max_data = 4096; + trans.in.max_setup = 0; + trans.in.flags = 0; + trans.in.timeout = 0; + trans.in.setup_count = 1; + trans.in.setup = &setup; + trans.in.trans_name = NULL; + + ndr_err = ndr_push_struct_blob(&trans.in.params, tree, + &ref->in.req, + (ndr_push_flags_fn_t)ndr_push_dfs_GetDFSReferral_in); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INTERNAL_ERROR; + } + trans.in.data = data_blob(NULL, 0); + + result = smb_raw_trans2(tree, tree, &trans); + + if (!NT_STATUS_IS_OK(result)) + return result; + + ndr_err = ndr_pull_struct_blob(&trans.out.data, tree, + ref->out.resp, + (ndr_pull_flags_fn_t)ndr_pull_dfs_referral_resp); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + return NT_STATUS_OK; +} + diff --git a/source4/torture/dfs/domaindfs.c b/source4/torture/dfs/domaindfs.c new file mode 100644 index 0000000..e9d9ce4 --- /dev/null +++ b/source4/torture/dfs/domaindfs.c @@ -0,0 +1,540 @@ +/* + Unix SMB/CIFS implementation. + test suite for various Domain DFS + Copyright (C) Matthieu Patou 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "../librpc/gen_ndr/ndr_dfsblobs.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" +#include "torture/torture.h" +#include "torture/dfs/proto.h" + +static bool test_getdomainreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r; + struct dfs_referral_resp resp; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + torture_assert_int_equal(tctx, resp.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp.nb_referrals != 0, 1, + "0 domains referrals returned"); + torture_assert_int_equal(tctx, resp.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp.referral_entries[1].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 1 got %d expected 3", + resp.referral_entries[1].version)); + torture_assert_int_equal(tctx, resp.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp.referral_entries[0].referral.v3.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp.referral_entries[0].referral.v3.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, + resp.referral_entries[0].referral.v3.referrals.r2.special_name[0] == '\\', + 1, + "domain didn't start with a \\"); + return true; +} + +static bool test_getdcreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + const char* str; + const char* str2; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + str = resp.referral_entries[0].referral.v3.referrals.r2.special_name; + if( strchr(str, '.') == NULL ) { + str = resp.referral_entries[1].referral.v3.referrals.r2.special_name; + } + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + + torture_assert_int_equal(tctx, resp2.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp2.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp2.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp2.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp2.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp2.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp2.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + str = strchr(resp2.referral_entries[0].referral.v3.referrals.r2.expanded_names[0], '.'); + str2 = resp2.referral_entries[0].referral.v3.referrals.r2.special_name; + if (str2[0] == '\\') { + str2++; + } + torture_assert_int_equal(tctx, strlen(str) >0, 1 ,"Length of domain too short"); + str++; + torture_assert_int_equal(tctx, strcmp(str,str2), 0, + talloc_asprintf(tctx, "Pb domain of the dc is not "\ + "the same as the requested: domain was = %s got =%s",str2 ,str)); + torture_assert_int_equal(tctx, + resp.referral_entries[0].referral.v3.referrals.r2.special_name[0] == '\\', + 1, + "dc name didn't start with a \\"); + + r3.in.req.max_referral_level = 3; + /* + * Windows 7 and at least windows 2008 server sends domain.fqdn instead of \domain.fqdn + * (as it is specified in the spec) + * Let's check that we are able to support it too + */ + r3.in.req.servername = str; + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp3.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp3.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + return true; +} + +static bool test_getdcreferral_netbios(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + const char* str; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + + str = resp.referral_entries[0].referral.v3.referrals.r2.special_name; + if( strchr(str, '.') != NULL ) { + str = resp.referral_entries[1].referral.v3.referrals.r2.special_name; + } + + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp2.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp2.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp2.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp2.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp2.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp2.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp2.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + torture_assert(tctx, strchr( + resp2.referral_entries[0].referral.v3.referrals.r2.expanded_names[0],'.') == NULL, + "referral contains dots it's not a netbios name"); + + r3.in.req.max_referral_level = 3; + /* + * Windows 7 and at least windows 2008 server sends domain.fqdn instead of \domain.fqdn + * (as it is specified in the spec) + * Let's check that we are able to support it too + */ + r3.in.req.servername = str + 1; + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp3.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp3.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + torture_assert(tctx, strchr( + resp3.referral_entries[0].referral.v3.referrals.r2.expanded_names[0],'.') == NULL, + "referral contains dots it's not a netbios name"); + return true; +} + +static bool test_getsysvolreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char* str; + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + str = resp.referral_entries[0].referral.v3.referrals.r2.special_name; + if( strchr(str, '.') == NULL ) { + str = resp.referral_entries[1].referral.v3.referrals.r2.special_name; + } + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + r3.in.req.max_referral_level = 3; + r3.in.req.servername = talloc_asprintf(tctx, "%s\\sysvol", str); + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get sysvol Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 2*strlen(r3.in.req.servername), + "Path consumed not equal to length of the request"); + torture_assert_int_equal(tctx, resp3.nb_referrals != 0, 1, + "We do not receive at least 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, DFS_HEADER_FLAG_STORAGE_SVR, + "Header flag different it's not a referral for a storage"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.entry_flags, + 0, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a non domain response and got %d", + resp3.referral_entries[0].referral.v3.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r1.DFS_path) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strstr(resp3.referral_entries[0].referral.v3.referrals.r1.DFS_path, + str+1) != NULL, 1, + talloc_asprintf(tctx, + "Wrong DFS_path %s unable to find substring %s in it", + resp3.referral_entries[0].referral.v3.referrals.r1.DFS_path, + str+1)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.referrals.r1.netw_address) > 0, + 1, + "Length of first referral is less than 0"); + torture_assert_int_equal(tctx, strstr(resp3.referral_entries[0].referral.v3.referrals.r1.netw_address, + str+1) != NULL, 1, + talloc_asprintf(tctx, + "Wrong DFS_path %s unable to find substring %s in it", + resp3.referral_entries[0].referral.v3.referrals.r1.netw_address, + str+1)); + /* + * Due to strange behavior with XP and level 4 + * we are obliged to degrade to level 3 ... + */ + r3.in.req.max_referral_level = 4; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get sysvol Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 4, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 4", + resp3.referral_entries[0].version)); + torture_assert(tctx, all_zero(resp3.referral_entries[0].referral.v3.service_site_guid.value, 16), + talloc_asprintf(tctx, + "Service_site_guid is not NULL as expected")); +#if 0 + /* Shouldn't be needed anymore*/ + r3.in.req.max_referral_level = 4; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get sysvol Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3 in degraded mode", + resp3.referral_entries[0].version)); + /* + * We do not support fallback indication for the moment + */ + torture_assert_int_equal(tctx, resp3.header_flags, + DFS_HEADER_FLAG_STORAGE_SVR | DFS_HEADER_FLAG_TARGET_BCK, + "Header flag different it's not a referral for a storage with fallback"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v4.entry_flags, + DFS_FLAG_REFERRAL_FIRST_TARGET_SET, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a non domain response and got %d", + resp3.referral_entries[0].referral.v4.entry_flags)); +#endif + return true; +} + +static bool test_unknowndomain(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2; + struct dfs_referral_resp resp, resp2; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = "foobar.none.net"; + r2.out.resp = &resp2; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r2), + NT_STATUS_INVALID_PARAMETER, + "Get DC Domain didn't return expected error code"); + + return true; +} + +static bool test_getsysvolplusreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char* str; + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = resp.referral_entries[0].referral.v3.referrals.r2.special_name; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + str = resp2.referral_entries[0].referral.v3.referrals.r2.special_name; + r3.in.req.max_referral_level = 3; + r3.in.req.servername = talloc_asprintf(tctx, "%s\\sysvol\\foo", str); + r3.out.resp = &resp3; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r3), + NT_STATUS_NOT_FOUND, + "Bad behavior with subtree sysvol referral"); + + return true; +} + +static bool test_low_referral_level(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r; + struct dfs_referral_resp resp; + + r.in.req.max_referral_level = 2; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r), + NT_STATUS_UNSUCCESSFUL, + "Unexpected STATUS for invalid referral request"); + + return true; +} + +NTSTATUS torture_dfs_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dfs"); + struct torture_suite *suite_basic = torture_suite_create(suite, "domain"); + + torture_suite_add_suite(suite, suite_basic); + + torture_suite_add_1smb_test(suite_basic, "domain referral", + test_getdomainreferral); + torture_suite_add_1smb_test(suite_basic, "dc referral", + test_getdcreferral); + torture_suite_add_1smb_test(suite_basic, "dc referral netbios", + test_getdcreferral_netbios); + + torture_suite_add_1smb_test(suite_basic, "sysvol referral", + test_getsysvolreferral); + + /* Non standard case */ + + torture_suite_add_1smb_test(suite_basic, "dc referral on unknown domain", + test_unknowndomain); + torture_suite_add_1smb_test(suite_basic, "sysvol with subtree referral", + test_getsysvolplusreferral); + torture_suite_add_1smb_test(suite_basic, "referral with a level 2", + test_low_referral_level); + + /* + * test with invalid level + * test with netbios + */ + + suite->description = talloc_strdup(suite, "DFS referrals calls"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/dns/dlz_bind9.c b/source4/torture/dns/dlz_bind9.c new file mode 100644 index 0000000..32725b7 --- /dev/null +++ b/source4/torture/dns/dlz_bind9.c @@ -0,0 +1,2514 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Bartlett 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "system/network.h" +#include "dns_server/dlz_minimal.h" +#include +#include +#include "lib/param/param.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/util.h" +#include "auth/session.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "system/network.h" +#include "dns_server/dnsserver_common.h" +#include "librpc/gen_ndr/ndr_dnsserver.h" +#include "librpc/gen_ndr/ndr_dnsserver_c.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_dnsp.h" + +#include "librpc/rpc/dcerpc.h" +#include "librpc/rpc/dcerpc_proto.h" + +/* Tests that configure multiple DLZs will use this. Increase to add stress. */ +#define NUM_DLZS_TO_CONFIGURE 4 + +struct torture_context *tctx_static; + +static void dlz_bind9_log_wrapper(int level, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); + +static void dlz_bind9_log_wrapper(int level, const char *fmt, ...) +{ + va_list ap; + char *msg; + va_start(ap, fmt); + msg = talloc_vasprintf(NULL, fmt, ap); + torture_comment(tctx_static, "%s\n", msg); + TALLOC_FREE(msg); + va_end(ap); +} + +static bool test_dlz_bind9_version(struct torture_context *tctx) +{ + unsigned int flags = 0; + torture_assert_int_equal(tctx, dlz_version(&flags), + DLZ_DLOPEN_VERSION, "got wrong DLZ version"); + return true; +} + +static char *dlz_bind9_binddns_dir(struct torture_context *tctx, + const char *file) +{ + return talloc_asprintf(tctx, + "ldb://%s/%s", + lpcfg_binddns_dir(tctx->lp_ctx), + file); +} + +static bool test_dlz_bind9_create(struct torture_context *tctx) +{ + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, NULL), ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + dlz_destroy(dbdata); + + return true; +} + +static bool calls_zone_hook = false; + +static isc_result_t dlz_bind9_writeable_zone_hook(dns_view_t *view, + dns_dlzdb_t *dlzdb, + const char *zone_name) +{ + struct torture_context *tctx = talloc_get_type((void *)view, struct torture_context); + struct ldb_context *samdb = NULL; + char *errstring = NULL; + int ret = samdb_connect_url( + tctx, + NULL, + tctx->lp_ctx, + system_session(tctx->lp_ctx), + 0, + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL, + &samdb, + &errstring); + struct ldb_message *msg; + const char *attrs[] = { + NULL + }; + if (ret != LDB_SUCCESS) { + torture_comment(tctx, "Failed to connect to samdb"); + return ISC_R_FAILURE; + } + + ret = dsdb_search_one(samdb, tctx, &msg, NULL, + LDB_SCOPE_SUBTREE, attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, + "(&(objectClass=dnsZone)(name=%s))", zone_name); + if (ret != LDB_SUCCESS) { + torture_comment(tctx, + "Failed to search for %s: %s", + zone_name, + ldb_errstring(samdb)); + return ISC_R_FAILURE; + } + talloc_free(msg); + + calls_zone_hook = true; + + return ISC_R_SUCCESS; +} + +static bool test_dlz_bind9_configure(struct torture_context *tctx) +{ + void *dbdata = NULL; + dns_dlzdb_t *dlzdb = NULL; + int ret; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + tctx_static = tctx; + ret = dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + NULL); + torture_assert_int_equal(tctx, + ret, + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + calls_zone_hook = false; + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, + dlzdb, + dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + dlz_destroy(dbdata); + + torture_assert_int_equal(tctx, calls_zone_hook, 1, "Hasn't called zone hook"); + + return true; +} + +static bool test_dlz_bind9_multiple_configure(struct torture_context *tctx) +{ + int i; + for(i = 0; i < NUM_DLZS_TO_CONFIGURE; i++){ + test_dlz_bind9_configure(tctx); + } + return true; +} + +static bool configure_multiple_dlzs(struct torture_context *tctx, + void **dbdata, int count) +{ + int i, res; + dns_dlzdb_t *dlzdb = NULL; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + + tctx_static = tctx; + for(i = 0; i < count; i++){ + res = dlz_create("samba_dlz", 3, argv, &(dbdata[i]), + "log", dlz_bind9_log_wrapper, + "writeable_zone", + dlz_bind9_writeable_zone_hook, NULL); + torture_assert_int_equal(tctx, res, ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + res = dlz_configure((void*)tctx, dlzdb, dbdata[i]); + torture_assert_int_equal(tctx, res, ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + } + + return true; +} + +static bool test_dlz_bind9_destroy_oldest_first(struct torture_context *tctx) +{ + void *dbdata[NUM_DLZS_TO_CONFIGURE]; + int i; + bool ret = configure_multiple_dlzs(tctx, + dbdata, + NUM_DLZS_TO_CONFIGURE); + if (ret == false) { + /* failure: has already been printed */ + return false; + } + + /* Reload faults are reported to happen on the first destroy */ + dlz_destroy(dbdata[0]); + + for(i = 1; i < NUM_DLZS_TO_CONFIGURE; i++){ + dlz_destroy(dbdata[i]); + } + + return true; +} + +static bool test_dlz_bind9_destroy_newest_first(struct torture_context *tctx) +{ + void *dbdata[NUM_DLZS_TO_CONFIGURE]; + int i; + bool ret = configure_multiple_dlzs(tctx, + dbdata, + NUM_DLZS_TO_CONFIGURE); + if (ret == false) { + /* failure: has already been printed */ + return false; + } + + for(i = NUM_DLZS_TO_CONFIGURE - 1; i >= 0; i--) { + dlz_destroy(dbdata[i]); + } + + return true; +} + +/* + * Test that a ticket obtained for the DNS service will be accepted on the Samba DLZ side + * + */ +static bool test_dlz_bind9_gensec(struct torture_context *tctx, const char *mech) +{ + NTSTATUS status; + dns_dlzdb_t *dlzdb = NULL; + + struct gensec_security *gensec_client_context; + + DATA_BLOB client_to_server, server_to_client; + + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, + dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + /* + * dlz_bind9 use the special dns/host.domain account + */ + status = gensec_set_target_hostname(gensec_client_context, + talloc_asprintf(tctx, + "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx))); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + + status = gensec_set_target_service(gensec_client_context, "dns"); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_service failed"); + + status = gensec_set_credentials(gensec_client_context, + samba_cmdline_get_creds()); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, mech); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + server_to_client = data_blob(NULL, 0); + + /* Do one step of the client-server update dance */ + status = gensec_update(gensec_client_context, tctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + torture_assert_int_equal(tctx, dlz_ssumatch( + cli_credentials_get_username( + samba_cmdline_get_creds()), + lpcfg_dnsdomain(tctx->lp_ctx), + "127.0.0.1", "type", "key", + client_to_server.length, + client_to_server.data, + dbdata), + ISC_TRUE, + "Failed to check key for update rights samba_dlz"); + + dlz_destroy(dbdata); + + return true; +} + +static bool test_dlz_bind9_gssapi(struct torture_context *tctx) +{ + return test_dlz_bind9_gensec(tctx, "GSSAPI"); +} + +static bool test_dlz_bind9_spnego(struct torture_context *tctx) +{ + return test_dlz_bind9_gensec(tctx, "GSS-SPNEGO"); +} + +struct test_expected_record { + const char *name; + const char *type; + const char *data; + int ttl; + bool printed; + const char *rdata; +}; + +struct test_expected_rr { + struct torture_context *tctx; + const char *query_name; + size_t num_records; + struct test_expected_record *records; + size_t num_rr; +}; + +static bool dlz_bind9_putnamedrr_torture_hook(struct test_expected_rr *expected, + const char *name, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + size_t i; + + torture_assert(expected->tctx, name != NULL, + talloc_asprintf(expected->tctx, + "Got unnamed record type[%s] data[%s]\n", + type, data)); + + expected->num_rr++; + torture_comment(expected->tctx, "%u: name[%s] type[%s] ttl[%u] data[%s]\n", + (unsigned)expected->num_rr, name, type, (unsigned)ttl, data); + + for (i = 0; i < expected->num_records; i++) { + if (expected->records[i].name != NULL) { + if (strcmp(name, expected->records[i].name) != 0) { + continue; + } + } + + if (strcmp(type, expected->records[i].type) != 0) { + continue; + } + + if (expected->records[i].data != NULL) { + /* + * For most types the data will have been reformatted + * or normalised, so we need to do approximately the + * same to compare. + */ + const char *data2 = expected->records[i].data; + if (strcmp(type, "aaaa") == 0) { + struct in6_addr adr1; + struct in6_addr adr2; + int ret; + ret = inet_pton(AF_INET6, data, &adr1); + if (ret != 1) { + continue; + } + ret = inet_pton(AF_INET6, data2, &adr2); + if (ret != 1) { + continue; + } + if (memcmp(&adr1, &adr2, sizeof(adr1)) != 0) { + continue; + } + } else if (strcmp(type, "cname") == 0 || + strcmp(type, "ptr") == 0 || + strcmp(type, "ns") == 0) { + if (!samba_dns_name_equal(data, data2)) { + continue; + } + } else if (strcmp(type, "mx") == 0) { + /* + * samba_dns_name_equal works for MX records + * because the space in "10 example.com." is + * theoretically OK as a DNS character. And we + * need it because dlz will add the trailing + * dot. + */ + if (!samba_dns_name_equal(data, data2)) { + continue; + } + } else if (strcmp(data, data2) != 0) { + /* default, works for A records */ + continue; + } + } + + torture_assert_int_equal(expected->tctx, ttl, + expected->records[i].ttl, + talloc_asprintf(expected->tctx, + "TTL did not match expectations for type %s", + type)); + + expected->records[i].printed = true; + } + + return true; +} + +/* + * Lookups in these tests end up coming round to run this function. + */ +static isc_result_t dlz_bind9_putrr_hook(dns_sdlzlookup_t *lookup, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + struct test_expected_rr *expected = + talloc_get_type_abort(lookup, struct test_expected_rr); + bool ok; + + ok = dlz_bind9_putnamedrr_torture_hook(expected, expected->query_name, + type, ttl, data); + if (!ok) { + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +static isc_result_t dlz_bind9_putnamedrr_hook(dns_sdlzallnodes_t *allnodes, + const char *name, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + struct test_expected_rr *expected = + talloc_get_type_abort(allnodes, struct test_expected_rr); + bool ok; + + ok = dlz_bind9_putnamedrr_torture_hook(expected, name, type, ttl, data); + if (!ok) { + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +/* + * Tests some lookups + */ +static bool test_dlz_bind9_lookup(struct torture_context *tctx) +{ + size_t i; + void *dbdata = NULL; + dns_clientinfomethods_t *methods = NULL; + dns_clientinfo_t *clientinfo = NULL; + dns_dlzdb_t *dlzdb = NULL; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + struct test_expected_rr *expected2 = NULL; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, + dlz_configure((void*)tctx, dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->query_name = "@"; + + expected1->num_records = 4; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = expected1->query_name; + expected1->records[0].type = "soa"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = talloc_asprintf(expected1->records, + "%s.%s. hostmaster.%s. 1 900 600 86400 3600", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[0].data != NULL, "talloc failed"); + + expected1->records[1].name = expected1->query_name; + expected1->records[1].type = "ns"; + expected1->records[1].ttl = 900; + expected1->records[1].data = talloc_asprintf(expected1->records, "%s.%s.", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[1].data != NULL, "talloc failed"); + + expected1->records[2].name = expected1->query_name; + expected1->records[2].type = "aaaa"; + expected1->records[2].ttl = 900; + + expected1->records[3].name = expected1->query_name; + expected1->records[3].type = "a"; + expected1->records[3].ttl = 900; + + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Failed to lookup @"); + for (i = 0; i < expected1->num_records; i++) { + torture_assert(tctx, expected1->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run for type %s", + expected1->records[i].type)); + } + torture_assert_int_equal(tctx, expected1->num_rr, + expected1->num_records, + "Got too much data"); + + expected2 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected2 != NULL, "talloc failed"); + expected2->tctx = tctx; + + expected2->query_name = torture_setting_string(tctx, "host", NULL); + torture_assert(tctx, expected2->query_name != NULL, "unknown host"); + + expected2->num_records = 2; + expected2->records = talloc_zero_array(expected2, + struct test_expected_record, + expected2->num_records); + torture_assert(tctx, expected2->records != NULL, "talloc failed"); + + expected2->records[0].name = expected2->query_name; + expected2->records[0].type = "aaaa"; + expected2->records[0].ttl = 900; + + expected2->records[1].name = expected2->query_name; + expected2->records[1].type = "a"; + expected2->records[1].ttl = 900; + + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected2->query_name, dbdata, + (dns_sdlzlookup_t *)expected2, + methods, clientinfo), + ISC_R_SUCCESS, + "Failed to lookup hostname"); + for (i = 0; i < expected2->num_records; i++) { + torture_assert(tctx, expected2->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected2->records[i].name, + expected2->records[i].type)); + } + torture_assert_int_equal(tctx, expected2->num_rr, + expected2->num_records, + "Got too much data"); + + dlz_destroy(dbdata); + + return true; +} + +/* + * Test some zone dumps + */ +static bool test_dlz_bind9_zonedump(struct torture_context *tctx) +{ + size_t i; + void *dbdata = NULL; + dns_dlzdb_t *dlzdb = NULL; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->num_records = 7; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = talloc_asprintf(expected1->records, + "%s.", lpcfg_dnsdomain(tctx->lp_ctx)); + expected1->records[0].type = "soa"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = talloc_asprintf(expected1->records, + "%s.%s. hostmaster.%s. 1 900 600 86400 3600", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[0].data != NULL, "talloc failed"); + + expected1->records[1].name = talloc_asprintf(expected1->records, + "%s.", lpcfg_dnsdomain(tctx->lp_ctx)); + expected1->records[1].type = "ns"; + expected1->records[1].ttl = 900; + expected1->records[1].data = talloc_asprintf(expected1->records, "%s.%s.", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[1].data != NULL, "talloc failed"); + + expected1->records[2].name = talloc_asprintf(expected1->records, + "%s.", lpcfg_dnsdomain(tctx->lp_ctx)); + expected1->records[2].type = "aaaa"; + expected1->records[2].ttl = 900; + + expected1->records[3].name = talloc_asprintf(expected1->records, + "%s.", lpcfg_dnsdomain(tctx->lp_ctx)); + expected1->records[3].type = "a"; + expected1->records[3].ttl = 900; + + expected1->records[4].name = talloc_asprintf(expected1->records, "%s.%s.", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[4].name != NULL, "unknown host"); + expected1->records[4].type = "aaaa"; + expected1->records[4].ttl = 900; + + expected1->records[5].name = talloc_asprintf(expected1->records, "%s.%s.", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[5].name != NULL, "unknown host"); + expected1->records[5].type = "a"; + expected1->records[5].ttl = 900; + + /* + * We expect multiple srv records + */ + expected1->records[6].name = NULL; + expected1->records[6].type = "srv"; + expected1->records[6].ttl = 900; + + torture_assert_int_equal(tctx, dlz_allnodes(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, (dns_sdlzallnodes_t *)expected1), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + for (i = 0; i < expected1->num_records; i++) { + torture_assert(tctx, expected1->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[i].name, + expected1->records[i].type)); + } + torture_assert_int_equal(tctx, expected1->num_rr, 24, + "Got wrong record count"); + + dlz_destroy(dbdata); + + return true; +} + +/* + * Test some updates + */ +static bool test_dlz_bind9_update01(struct torture_context *tctx) +{ + NTSTATUS status; + struct gensec_security *gensec_client_context; + DATA_BLOB client_to_server, server_to_client; + void *dbdata = NULL; + dns_dlzdb_t *dlzdb = NULL; + void *version = NULL; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + char *name = NULL; + char *data0 = NULL; + char *data1 = NULL; + char *data2 = NULL; + bool ret = false; + dns_clientinfomethods_t *methods = NULL; + dns_clientinfo_t *clientinfo = NULL; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->query_name = __func__; + + name = talloc_asprintf(expected1, "%s.%s", + expected1->query_name, + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, name != NULL, "talloc failed"); + + expected1->num_records = 2; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = expected1->query_name; + expected1->records[0].type = "a"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = "127.1.2.3"; + expected1->records[0].printed = false; + + data0 = talloc_asprintf(expected1, + "%s.\t" "%u\t" "%s\t" "%s\t" "%s", + name, + (unsigned)expected1->records[0].ttl, + "in", + expected1->records[0].type, + expected1->records[0].data); + torture_assert(tctx, data0 != NULL, "talloc failed"); + + expected1->records[1].name = expected1->query_name; + expected1->records[1].type = "a"; + expected1->records[1].ttl = 3600; + expected1->records[1].data = "127.3.2.1"; + expected1->records[1].printed = false; + + data1 = talloc_asprintf(expected1, + "%s.\t" "%u\t" "%s\t" "%s\t" "%s", + name, + (unsigned)expected1->records[1].ttl, + "in", + expected1->records[1].type, + expected1->records[1].data); + torture_assert(tctx, data1 != NULL, "talloc failed"); + + data2 = talloc_asprintf(expected1, + "%s.\t" "0\t" "in\t" "a\t" "127.3.3.3", + name); + torture_assert(tctx, data2 != NULL, "talloc failed"); + + /* + * Prepare session info + */ + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + /* + * dlz_bind9 use the special dns/host.domain account + */ + status = gensec_set_target_hostname(gensec_client_context, + talloc_asprintf(tctx, + "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx))); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + + status = gensec_set_target_service(gensec_client_context, "dns"); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_service failed"); + + status = gensec_set_credentials(gensec_client_context, + samba_cmdline_get_creds()); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSS-SPNEGO"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + server_to_client = data_blob(NULL, 0); + + /* Do one step of the client-server update dance */ + status = gensec_update(gensec_client_context, tctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + torture_assert_int_equal(tctx, dlz_ssumatch( + cli_credentials_get_username( + samba_cmdline_get_creds()), + name, + "127.0.0.1", + expected1->records[0].type, + "key", + client_to_server.length, + client_to_server.data, + dbdata), + ISC_TRUE, + "Failed to check key for update rights samba_dlz"); + + /* + * We test the following: + * + * 1. lookup the records => NOT_FOUND + * 2. delete all records => NOT_FOUND + * 3. delete 1st record => NOT_FOUND + * 4. create 1st record => SUCCESS + * 5. lookup the records => found 1st + * 6. create 2nd record => SUCCESS + * 7. lookup the records => found 1st and 2nd + * 8. delete unknown record => NOT_FOUND + * 9. lookup the records => found 1st and 2nd + * 10. delete 1st record => SUCCESS + * 11. lookup the records => found 2nd + * 12. delete 2nd record => SUCCESS + * 13. lookup the records => NOT_FOUND + * 14. create 1st record => SUCCESS + * 15. lookup the records => found 1st + * 16. create 2nd record => SUCCESS + * 17. lookup the records => found 1st and 2nd + * 18. update 1st record => SUCCESS + * 19. lookup the records => found 1st and 2nd + * 20. delete all unknown type records => NOT_FOUND + * 21. lookup the records => found 1st and 2nd + * 22. delete all records => SUCCESS + * 23. lookup the records => NOT_FOUND + */ + + /* Step 1. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + /* Step 2. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, + expected1->records[0].type, + dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] type[%s]\n", + name, expected1->records[0].type)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 3. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data0, dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 4. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 5. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 6. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 7. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 8. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data2, dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] data[%s]\n", + name, data2)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 9. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 10. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 11. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 12. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 13. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + /* Step 14. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 15. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 16. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 17. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 18. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to update name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 19. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 20. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, "txt", dbdata, version), + ISC_R_FAILURE, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] type[%s]\n", + name, "txt")); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 21. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 22. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, + expected1->records[0].type, + dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] type[%s]\n", + name, expected1->records[0].type)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 23. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1, + methods, clientinfo), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + dlz_destroy(dbdata); + + return true; + +cancel_version: + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + return ret; +} + +/* + * Test zone transfer requests restrictions + * + * 1: test that zone transfer is denied by default + * 2: with an authorized list of IPs set in smb.conf, test that zone transfer + * is accepted only for selected IPs. + */ +static bool test_dlz_bind9_allowzonexfr(struct torture_context *tctx) +{ + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + isc_result_t ret; + dns_dlzdb_t *dlzdb = NULL; + bool ok; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + /* Ask for zone transfer with no specific config => expect denied */ + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "127.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer accepted with default settings"); + + /* Ask for zone transfer with authorizations set */ + ok = lpcfg_set_option(tctx->lp_ctx, "dns zone transfer clients allow=127.0.0.1,1234:5678::1,192.168.0."); + torture_assert(tctx, ok, "Failed to set dns zone transfer clients allow option."); + + ok = lpcfg_set_option(tctx->lp_ctx, "dns zone transfer clients deny=192.168.0.2"); + torture_assert(tctx, ok, "Failed to set dns zone transfer clients deny option."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "127.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for authorized IPv4 address"); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "1234:5678::1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for authorized IPv6 address."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "10.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer accepted for unauthorized IP"); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "192.168.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for address in authorized IPv4 subnet."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "192.168.0.2"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer allowed for denied client."); + + dlz_destroy(dbdata); + return true; +} + + +static int init_dlz(struct torture_context *tctx, + void **dbdata) +{ + isc_result_t ret; + const char *argv[] = { + "samba_dlz", + "-H", + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + + ret = dlz_create("samba_dlz", 3, argv, dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL); + + torture_assert_int_equal(tctx, + ret, + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + ret = dlz_configure((void*)tctx, NULL, *dbdata); + torture_assert_int_equal(tctx, + ret, + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + return true; +} + + +static int init_gensec(struct torture_context *tctx, + struct gensec_security **gensec_client_context) +{ + NTSTATUS status; + /* + * Prepare session info + */ + status = gensec_client_start(tctx, gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, + "gensec_client_start (client) failed"); + + /* + * dlz_bind9 use the special dns/host.domain account + */ + status = gensec_set_target_hostname(*gensec_client_context, + talloc_asprintf(tctx, + "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx))); + torture_assert_ntstatus_ok(tctx, status, + "gensec_set_target_hostname (client) failed"); + + status = gensec_set_target_service(*gensec_client_context, "dns"); + torture_assert_ntstatus_ok(tctx, status, + "gensec_set_target_service failed"); + + status = gensec_set_credentials(*gensec_client_context, + samba_cmdline_get_creds()); + torture_assert_ntstatus_ok(tctx, status, + "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(*gensec_client_context, + "GSS-SPNEGO"); + torture_assert_ntstatus_ok(tctx, status, + "gensec_start_mech_by_sasl_name (client) failed"); + + + return true; +} + + + +static bool expected_record(TALLOC_CTX *mem_ctx, + struct test_expected_record *r, + const char *name, + const char *type, + const char *data) +{ + unsigned int ttl = 3600; + const char *rdata = talloc_asprintf( + mem_ctx, + "%s.\t" "%u\t" "in\t" "%s\t" "%s", + name, ttl, type, data); + if (rdata == NULL) { + return false; + } + + *r = (struct test_expected_record){ + .name = name, + .type = type, + .data = data, + .ttl = ttl, + .printed = false, + .rdata = rdata + }; + return true; +} + + +struct dlz_test_handle { + struct dcerpc_pipe *p; +}; + + +static bool set_zone_aging(struct torture_context *tctx, + const char *zone, + int value) +{ + int ret; + char *cmd = talloc_asprintf(tctx, + "bin/samba-tool dns zoneoptions " + "$SERVER %s -U$USERNAME%%$PASSWORD " + "--aging %d", zone, value); + + if (cmd == NULL) { + return false; + } + + ret = system(cmd); + if (ret != 0) { + TALLOC_FREE(cmd); + return false; + } + TALLOC_FREE(cmd); + return true; +} + + +static struct ldb_context* get_samdb(struct torture_context *tctx) +{ + struct ldb_context *samdb = NULL; + char *errstring; + int ret = samdb_connect_url( + tctx, + NULL, + tctx->lp_ctx, + system_session(tctx->lp_ctx), + 0, + dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL, + &samdb, + &errstring); + if (ret != LDB_SUCCESS) { + return NULL; + } + return samdb; +} + + +static void print_node_records(struct torture_context *tctx, + struct ldb_context *samdb, + struct ldb_dn *node_dn, + const char *msg) +{ + int ret; + struct ldb_result *result = NULL; + struct dnsp_DnssrvRpcRecord rec; + struct ldb_message_element *el = NULL; + size_t i; + + if (msg != NULL) { + torture_comment(tctx, + "\033[1;32m%s\033[0m\n", + msg); + } + + ret = dsdb_search(samdb, tctx, &result, node_dn, + LDB_SCOPE_SUBTREE, NULL, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_comment(tctx, + "Failed to find node: %s", + ldb_errstring(samdb)); + } + + el = ldb_msg_find_element(result->msgs[0], "dnsRecord"); + + for (i = 0; i < el->num_values; i++) { + ret = ndr_pull_struct_blob( + &(el->values[i]), + result, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ret)) { + DBG_ERR("Failed to pull dns rec blob [%zu].\n", + i); + TALLOC_FREE(result); + } + torture_comment(tctx, "record[%zu]:\n", i); + torture_comment(tctx, "type: %d\n", rec.wType); + torture_comment(tctx, "timestamp: %u\n", rec.dwTimeStamp); + torture_comment(tctx, "%s\n", + NDR_PRINT_STRUCT_STRING(result, + dnsp_DnssrvRpcRecord, + &rec)); + } +} + + + +/* + * Test some MORE updates, this time focussing on more record types and aging. + */ +static bool test_dlz_bind9_aging(struct torture_context *tctx) +{ + struct gensec_security *gensec_client_context = NULL; + DATA_BLOB client_to_server, server_to_client; + NTSTATUS status; + void *dbdata = NULL; + void *version = NULL; + struct test_expected_rr *testdata = NULL; + bool ok = false; + struct ldb_context *samdb = NULL; + isc_result_t ret; + size_t i, j; + const char *domain = lpcfg_dnsdomain(tctx->lp_ctx); + struct ldb_dn *domain_dn = NULL; + struct ldb_dn *node_dn = NULL; + struct ldb_result *result = NULL; + uint32_t dns_timestamp_before; + uint32_t dns_timestamp_after; + const char *name = NULL; + const char *attrs[] = {"dnsrecord", NULL}; + const char *node_dn_str = NULL; + struct ldb_message_element *el = NULL; + struct ldb_message *msg = NULL; + + tctx_static = tctx; + + /* Step 0. set things up */ + + ok = init_dlz(tctx, &dbdata); + if (! ok) { + torture_fail(tctx, "Failed to init_dlz"); + } + ok = init_gensec(tctx, &gensec_client_context); + if (! ok) { + torture_fail(tctx, "Failed to init_gensec"); + } + + samdb = get_samdb(tctx); + if (samdb == NULL) { + torture_fail(tctx, "Failed to connect to samdb"); + } + + domain_dn = ldb_get_default_basedn(samdb); + testdata = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, testdata != NULL, "talloc failed"); + testdata->tctx = tctx; + + testdata->query_name = __func__; + + name = talloc_asprintf(testdata, "%s.%s", + testdata->query_name, + domain); + torture_assert(tctx, name != NULL, "talloc failed"); + + testdata->num_records = 6; + testdata->records = talloc_zero_array(testdata, + struct test_expected_record, + testdata->num_records); + torture_assert(tctx, testdata->records != NULL, "talloc failed"); + + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[0], + testdata->query_name, + "aaaa", + "::1"), + "failed to add record"); + + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[1], + testdata->query_name, + "a", + "127.11.12.13"), + "failed to add record"); + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[2], + testdata->query_name, + "a", + "127.11.12.14"), + "failed to add record"); + + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[3], + testdata->query_name, + "ptr", + "samba.example.com"), + "failed to add record"); + + /* + * NOTE: Here we add the MX record with the priority before the name, + * rather than the other way around which you are more likely to see + * ("samba.example.com 11" e.g. in samba-tool dns), because this is + * how it goes in BIND9 configuration. + */ + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[4], + testdata->query_name, + "mx", + "11 samba.example.com."), + "failed to add record"); + + torture_assert(tctx, + expected_record(testdata->records, + &testdata->records[5], + testdata->query_name, + "cname", + "samba.example.com"), + "failed to add record"); + + + server_to_client = data_blob(NULL, 0); + + /* Do one step of the client-server update dance */ + status = gensec_update(gensec_client_context, tctx, server_to_client, + &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, + "gensec_update (client) failed"); + } + + torture_assert_int_equal(tctx, dlz_ssumatch( + cli_credentials_get_username( + samba_cmdline_get_creds()), + domain, + "127.0.0.1", + testdata->records[0].type, + "key", + client_to_server.length, + client_to_server.data, + dbdata), + ISC_TRUE, + "Failed to check key for update rights samba_dlz"); + + /* remember the DN for use below */ + node_dn = ldb_dn_copy(testdata, domain_dn); + if (node_dn == NULL) { + torture_fail(tctx, "Failed to make node dn"); + } + + ok = ldb_dn_add_child_fmt( + node_dn, + "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", + testdata->query_name, + domain); + if (! ok) { + torture_fail(tctx, "Failed to make node dn"); + } + node_dn_str = ldb_dn_get_linearized(node_dn); + if (node_dn_str == NULL) { + torture_fail(tctx, "Failed to linearise node dn"); + } + + /* LOOK: we are chopping off the last one (the CNAME) for now */ + testdata->num_records = 5; + + /* + * We test the following: + * + * Step 1. Ensure we are starting with an empty node. + * Step 2. Add all the records (with aging off). + * Step 3. Check the timestamps are now-ish. + * Step 4. Add all the records AGAIN. + * Step 5: Turn aging on. + * Step 6. Add all the records again. + * Step 7. Check the timestamps are still now-ish. + * Step 8. Wind back the timestamps in the database. + * Step 9. Do another update, changing some timestamps + * Step 10. Check that the timestamps are right. + * Step 11. Set one record to be static. + * Step 12. Do updates on some records, zeroing their timestamps + * Step 13. Check that the record timeouts are *mostly* zero. + * Step 14. Turn aging off + * Step 15. Update, setting timestamps to zero + * Step 16. Check that the timestamps are all zero. + * Step 17. Reset to non-zero via ldb, with aging still off. + * Step 18. Update with aging off. Nothing should change. + * Step 19. Check that the timestamps didn't change. + * Step 20. Delete all the records, 1 by 1. + */ + + + /* + * Step 1. Ensure we are starting with an empty node. + */ + torture_comment(tctx, "step 1: %s records are not there\n", + testdata->query_name); + testdata->num_rr = 0; + torture_assert_int_equal(tctx, dlz_lookup(domain, + testdata->query_name, + dbdata, + (dns_sdlzlookup_t *)testdata, + NULL, NULL), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, testdata->num_rr, 0, + "Got records when there should be none"); + + + dns_timestamp_before = unix_to_dns_timestamp(time(NULL)); + + /* + * Step 2. Add all the records (with aging off). + * After adding each one, expect to find it and earlier ones. + */ + torture_comment(tctx, + "step 2: add %zu records\n", + testdata->num_records); + + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + torture_assert_int_equal_goto( + tctx, ret, ISC_R_SUCCESS, ok, + cancel_version, + talloc_asprintf(tctx, + "Failed to add record %zu «%s»\n", + i, r.rdata)); + + dlz_closeversion(domain, true, dbdata, &version); + + testdata->num_rr = 0; + + ret = dlz_lookup(domain, testdata->query_name, dbdata, + (dns_sdlzlookup_t *)testdata, NULL, NULL); + + torture_assert_int_equal(tctx, ret, + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert_int_equal(tctx, testdata->num_rr, i + 1, + "Got wrong record count"); + + for (j = 0; j < testdata->num_records; j++) { + struct test_expected_record *r2 = &testdata->records[j]; + if (j <= i) { + torture_assertf( + tctx, + r2->printed, + "putrr callback not run on %s «%s»", + r2->type, r2->name); + } else { + torture_assertf( + tctx, + ! r2->printed, + "putrr callback should not see %s «%s»", + r2->type, r2->name); + } + r2->printed = false; + } + } + + dns_timestamp_after = unix_to_dns_timestamp(time(NULL)); + /* + * Step 3. Check the timestamps are now-ish. + * + * Those records should have DNS timestamps between + * dns_timestamp_before and dns_timestamp_after (the resolution is + * hourly, so probably both are equal). + */ + ret = dsdb_search(samdb, tctx, &result, node_dn, + LDB_SCOPE_SUBTREE, NULL, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + torture_assert_int_equal(tctx, result->count, 1, + "Should be one node"); + + el = ldb_msg_find_element(result->msgs[0], "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert(tctx, dns_timestamp_before <= dns_timestamp_after, "<"); + torture_assert_int_equal(tctx, el->num_values, testdata->num_records, + "num_values != num_records"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + result, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ret)) { + DBG_ERR("Failed to pull dns rec blob [%zu].\n", + i); + TALLOC_FREE(result); + torture_fail(tctx, "Failed to pull dns rec blob"); + } + torture_comment(tctx, "record[%zu]:\n", i); + torture_comment(tctx, "type: %d\n", rec.wType); + torture_comment(tctx, "timestamp: %u\n", rec.dwTimeStamp); + torture_comment(tctx, "%s\n", + NDR_PRINT_STRUCT_STRING(result, + dnsp_DnssrvRpcRecord, + &rec)); + + torture_assert(tctx, rec.dwTimeStamp >= dns_timestamp_before, + "timestamp < dns_timestamp_before"); + torture_assert(tctx, rec.dwTimeStamp <= dns_timestamp_after, + "timestamp > dns_timestamp_after"); + } + + talloc_free(result); + + /* + * Step 4. Add all the records AGAIN. + * + * After adding each one, we expect no change in the number or nature + * of records. + */ + torture_comment(tctx, "step 4: add the records again\n"); + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + torture_assert_int_equal_goto( + tctx, ret, ISC_R_SUCCESS, ok, + cancel_version, + talloc_asprintf(tctx, + "Failed to add record %zu «%s»\n", + i, r.rdata)); + + dlz_closeversion(domain, true, dbdata, &version); + + testdata->num_rr = 0; + + ret = dlz_lookup(domain, testdata->query_name, dbdata, + (dns_sdlzlookup_t *)testdata, NULL, NULL); + + torture_assert_int_equal(tctx, ret, + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert_int_equal(tctx, + testdata->num_rr, + testdata->num_records, + "Got wrong record count"); + + for (j = 0; j <= i; j++) { + /* these ones are printed again. */ + struct test_expected_record *r2 = &testdata->records[j]; + torture_assert( + tctx, + r2->printed, + talloc_asprintf( + tctx, + "putrr callback not run on %s «%s»", + r2->type, r2->name)); + r2->printed = false; + } + } + + print_node_records(tctx, samdb, node_dn, "after adding again"); + + + /* + * Step 5: Turn aging on. + */ + torture_comment(tctx, "step 5: turn aging on\n"); + ok = set_zone_aging(tctx, domain, 1); + torture_assert(tctx, ok, "failed to enable aging"); + + print_node_records(tctx, samdb, node_dn, "aging on"); + + /* + * Step 6. Add all the records again. + * + * We expect no change in the number or nature of records, even with + * aging on, because the default noRefreshInterval is 7 days (also, + * there should be no change because almost no time has passed). + */ + torture_comment(tctx, "step 6: add records again\n"); + + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(domain, r.rdata, dbdata, version); + torture_assert_int_equal_goto( + tctx, ret, ISC_R_SUCCESS, ok, + cancel_version, + talloc_asprintf(tctx, + "Failed to add record %zu «%s»\n", + i, r.rdata)); + + dlz_closeversion(domain, true, dbdata, &version); + } + + print_node_records(tctx, samdb, node_dn, "add again"); + + + /* + * Step 7. Check the timestamps are still now-ish. + * + */ + ret = dsdb_search(samdb, tctx, &result, node_dn, + LDB_SCOPE_SUBTREE, NULL, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + torture_assert_int_equal(tctx, result->count, 1, + "Should be one node"); + + el = ldb_msg_find_element(result->msgs[0], "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert(tctx, dns_timestamp_before <= dns_timestamp_after, "<"); + torture_assert_int_equal(tctx, el->num_values, testdata->num_records, + "num_values != num_records"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + result, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ret)) { + DBG_ERR("Failed to pull dns rec blob [%zu].\n", + i); + TALLOC_FREE(result); + torture_fail(tctx, "Failed to pull dns rec blob"); + } + torture_comment(tctx, "record[%zu]:\n", i); + torture_comment(tctx, "type: %d\n", rec.wType); + torture_comment(tctx, "timestamp: %u\n", rec.dwTimeStamp); + torture_comment(tctx, "%s\n", + NDR_PRINT_STRUCT_STRING(result, + dnsp_DnssrvRpcRecord, + &rec)); + + torture_assert(tctx, rec.dwTimeStamp >= dns_timestamp_before, + "timestamp < dns_timestamp_before"); + torture_assert(tctx, rec.dwTimeStamp <= dns_timestamp_after, + "timestamp > dns_timestamp_after"); + } + + talloc_free(result); + + /* + * Step 8. Wind back the timestamps in the database. + * + * We use a different number of days for each record, so that some + * should be refreshed, and some shouldn't. + */ + torture_comment(tctx, "step 8: alter timestamps\n"); + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert_int_equal(tctx, el->num_values, + testdata->num_records, + "num_values != num_records"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to pull record"); + + rec.dwTimeStamp = dns_timestamp_after + 3 - 24 * (i + 5); + + ret = ndr_push_struct_blob( + &el->values[i], + msg, + &rec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to PUSH record"); + } + el->flags = LDB_FLAG_MOD_REPLACE; + + ret = ldb_modify(samdb, msg); + torture_assert_int_equal(tctx, ret, 0, "failed to ldb_modify"); + print_node_records(tctx, samdb, node_dn, "after ldb_modify"); + + + /* + * Step 9. Do another update, changing some timestamps + */ + + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + dlz_closeversion(domain, ret == ISC_R_SUCCESS, dbdata, + &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to update record\n"); + } + print_node_records(tctx, samdb, node_dn, "after update"); + + /* + * Step 10. Check that the timestamps are right. + * + * The formula was + * (i + 5) days + 3 hours + * so 1 is 6 days + 3 hours, and should not be renewed. + * 2 is 7 days + 3 hours, and should be renewed + * + * NOTE: the ldb record order is different from the insertion order, + * but it should stay the same between searches. + */ + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert_int_equal(tctx, el->num_values, + testdata->num_records, + "num_values != num_records"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to pull record"); + if (i < 3) { + /* records 0 and 1 should not have been renewed */ + int old_ts = dns_timestamp_after + 3 - 24 * (i + 5); + torture_assertf( + tctx, + rec.dwTimeStamp == old_ts, + "record[%zu] timestamp should not be altered." + " diff is %d\n", + i, rec.dwTimeStamp - old_ts); + } else { + /* records 3+ should have a now-ish timestamp */ + int old_ts = dns_timestamp_after + 3 - 24 * (i + 5); + torture_assertf( + tctx, + rec.dwTimeStamp >= dns_timestamp_before, + "record[%zu] should have altered timestamp " + "now ~= %d, then ~= %d, has %d, diff %d\n", i, + dns_timestamp_before, old_ts, rec.dwTimeStamp, + dns_timestamp_before - rec.dwTimeStamp + ); + } + } + + /* + * Step 11. Set one record to be static. + * + * This should make the node static, but it won't "know" that until we + * force it with an update. + */ + torture_comment(tctx, "step 11: alter one timestamp to be 0\n"); + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert_int_equal(tctx, el->num_values, + testdata->num_records, + "num_values != num_records"); + + { + /* we're arbitrarily picking on record 3 */ + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[3]), + msg, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to pull record"); + + rec.dwTimeStamp = 0; + + ret = ndr_push_struct_blob( + &el->values[3], + msg, + &rec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to PUSH record"); + } + el->flags = LDB_FLAG_MOD_REPLACE; + + ret = ldb_modify(samdb, msg); + torture_assert_int_equal(tctx, ret, 0, "failed to ldb_modify"); + print_node_records(tctx, samdb, node_dn, "after ldb_modify"); + + + /* + * Step 12. Do updates on some records, zeroing their timestamps + * + * Zero means static. A single zero timestamp is infectious, so other + * records get it when they are updated. + */ + + for (i = 0; i < testdata->num_records - 2; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + dlz_closeversion(domain, ret == ISC_R_SUCCESS, dbdata, + &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to update record\n"); + } + print_node_records(tctx, samdb, node_dn, "after update to static"); + + + /* + * Step 13. Check that the record timeouts are *mostly* zero. + * + * one or two will be non-zero: we updated all but two, but one of + * excluded ones might be the el->records[3] that we explicitly set to + * zero. + */ + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + { + unsigned n_zero = 0; + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t)\ + ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, + "failed to pull record"); + if (rec.dwTimeStamp == 0) { + n_zero++; + } + } + if (n_zero != el->num_values - 1 && + n_zero != el->num_values - 2) { + torture_comment(tctx, "got %u zeros, expected %u or %u", + n_zero, + el->num_values - 2, + el->num_values - 1); + torture_fail(tctx, + "static node not setting zero timestamps\n"); + + } + } + + + /* + * Step 14. Turn aging off. + */ + torture_comment(tctx, "step 14: turn aging off\n"); + ok = set_zone_aging(tctx, domain, 0); + torture_assert(tctx, ok, "failed to disable aging"); + print_node_records(tctx, samdb, node_dn, "aging off"); + + /* + * Step 15. Update, setting timestamps to zero. + * + * Even with aging off, timestamps are still changed to static. + */ + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + dlz_closeversion(domain, ret == ISC_R_SUCCESS, dbdata, + &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to update record\n"); + } + print_node_records(tctx, samdb, node_dn, "after update with aging off"); + + + /* + * Step 16. Check that the timestamps are all zero. + */ + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t) ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, + "failed to pull record"); + torture_assertf(tctx, rec.dwTimeStamp == 0, + "record[%zu].dwTimeStamp is %u, expected 0\n", + i, rec.dwTimeStamp); + + } + + + /* + * Step 17. Reset to non-zero via ldb, with aging still off. + * + * We chose timestamps in the distant past that would all be updated + * if aging was on. + */ + torture_comment(tctx, "step 17: reset to non-zero timestamps\n"); + ret = dsdb_search_one(samdb, tctx, &msg, node_dn, + LDB_SCOPE_BASE, attrs, + 0, NULL); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf( + tctx, + "Failed to find %s node: %s", + name, ldb_errstring(samdb))); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to pull record"); + + rec.dwTimeStamp = 10000 + i; /* a long time ago */ + + ret = ndr_push_struct_blob( + &el->values[i], + msg, + &rec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to PUSH record"); + } + el->flags = LDB_FLAG_MOD_REPLACE; + + ret = ldb_modify(samdb, msg); + torture_assert_int_equal(tctx, ret, 0, "failed to ldb_modify"); + print_node_records(tctx, samdb, node_dn, "timestamps no-zero, aging off"); + + + /* + * Step 18. Update with aging off. Nothing should change. + * + */ + + /* now, with another update, some will be updated and some won't */ + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_addrdataset(name, r.rdata, dbdata, version); + dlz_closeversion(domain, ret == ISC_R_SUCCESS, dbdata, + &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to update record\n"); + } + print_node_records(tctx, samdb, node_dn, "after update"); + + + /* + * Step 19. Check that the timestamps didn't change. + */ + el = ldb_msg_find_element(msg, "dnsRecord"); + torture_assert_not_null(tctx, el, "el"); + torture_assert_int_equal(tctx, el->num_values, + testdata->num_records, + "num_values != num_records"); + + for (i = 0; i < el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec; + ret = ndr_pull_struct_blob( + &(el->values[i]), + msg, + &rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + torture_assert_ndr_success(tctx, ret, "failed to pull record"); + torture_assertf( + tctx, + rec.dwTimeStamp == 10000 + i, + "record[%zu] timestamp should not be altered.\n", + i); + } + + + /* + * Step 20. Delete all the records, 1 by 1. + * + */ + torture_comment(tctx, "step 20: delete the records\n"); + + for (i = 0; i < testdata->num_records; i++) { + struct test_expected_record r = testdata->records[i]; + + ret = dlz_newversion(domain, dbdata, &version); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Failed to start transaction"); + + ret = dlz_subrdataset(name, r.rdata, dbdata, version); + torture_assert_int_equal_goto( + tctx, ret, ISC_R_SUCCESS, ok, + cancel_version, + talloc_asprintf(tctx, + "Failed to delete record %zu «%s»\n", + i, r.rdata)); + + dlz_closeversion(domain, true, dbdata, &version); + + testdata->num_rr = 0; + + ret = dlz_lookup(domain, testdata->query_name, dbdata, + (dns_sdlzlookup_t *)testdata, NULL, NULL); + + if (i == testdata->num_records - 1) { + torture_assert_int_equal(tctx, ret, + ISC_R_NOTFOUND, + "no records should exist"); + } else { + torture_assert_int_equal(tctx, ret, + ISC_R_SUCCESS, + "records not found"); + } + + torture_assert_int_equal(tctx, + testdata->num_rr, + testdata->num_records - 1 - i, + "Got wrong record count"); + + for (j = 0; j < testdata->num_records; j++) { + struct test_expected_record *r2 = &testdata->records[j]; + if (j > i) { + torture_assert( + tctx, + r2->printed, + talloc_asprintf(tctx, + "putrr callback not run on %s «%s»", + r2->type, r2->name)); + } else { + torture_assert( + tctx, + ! r2->printed, + talloc_asprintf(tctx, + "putrr callback should not see %s «%s»", + r2->type, r2->name)); + } + r2->printed = false; + } + } + + dlz_destroy(dbdata); + + return true; + +cancel_version: + DBG_ERR("exiting with %d\n", ret); + dlz_closeversion(domain, false, dbdata, &version); + return ret; +} + + +static struct torture_suite *dlz_bind9_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dlz_bind9"); + + suite->description = talloc_strdup(suite, + "Tests for the BIND 9 DLZ module"); + torture_suite_add_simple_test(suite, "version", test_dlz_bind9_version); + torture_suite_add_simple_test(suite, "create", test_dlz_bind9_create); + torture_suite_add_simple_test(suite, "configure", test_dlz_bind9_configure); + torture_suite_add_simple_test(suite, "destroyoldestfirst", + test_dlz_bind9_destroy_oldest_first); + torture_suite_add_simple_test(suite, "destroynewestfirst", + test_dlz_bind9_destroy_newest_first); + torture_suite_add_simple_test(suite, "multipleconfigure", + test_dlz_bind9_multiple_configure); + + torture_suite_add_simple_test(suite, "gssapi", test_dlz_bind9_gssapi); + torture_suite_add_simple_test(suite, "spnego", test_dlz_bind9_spnego); + torture_suite_add_simple_test(suite, "lookup", test_dlz_bind9_lookup); + torture_suite_add_simple_test(suite, "zonedump", test_dlz_bind9_zonedump); + torture_suite_add_simple_test(suite, "update01", test_dlz_bind9_update01); + torture_suite_add_simple_test(suite, "aging", test_dlz_bind9_aging); + torture_suite_add_simple_test(suite, "allowzonexfr", test_dlz_bind9_allowzonexfr); + return suite; +} + +/** + * DNS torture module initialization + */ +NTSTATUS torture_bind_dns_init(TALLOC_CTX *); +NTSTATUS torture_bind_dns_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + /* register DNS related test cases */ + suite = dlz_bind9_suite(ctx); + if (!suite) return NT_STATUS_NO_MEMORY; + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/dns/internal_dns.c b/source4/torture/dns/internal_dns.c new file mode 100644 index 0000000..2ccc7ed --- /dev/null +++ b/source4/torture/dns/internal_dns.c @@ -0,0 +1,189 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Kai Blin 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include +#include "lib/addns/dns.h" + +static struct dns_connection *setup_connection(struct torture_context *tctx) +{ + DNS_ERROR err; + struct dns_connection *conn; + + err = dns_open_connection(getenv("DC_SERVER_IP"), DNS_TCP, tctx, &conn); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to open connection to DNS server\n"); + return NULL; + } + + return conn; +} + +static char *get_dns_domain(struct torture_context *tctx) +{ + return strlower_talloc(tctx, getenv("REALM")); +} + +static struct sockaddr_storage *str_to_sockaddr(TALLOC_CTX *mem_ctx, const char *ip_string) +{ + struct sockaddr_storage *ss = talloc_zero(mem_ctx, struct sockaddr_storage); + int ret; + + if (ss == NULL) { + return NULL; + } + + ss->ss_family = AF_INET; + + ret = inet_pton(AF_INET, ip_string, &(((struct sockaddr_in *)ss)->sin_addr)); + if (ret != 1) { + return NULL; + } + + return ss; +} + +static bool test_internal_dns_query_self(struct torture_context *tctx) +{ + struct dns_connection *conn; + struct dns_request *req, *resp; + char *host; + DNS_ERROR err; + + conn = setup_connection(tctx); + if (conn == NULL) { + return false; + } + + host = talloc_asprintf(tctx, "%s.%s", getenv("DC_SERVER"), get_dns_domain(tctx)); + if (host == NULL) { + return false; + } + + err = dns_create_query(conn, host, QTYPE_A, DNS_CLASS_IN, &req); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to create A record query\n"); + return false; + } + + err = dns_transaction(conn, conn, req, &resp); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to query DNS server\n"); + return false; + } + + if (dns_response_code(resp->flags) != DNS_NO_ERROR) { + printf("Query returned %u\n", dns_response_code(resp->flags)); + return false; + } + + /* FIXME: is there _any_ way to unmarshal the response to check this? */ + + return true; +} + +static bool test_internal_dns_update_self(struct torture_context *tctx) +{ + struct dns_connection *conn; + struct dns_update_request *req, *resp; + struct dns_rrec *rec = NULL; + char *host; + DNS_ERROR err; + struct sockaddr_storage *ss; + + conn = setup_connection(tctx); + if (conn == NULL) { + return false; + } + + host = talloc_asprintf(tctx, "%s.%s", getenv("DC_SERVER"), get_dns_domain(tctx)); + if (host == NULL) { + return false; + } + + err = dns_create_update(conn, get_dns_domain(tctx), &req); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to update packet\n"); + return false; + } + + ss = str_to_sockaddr(conn, getenv("DC_SERVER_IP")); + if (ss == NULL) { + printf("Converting '%s' to sockaddr_storage failed\n", getenv("DC_SERVER_IP")); + return false; + } + + err = dns_create_a_record(req, host, 300, ss, &rec); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to create A update record\n"); + return false; + } + + err = dns_add_rrec(req, rec, &req->num_updates, &req->updates); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to add A update record to update packet\n"); + return false; + } + + err = dns_update_transaction(conn, conn, req, &resp); + if (!ERR_DNS_IS_OK(err)) { + printf("Failed to send update\n"); + return false; + } + + if (dns_response_code(resp->flags) != DNS_REFUSED) { + printf("Update returned %u\n", dns_response_code(resp->flags)); + return false; + } + + /* FIXME: is there _any_ way to unmarshal the response to check this? */ + + return true; +} + +static struct torture_suite *internal_dns_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dns_internal"); + + suite->description = talloc_strdup(suite, + "Tests for the internal DNS server"); + torture_suite_add_simple_test(suite, "queryself", test_internal_dns_query_self); + torture_suite_add_simple_test(suite, "updateself", test_internal_dns_update_self); + return suite; +} + + +/* Silence silly compiler warning */ +NTSTATUS torture_internal_dns_init(TALLOC_CTX *); + +/** + * DNS torture module initialization + */ +NTSTATUS torture_internal_dns_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + /* register internal DNS torture test cases */ + suite = internal_dns_suite(ctx); + if (!suite) return NT_STATUS_NO_MEMORY; + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/dns/wscript_build b/source4/torture/dns/wscript_build new file mode 100644 index 0000000..0b40e03 --- /dev/null +++ b/source4/torture/dns/wscript_build @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +if bld.AD_DC_BUILD_IS_ENABLED(): + bld.SAMBA_MODULE('TORTURE_BIND_DNS', + source='dlz_bind9.c', + subsystem='smbtorture', + init_function='torture_bind_dns_init', + cflags='-DBIND_VERSION_9_16', + deps='torture talloc torturemain dlz_bind9_for_torture', + internal_module=True + ) + + bld.SAMBA_MODULE('TORTURE_INTERNAL_DNS', + source='internal_dns.c', + subsystem='smbtorture', + init_function='torture_internal_dns_init', + deps='torture talloc torturemain', + internal_module=True + ) diff --git a/source4/torture/drs/drs_init.c b/source4/torture/drs/drs_init.c new file mode 100644 index 0000000..bbe246d --- /dev/null +++ b/source4/torture/drs/drs_init.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + + DRSUAPI utility functions to be used in torture tests + + Copyright (C) Kamen Mazdrashki 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/rpc/drsuapi.h" +#include "dsdb/samdb/samdb.h" +#include "torture/drs/proto.h" + +/** + * DRSUAPI tests to be executed remotely + */ +static struct torture_suite * torture_drs_rpc_suite(TALLOC_CTX *mem_ctx, + const char *suite_name) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, suite_name); + + torture_drs_rpc_dssync_tcase(suite); + torture_drs_rpc_dsintid_tcase(suite); + + suite->description = talloc_strdup(suite, + "DRSUAPI RPC Tests Suite"); + + return suite; +} + +/** + * DRSUAPI tests to be executed remotely + */ +static struct torture_suite * torture_drs_unit_suite(TALLOC_CTX *mem_ctx, + const char *suite_name) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, suite_name); + + torture_drs_unit_prefixmap(suite); + torture_drs_unit_schemainfo(suite); + + suite->description = talloc_strdup(suite, + "DRSUAPI Unit Tests Suite"); + + return suite; +} + +/** + * DRSUAPI torture module initialization + */ +NTSTATUS torture_drs_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + /* register RPC related test cases */ + suite = torture_drs_rpc_suite(ctx, "drs.rpc"); + if (!suite) return NT_STATUS_NO_MEMORY; + torture_register_suite(ctx, suite); + + /* register DRS Unit test cases */ + suite = torture_drs_unit_suite(ctx, "drs.unit"); + if (!suite) return NT_STATUS_NO_MEMORY; + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/drs/drs_util.c b/source4/torture/drs/drs_util.c new file mode 100644 index 0000000..c43836e --- /dev/null +++ b/source4/torture/drs/drs_util.c @@ -0,0 +1,167 @@ +/* + Unix SMB/CIFS implementation. + + DRSUAPI utility functions to be used in torture tests + + Copyright (C) Kamen Mazdrashki 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "dsdb/samdb/samdb.h" +#include "torture/rpc/drsuapi.h" +#include "../lib/util/asn1.h" +#include "torture/drs/proto.h" + +/** + * Decode Attribute OID based on MS documentation + * See MS-DRSR.pdf - 5.16.4 + * + * On success returns decoded OID and + * corresponding prefix_map index (if requested) + */ +bool drs_util_oid_from_attid(struct torture_context *tctx, + const struct drsuapi_DsReplicaOIDMapping_Ctr *prefix_map, + uint32_t attid, + const char **_oid, + int *map_idx) +{ + uint32_t i, hi_word, lo_word; + DATA_BLOB bin_oid = {NULL, 0}; + char *oid; + struct drsuapi_DsReplicaOIDMapping *map_entry = NULL; + TALLOC_CTX *mem_ctx = talloc_named(tctx, 0, "util_drsuapi_oid_from_attid"); + + /* crack attid value */ + hi_word = attid >> 16; + lo_word = attid & 0xFFFF; + + /* check last entry in the prefix map is the special one */ + map_entry = &prefix_map->mappings[prefix_map->num_mappings-1]; + torture_assert(tctx, + (map_entry->id_prefix == 0) + && (*map_entry->oid.binary_oid == 0xFF), + "Last entry in Prefix Map is not the special one!"); + + /* locate corresponding prefixMap entry */ + map_entry = NULL; + for (i = 0; i < prefix_map->num_mappings - 1; i++) { + + if (hi_word == prefix_map->mappings[i].id_prefix) { + map_entry = &prefix_map->mappings[i]; + if (map_idx) *map_idx = i; + break; + } + } + + torture_assert(tctx, map_entry, "Unable to locate corresponding Prefix Map entry"); + + /* copy partial oid making enough room */ + bin_oid.length = map_entry->oid.length + 2; + bin_oid.data = talloc_array(mem_ctx, uint8_t, bin_oid.length); + torture_assert(tctx, bin_oid.data, "Not enough memory"); + memcpy(bin_oid.data, map_entry->oid.binary_oid, map_entry->oid.length); + + if (lo_word < 128) { + bin_oid.length = bin_oid.length - 1; + bin_oid.data[bin_oid.length-1] = lo_word; + } + else { + if (lo_word >= 32768) { + lo_word -= 32768; + } + bin_oid.data[bin_oid.length-2] = ((lo_word / 128) % 128) + 128; /* (0x80 | ((lo_word>>7) & 0x7f)) */ + bin_oid.data[bin_oid.length-1] = lo_word % 128; /* lo_word & 0x7f */ + } + + torture_assert(tctx, + ber_read_OID_String(tctx, bin_oid, &oid), + "Failed to decode binary OID"); + talloc_free(mem_ctx); + + *_oid = oid; + + return true; +} + + +/** + * Loads dsdb_schema from ldb connection using remote prefixMap. + * Schema will be loaded only if: + * - ldb has no attached schema + * - reload_schema is true + * + * This function is to be used in tests that use GetNCChanges() function + */ +bool drs_util_dsdb_schema_load_ldb(struct torture_context *tctx, + struct ldb_context *ldb, + const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr, + bool reload_schema) +{ + int ret; + WERROR werr; + char *err_msg; + struct ldb_result *res; + struct ldb_dn *schema_dn; + struct dsdb_schema *ldap_schema; + + ldap_schema = dsdb_get_schema(ldb, NULL); + if (ldap_schema && !reload_schema) { + return true; + } + + schema_dn = ldb_get_schema_basedn(ldb); + torture_assert(tctx, schema_dn != NULL, + talloc_asprintf(tctx, "ldb_get_schema_basedn() failed: %s", ldb_errstring(ldb))); + + ldap_schema = dsdb_new_schema(ldb); + torture_assert(tctx, ldap_schema != NULL, "dsdb_new_schema() failed!"); + + werr = dsdb_load_prefixmap_from_drsuapi(ldap_schema, mapping_ctr); + torture_assert_werr_ok(tctx, werr, + "Failed to construct prefixMap from drsuapi data"); + + /* + * load the attribute and objectClass definitions + */ + ret = ldb_search(ldb, ldap_schema, &res, + schema_dn, LDB_SCOPE_ONELEVEL, NULL, + "(|(objectClass=attributeSchema)(objectClass=classSchema))"); + if (ret != LDB_SUCCESS) { + err_msg = talloc_asprintf(tctx, + "failed to search attributeSchema or classSchema objects: %s", + ldb_errstring(ldb)); + torture_fail(tctx, err_msg); + } + + ret = dsdb_load_ldb_results_into_schema(tctx, ldb, ldap_schema, res, &err_msg); + if (ret != LDB_SUCCESS) { + err_msg = talloc_asprintf(tctx, + "dsdb_load_ldb_results_into_schema failed: %s", + err_msg); + torture_fail(tctx, err_msg); + } + + talloc_free(res); + + ret = dsdb_set_schema(ldb, ldap_schema, SCHEMA_WRITE); + if (ret != LDB_SUCCESS) { + torture_fail(tctx, + talloc_asprintf(tctx, "dsdb_set_schema() failed: %s", ldb_strerror(ret))); + } + + return true; +} diff --git a/source4/torture/drs/python/cracknames.py b/source4/torture/drs/python/cracknames.py new file mode 100644 index 0000000..f244605 --- /dev/null +++ b/source4/torture/drs/python/cracknames.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) Catalyst .Net Ltd 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import samba.tests +import ldb +import drs_base + +from samba.dcerpc import drsuapi + + +class DrsCracknamesTestCase(drs_base.DrsBaseTestCase): + def setUp(self): + super(DrsCracknamesTestCase, self).setUp() + (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) + + self.ou = "ou=Cracknames_ou,%s" % self.ldb_dc1.get_default_basedn() + self.username = "Cracknames_user" + self.user = "cn=%s,%s" % (self.username, self.ou) + + self.ldb_dc1.add({ + "dn": self.ou, + "objectclass": "organizationalUnit"}) + + self.user_record = { + "dn": self.user, + "objectclass": "user", + "sAMAccountName": self.username, + "userPrincipalName": "test@test.com", + "servicePrincipalName": "test/%s" % self.ldb_dc1.get_default_basedn(), + "displayName": "test"} + + self.ldb_dc1.add(self.user_record) + self.ldb_dc1.delete(self.user_record["dn"]) + self.ldb_dc1.add(self.user_record) + + # The formats specified in MS-DRSR 4.1.4.13; DS_NAME_FORMAT + # We don't support any of the ones specified in 4.1.4.1.2. + self.formats = { + drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + drsuapi.DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + drsuapi.DRSUAPI_DS_NAME_FORMAT_DISPLAY, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID, + drsuapi.DRSUAPI_DS_NAME_FORMAT_CANONICAL, + drsuapi.DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + drsuapi.DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + drsuapi.DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + # This format is not supported by Windows (or us) + # drsuapi.DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN, + } + + def tearDown(self): + self.ldb_dc1.delete(self.user) + self.ldb_dc1.delete(self.ou) + super(DrsCracknamesTestCase, self).tearDown() + + def test_Cracknames(self): + """ + Verifies that we can cracknames any of the standard formats + (DS_NAME_FORMAT) to a GUID, and that we can cracknames a + GUID to any of the standard formats. + + GUID was chosen just so that we don't have to do an n^2 loop. + """ + (result, ctr) = self._do_cracknames(self.user, + drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_OK) + + user_guid = ctr.array[0].result_name + + for name_format in self.formats: + (result, ctr) = self._do_cracknames(user_guid, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID, + name_format) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_OK, + "Expected 0, got %s, desired format is %s" + % (ctr.array[0].status, name_format)) + + (result, ctr) = self._do_cracknames(ctr.array[0].result_name, + name_format, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_OK, + "Expected 0, got %s, offered format is %s" + % (ctr.array[0].status, name_format)) + + def test_MultiValuedAttribute(self): + """ + Verifies that, if we try and cracknames with the desired output + being a multi-valued attribute, it returns + DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE. + """ + username = "Cracknames_user_MVA" + user = "cn=%s,%s" % (username, self.ou) + + user_record = { + "dn": user, + "objectclass": "user", + "sAMAccountName": username, + "userPrincipalName": "test2@test.com", + "servicePrincipalName": ["test2/%s" % self.ldb_dc1.get_default_basedn(), + "test3/%s" % self.ldb_dc1.get_default_basedn()], + "displayName": "test2"} + + self.ldb_dc1.add(user_record) + + (result, ctr) = self._do_cracknames(user, + drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_OK) + + user_guid = ctr.array[0].result_name + + (result, ctr) = self._do_cracknames(user_guid, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID, + drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) + + self.ldb_dc1.delete(user) + + def test_NoSPNAttribute(self): + """ + Verifies that, if we try and cracknames with the desired output + being an SPN, it returns + DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE. + """ + username = "Cracknames_no_SPN" + user = "cn=%s,%s" % (username, self.ou) + + user_record = { + "dn": user, + "objectclass": "user", + "sAMAccountName" : username, + "userPrincipalName" : "test4@test.com", + "displayName" : "test4"} + + self.ldb_dc1.add(user_record) + + (result, ctr) = self._do_cracknames(user, + drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_OK) + + user_guid = ctr.array[0].result_name + + (result, ctr) = self._do_cracknames(user_guid, + drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID, + drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) + + self.assertEqual(ctr.count, 1) + self.assertEqual(ctr.array[0].status, + drsuapi.DRSUAPI_DS_NAME_STATUS_NOT_FOUND) + + self.ldb_dc1.delete(user) + + def _do_cracknames(self, name, format_offered, format_desired): + req = drsuapi.DsNameRequest1() + names = drsuapi.DsNameString() + names.str = name + + req.codepage = 1252 # German, but it doesn't really matter here + req.language = 1033 + req.format_flags = 0 + req.format_offered = format_offered + req.format_desired = format_desired + req.count = 1 + req.names = [names] + + (result, ctr) = self.drs.DsCrackNames(self.drs_handle, 1, req) + return (result, ctr) diff --git a/source4/torture/drs/python/delete_object.py b/source4/torture/drs/python/delete_object.py new file mode 100644 index 0000000..5f2f703 --- /dev/null +++ b/source4/torture/drs/python/delete_object.py @@ -0,0 +1,376 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Kamen Mazdrashki 2010 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN delete_object -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import time + + +from ldb import ( + SCOPE_SUBTREE, +) + +import drs_base +import ldb + + +class DrsDeleteObjectTestCase(drs_base.DrsBaseTestCase): + + def setUp(self): + super(DrsDeleteObjectTestCase, self).setUp() + # disable automatic replication temporary + self._disable_all_repl(self.dnsname_dc1) + self._disable_all_repl(self.dnsname_dc2) + # make sure DCs are synchronized before the test + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + def tearDown(self): + self._enable_all_repl(self.dnsname_dc1) + self._enable_all_repl(self.dnsname_dc2) + super(DrsDeleteObjectTestCase, self).tearDown() + + def _make_username(self): + return "DrsDelObjUser_" + time.strftime("%s", time.gmtime()) + + # now also used to check the group + def _check_obj(self, sam_ldb, obj_orig, is_deleted): + # search the user by guid as it may be deleted + guid_str = self._GUID_string(obj_orig["objectGUID"][0]) + expression = "(objectGUID=%s)" % guid_str + res = sam_ldb.search(base=self.domain_dn, + expression=expression, + controls=["show_deleted:1"]) + self.assertEqual(len(res), 1) + user_cur = res[0] + # Deleted Object base DN + dodn = self._deleted_objects_dn(sam_ldb) + # now check properties of the user + cn_orig = str(obj_orig["cn"][0]) + cn_cur = str(user_cur["cn"][0]) + name_orig = str(obj_orig["name"][0]) + name_cur = str(user_cur["name"][0]) + if is_deleted: + self.assertEqual(str(user_cur["isDeleted"][0]), "TRUE") + self.assertFalse("objectCategory" in user_cur) + self.assertFalse("sAMAccountType" in user_cur) + self.assertFalse("description" in user_cur) + self.assertFalse("memberOf" in user_cur) + self.assertFalse("member" in user_cur) + self.assertTrue(dodn in str(user_cur["dn"]), + "User %s is deleted but it is not located under %s (found at %s)!" % (name_orig, dodn, user_cur["dn"])) + self.assertEqual(name_cur, name_orig + "\nDEL:" + guid_str) + self.assertEqual(name_cur, user_cur.dn.get_rdn_value()) + self.assertEqual(cn_cur, cn_orig + "\nDEL:" + guid_str) + self.assertEqual(name_cur, cn_cur) + else: + self.assertFalse("isDeleted" in user_cur) + self.assertEqual(name_cur, name_orig) + self.assertEqual(name_cur, user_cur.dn.get_rdn_value()) + self.assertEqual(cn_cur, cn_orig) + self.assertEqual(name_cur, cn_cur) + self.assertEqual(obj_orig["dn"], user_cur["dn"]) + self.assertTrue(dodn not in str(user_cur["dn"])) + return user_cur + + def test_ReplicateDeletedObject1(self): + """Verifies how a deleted-object is replicated between two DCs. + This test should verify that: + - deleted-object is replicated properly + - We verify that after replication, + object's state to conform to a tombstone-object state + - This test replicates the object modifications to + the server with the user deleted first + + TODO: It will also be great if check replPropertyMetaData. + TODO: Check for deleted-object state, depending on DC's features + when recycle-bin is enabled + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, password="P@sswOrd!") + ldb_res = self.ldb_dc1.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # delete user on DC1 + self.ldb_dc1.delete(user_dn) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + # The user should not have a description or memberOf yet + self.assertFalse("description" in user_cur) + self.assertFalse("memberOf" in user_cur) + + self.ldb_dc2.newgroup("group_%s" % username) + + self.ldb_dc2.newgroup("group2_%s" % username) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("sAMAccountName" in ldb_res[0]) + group_orig = ldb_res[0] + group_dn = ldb_res[0]["dn"] + + # modify user on DC2 to have a description and be a member of the group + m = ldb.Message() + m.dn = user_dn + m["description"] = ldb.MessageElement("a description", + ldb.FLAG_MOD_ADD, "description") + self.ldb_dc2.modify(m) + m = ldb.Message() + m.dn = group_dn + m["member"] = ldb.MessageElement(str(user_dn), + ldb.FLAG_MOD_ADD, "member") + self.ldb_dc2.modify(m) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group2_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("sAMAccountName" in ldb_res[0]) + group2_dn = ldb_res[0]["dn"] + group2_orig = ldb_res[0] + + m = ldb.Message() + m.dn = group2_dn + m["member"] = ldb.MessageElement(str(group_dn), + ldb.FLAG_MOD_ADD, "member") + self.ldb_dc2.modify(m) + + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + # The user should not have a description yet + self.assertTrue("description" in user_cur) + self.assertTrue("memberOf" in user_cur) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + + # This group is a member of another group + self.assertTrue("memberOf" in ldb_res[0]) + + # The user was deleted on DC1, but check the modify we just did on DC2 + self.assertTrue("member" in ldb_res[0]) + + # trigger replication from DC2 to DC1 + # to check if deleted object gets restored + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be valid user + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + ldb_res = self.ldb_dc1.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + + # This group is a member of another group + self.assertTrue("memberOf" in ldb_res[0]) + + # The user was deleted on DC1, but the modify we did on DC2, check it never replicated in + self.assertFalse("member" in ldb_res[0]) + + # trigger replication from DC1 to DC2 + # to check if deleted object is replicated + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=True) + + # delete group on DC1 + self.ldb_dc1.delete(group_dn) + + # trigger replication from DC1 to DC2 + # to check if deleted object is replicated + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check group info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=group_orig, is_deleted=True) + # check group info on DC2 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=group_orig, is_deleted=True) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group2_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertFalse("member" in ldb_res[0]) + + # delete group on DC1 + self.ldb_dc1.delete(group2_dn) + + # trigger replication from DC1 to DC2 + # to check if deleted object is replicated + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check group info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=group2_orig, is_deleted=True) + # check group info on DC2 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=group2_orig, is_deleted=True) + + def test_ReplicateDeletedObject2(self): + """Verifies how a deleted-object is replicated between two DCs. + This test should verify that: + - deleted-object is replicated properly + - We verify that after replication, + object's state to conform to a tombstone-object state + - This test replicates the delete to the server with the + object modifications first + + TODO: It will also be great if check replPropertyMetaData. + TODO: Check for deleted-object state, depending on DC's features + when recycle-bin is enabled + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, password="P@sswOrd!") + ldb_res = self.ldb_dc1.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # delete user on DC1 + self.ldb_dc1.delete(user_dn) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + # The user should not have a description or memberOf yet + self.assertFalse("description" in user_cur) + self.assertFalse("memberOf" in user_cur) + + self.ldb_dc2.newgroup("group_%s" % username) + + self.ldb_dc2.newgroup("group2_%s" % username) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("sAMAccountName" in ldb_res[0]) + group_dn = ldb_res[0]["dn"] + + # modify user on DC2 to have a description and be a member of the group + m = ldb.Message() + m.dn = user_dn + m["description"] = ldb.MessageElement("a description", + ldb.FLAG_MOD_ADD, "description") + self.ldb_dc2.modify(m) + m = ldb.Message() + m.dn = group_dn + m["member"] = ldb.MessageElement(str(user_dn), + ldb.FLAG_MOD_ADD, "member") + self.ldb_dc2.modify(m) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group2_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("sAMAccountName" in ldb_res[0]) + group2_dn = ldb_res[0]["dn"] + + m = ldb.Message() + m.dn = group2_dn + m["member"] = ldb.MessageElement(str(group_dn), + ldb.FLAG_MOD_ADD, "member") + self.ldb_dc2.modify(m) + + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + # The user should not have a description yet + self.assertTrue("description" in user_cur) + self.assertTrue("memberOf" in user_cur) + + # trigger replication from DC1 to DC2 + # to check if deleted object gets restored + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=True) + + ldb_res = self.ldb_dc2.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("memberOf" in ldb_res[0]) + self.assertFalse("member" in ldb_res[0]) + + # trigger replication from DC2 to DC1 + # to check if deleted object is replicated + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=True) + # check user info on DC2 - should be deleted + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=True) + + ldb_res = self.ldb_dc1.search(base=self.domain_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=group_%s)" % username) + self.assertTrue(len(ldb_res) == 1) + self.assertTrue("memberOf" in ldb_res[0]) + self.assertFalse("member" in ldb_res[0]) + + # delete group on DC1 + self.ldb_dc1.delete(group_dn) + self.ldb_dc1.delete(group2_dn) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) diff --git a/source4/torture/drs/python/drs_base.py b/source4/torture/drs/python/drs_base.py new file mode 100644 index 0000000..bf98e59 --- /dev/null +++ b/source4/torture/drs/python/drs_base.py @@ -0,0 +1,632 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2016 +# Copyright (C) Catalyst IT Ltd. 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys +import time +import os +import ldb + +sys.path.insert(0, "bin/python") +import samba.tests +from samba.tests.samba_tool.base import SambaToolCmdTest +from samba import dsdb +from samba.dcerpc import drsuapi, misc, drsblobs, security +from samba.ndr import ndr_unpack, ndr_pack +from samba.drs_utils import drs_DsBind +from samba import gensec +from ldb import ( + SCOPE_BASE, + Message, + FLAG_MOD_REPLACE, +) +from samba.common import cmp +from samba.common import get_string + + +class DrsBaseTestCase(SambaToolCmdTest): + """Base class implementation for all DRS python tests. + It is intended to provide common initialization and + and functionality used by all DRS tests in drs/python + test package. For instance, DC1 and DC2 are always used + to pass URLs for DCs to test against""" + + def setUp(self): + super(DrsBaseTestCase, self).setUp() + creds = self.get_credentials() + creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL) + + # connect to DCs + self.url_dc1 = samba.tests.env_get_var_value("DC1") + (self.ldb_dc1, self.info_dc1) = samba.tests.connect_samdb_ex(self.url_dc1, + ldap_only=True) + self.url_dc2 = samba.tests.env_get_var_value("DC2") + (self.ldb_dc2, self.info_dc2) = samba.tests.connect_samdb_ex(self.url_dc2, + ldap_only=True) + self.test_ldb_dc = self.ldb_dc1 + + # cache some of RootDSE props + self.schema_dn = str(self.info_dc1["schemaNamingContext"][0]) + self.domain_dn = str(self.info_dc1["defaultNamingContext"][0]) + self.config_dn = str(self.info_dc1["configurationNamingContext"][0]) + self.forest_level = int(self.info_dc1["forestFunctionality"][0]) + + # we will need DCs DNS names for 'samba-tool drs' command + self.dnsname_dc1 = str(self.info_dc1["dnsHostName"][0]) + self.dnsname_dc2 = str(self.info_dc2["dnsHostName"][0]) + + # for debugging the test code + self._debug = False + + def tearDown(self): + super(DrsBaseTestCase, self).tearDown() + + def set_test_ldb_dc(self, ldb_dc): + """Sets which DC's LDB we perform operations on during the test""" + self.test_ldb_dc = ldb_dc + + def _GUID_string(self, guid): + return get_string(self.test_ldb_dc.schema_format_value("objectGUID", guid)) + + def _ldap_schemaUpdateNow(self, sam_db): + rec = {"dn": "", + "schemaUpdateNow": "1"} + m = Message.from_dict(sam_db, rec, FLAG_MOD_REPLACE) + sam_db.modify(m) + + def _deleted_objects_dn(self, sam_ldb): + wkdn = "" % self.domain_dn + res = sam_ldb.search(base=wkdn, + scope=SCOPE_BASE, + controls=["show_deleted:1"]) + self.assertEqual(len(res), 1) + return str(res[0]["dn"]) + + def _lost_and_found_dn(self, sam_ldb, nc): + wkdn = "" % (dsdb.DS_GUID_LOSTANDFOUND_CONTAINER, nc) + res = sam_ldb.search(base=wkdn, + scope=SCOPE_BASE) + self.assertEqual(len(res), 1) + return str(res[0]["dn"]) + + def _make_obj_name(self, prefix): + return prefix + time.strftime("%s", time.gmtime()) + + def _samba_tool_cmd_list(self, drs_command): + # make command line credentials string + + # If test runs on windows then it can provide its own auth string + if hasattr(self, 'cmdline_auth'): + cmdline_auth = self.cmdline_auth + else: + ccache_name = self.get_creds_ccache_name() + + # Tunnel the command line credentials down to the + # subcommand to avoid a new kinit + cmdline_auth = "--use-krb5-ccache=%s" % ccache_name + + # bin/samba-tool drs + return ["drs", drs_command, cmdline_auth] + + def _net_drs_replicate(self, DC, fromDC, nc_dn=None, forced=True, + local=False, full_sync=False, single=False): + if nc_dn is None: + nc_dn = self.domain_dn + # make base command line + samba_tool_cmdline = self._samba_tool_cmd_list("replicate") + # bin/samba-tool drs replicate + samba_tool_cmdline += [DC, fromDC, nc_dn] + + if forced: + samba_tool_cmdline += ["--sync-forced"] + if local: + samba_tool_cmdline += ["--local"] + if full_sync: + samba_tool_cmdline += ["--full-sync"] + if single: + samba_tool_cmdline += ["--single-object"] + + (result, out, err) = self.runsubcmd(*samba_tool_cmdline) + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + def _enable_inbound_repl(self, DC): + # make base command line + samba_tool_cmd = self._samba_tool_cmd_list("options") + # disable replication + samba_tool_cmd += [DC, "--dsa-option=-DISABLE_INBOUND_REPL"] + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + def _disable_inbound_repl(self, DC): + # make base command line + samba_tool_cmd = self._samba_tool_cmd_list("options") + # disable replication + samba_tool_cmd += [DC, "--dsa-option=+DISABLE_INBOUND_REPL"] + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + def _enable_all_repl(self, DC): + self._enable_inbound_repl(DC) + # make base command line + samba_tool_cmd = self._samba_tool_cmd_list("options") + # enable replication + samba_tool_cmd += [DC, "--dsa-option=-DISABLE_OUTBOUND_REPL"] + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + def _disable_all_repl(self, DC): + self._disable_inbound_repl(DC) + # make base command line + samba_tool_cmd = self._samba_tool_cmd_list("options") + # disable replication + samba_tool_cmd += [DC, "--dsa-option=+DISABLE_OUTBOUND_REPL"] + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + def _get_highest_hwm_utdv(self, ldb_conn): + res = ldb_conn.search("", scope=ldb.SCOPE_BASE, attrs=["highestCommittedUSN"]) + hwm = drsuapi.DsReplicaHighWaterMark() + hwm.tmp_highest_usn = int(res[0]["highestCommittedUSN"][0]) + hwm.reserved_usn = 0 + hwm.highest_usn = hwm.tmp_highest_usn + + utdv = drsuapi.DsReplicaCursorCtrEx() + cursors = [] + c1 = drsuapi.DsReplicaCursor() + c1.source_dsa_invocation_id = misc.GUID(ldb_conn.get_invocation_id()) + c1.highest_usn = hwm.highest_usn + cursors.append(c1) + utdv.count = len(cursors) + utdv.cursors = cursors + return (hwm, utdv) + + def _get_identifier(self, ldb_conn, dn): + res = ldb_conn.search(dn, scope=ldb.SCOPE_BASE, + attrs=["objectGUID", "objectSid"]) + id = drsuapi.DsReplicaObjectIdentifier() + id.guid = ndr_unpack(misc.GUID, res[0]['objectGUID'][0]) + if "objectSid" in res[0]: + id.sid = ndr_unpack(security.dom_sid, res[0]['objectSid'][0]) + id.dn = str(res[0].dn) + return id + + def _get_ctr6_links(self, ctr6): + """ + Unpacks the linked attributes from a DsGetNCChanges response + and returns them as a list. + """ + ctr6_links = [] + for lidx in range(0, ctr6.linked_attributes_count): + l = ctr6.linked_attributes[lidx] + try: + target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + l.value.blob) + except: + target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary, + l.value.blob) + al = AbstractLink(l.attid, l.flags, + l.identifier.guid, + target.guid, target.dn) + ctr6_links.append(al) + + return ctr6_links + + def _get_ctr6_object_guids(self, ctr6): + """Returns all the object GUIDs in a GetNCChanges response""" + guid_list = [] + + obj = ctr6.first_object + for i in range(0, ctr6.object_count): + guid_list.append(str(obj.object.identifier.guid)) + obj = obj.next_object + + return guid_list + + def _ctr6_debug(self, ctr6): + """ + Displays basic info contained in a DsGetNCChanges response. + Having this debug code allows us to see the difference in behaviour + between Samba and Windows easier. Turn on the self._debug flag to see it. + """ + + if self._debug: + print("------------ recvd CTR6 -------------") + + next_object = ctr6.first_object + for i in range(0, ctr6.object_count): + print("Obj %d: %s %s" % (i, next_object.object.identifier.dn[:25], + next_object.object.identifier.guid)) + next_object = next_object.next_object + + print("Linked Attributes: %d" % ctr6.linked_attributes_count) + for lidx in range(0, ctr6.linked_attributes_count): + l = ctr6.linked_attributes[lidx] + try: + target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + l.value.blob) + except: + target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary, + l.value.blob) + + print("Link Tgt %s... <-- Src %s" + % (target.dn[:25], l.identifier.guid)) + state = "Del" + if l.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE: + state = "Act" + print(" v%u %s changed %u" % (l.meta_data.version, state, + l.meta_data.originating_change_time)) + + print("HWM: %d" % (ctr6.new_highwatermark.highest_usn)) + print("Tmp HWM: %d" % (ctr6.new_highwatermark.tmp_highest_usn)) + print("More data: %d" % (ctr6.more_data)) + + def _get_replication(self, replica_flags, + drs_error=drsuapi.DRSUAPI_EXOP_ERR_NONE, drs=None, drs_handle=None, + highwatermark=None, uptodateness_vector=None, + more_flags=0, max_objects=133, exop=0, + dest_dsa=drsuapi.DRSUAPI_DS_BIND_GUID_W2K3, + source_dsa=None, invocation_id=None, nc_dn_str=None): + """ + Builds a DsGetNCChanges request based on the information provided + and returns the response received from the DC. + """ + if source_dsa is None: + source_dsa = self.test_ldb_dc.get_ntds_GUID() + if invocation_id is None: + invocation_id = self.test_ldb_dc.get_invocation_id() + if nc_dn_str is None: + nc_dn_str = self.test_ldb_dc.domain_dn() + + if highwatermark is None: + if self.default_hwm is None: + (highwatermark, _) = self._get_highest_hwm_utdv(self.test_ldb_dc) + else: + highwatermark = self.default_hwm + + if drs is None: + drs = self.drs + if drs_handle is None: + drs_handle = self.drs_handle + + req10 = self._getnc_req10(dest_dsa=dest_dsa, + invocation_id=invocation_id, + nc_dn_str=nc_dn_str, + exop=exop, + max_objects=max_objects, + replica_flags=replica_flags, + more_flags=more_flags) + req10.highwatermark = highwatermark + if uptodateness_vector is not None: + uptodateness_vector_v1 = drsuapi.DsReplicaCursorCtrEx() + cursors = [] + for i in range(0, uptodateness_vector.count): + c = uptodateness_vector.cursors[i] + c1 = drsuapi.DsReplicaCursor() + c1.source_dsa_invocation_id = c.source_dsa_invocation_id + c1.highest_usn = c.highest_usn + cursors.append(c1) + uptodateness_vector_v1.count = len(cursors) + uptodateness_vector_v1.cursors = cursors + req10.uptodateness_vector = uptodateness_vector_v1 + (level, ctr) = drs.DsGetNCChanges(drs_handle, 10, req10) + self._ctr6_debug(ctr) + + self.assertEqual(level, 6, "expected level 6 response!") + self.assertEqual(ctr.source_dsa_guid, misc.GUID(source_dsa)) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(invocation_id)) + self.assertEqual(ctr.extended_ret, drs_error) + + return ctr + + def _check_replication(self, expected_dns, replica_flags, expected_links=None, + drs_error=drsuapi.DRSUAPI_EXOP_ERR_NONE, drs=None, drs_handle=None, + highwatermark=None, uptodateness_vector=None, + more_flags=0, more_data=False, + dn_ordered=True, links_ordered=True, + max_objects=133, exop=0, + dest_dsa=drsuapi.DRSUAPI_DS_BIND_GUID_W2K3, + source_dsa=None, invocation_id=None, nc_dn_str=None, + nc_object_count=0, nc_linked_attributes_count=0): + """ + Makes sure that replication returns the specific error given. + """ + if expected_links is None: + expected_links = [] + + # send a DsGetNCChanges to the DC + ctr6 = self._get_replication(replica_flags, + drs_error, drs, drs_handle, + highwatermark, uptodateness_vector, + more_flags, max_objects, exop, dest_dsa, + source_dsa, invocation_id, nc_dn_str) + + # check the response is what we expect + self._check_ctr6(ctr6, expected_dns, expected_links, + nc_object_count=nc_object_count, more_data=more_data, + dn_ordered=dn_ordered) + return (ctr6.new_highwatermark, ctr6.uptodateness_vector) + + def _get_ctr6_dn_list(self, ctr6): + """ + Returns the DNs contained in a DsGetNCChanges response. + """ + dn_list = [] + next_object = ctr6.first_object + for i in range(0, ctr6.object_count): + dn_list.append(next_object.object.identifier.dn) + next_object = next_object.next_object + self.assertEqual(next_object, None) + + return dn_list + + def _check_ctr6(self, ctr6, expected_dns=None, expected_links=None, + dn_ordered=True, links_ordered=True, + more_data=False, nc_object_count=0, + nc_linked_attributes_count=0, drs_error=0): + """ + Check that a ctr6 matches the specified parameters. + """ + if expected_dns is None: + expected_dns = [] + + if expected_links is None: + expected_links = [] + + ctr6_raw_dns = self._get_ctr6_dn_list(ctr6) + + # filter out changes to the RID Set objects, as these can happen + # intermittently and mess up the test assertions + ctr6_dns = [] + for dn in ctr6_raw_dns: + if "CN=RID Set," in dn or "CN=RID Manager$," in dn: + print("Removing {0} from GetNCChanges reply".format(dn)) + else: + ctr6_dns.append(dn) + + self.assertEqual(len(ctr6_dns), len(expected_dns), + "Received unexpected objects (%s)" % ctr6_dns) + self.assertEqual(ctr6.object_count, len(ctr6_raw_dns)) + self.assertEqual(ctr6.linked_attributes_count, len(expected_links)) + self.assertEqual(ctr6.more_data, more_data) + self.assertEqual(ctr6.nc_object_count, nc_object_count) + self.assertEqual(ctr6.nc_linked_attributes_count, nc_linked_attributes_count) + self.assertEqual(ctr6.drs_error[0], drs_error) + + i = 0 + for dn in expected_dns: + # Expect them back in the exact same order as specified. + if dn_ordered: + self.assertNotEqual(ctr6_dns[i], None) + self.assertEqual(ctr6_dns[i], dn) + i = i + 1 + # Don't care what order + else: + self.assertTrue(dn in ctr6_dns, "Couldn't find DN '%s' anywhere in ctr6 response." % dn) + + # Extract the links from the response + ctr6_links = self._get_ctr6_links(ctr6) + expected_links.sort() + + lidx = 0 + for el in expected_links: + if links_ordered: + self.assertEqual(el, ctr6_links[lidx]) + lidx += 1 + else: + self.assertTrue(el in ctr6_links, "Couldn't find link '%s' anywhere in ctr6 response." % el) + + def _exop_req8(self, dest_dsa, invocation_id, nc_dn_str, exop, + replica_flags=0, max_objects=0, partial_attribute_set=None, + partial_attribute_set_ex=None, mapping_ctr=None, nc_guid=None): + req8 = drsuapi.DsGetNCChangesRequest8() + + req8.destination_dsa_guid = misc.GUID(dest_dsa) if dest_dsa else misc.GUID() + req8.source_dsa_invocation_id = misc.GUID(invocation_id) + req8.naming_context = drsuapi.DsReplicaObjectIdentifier() + req8.naming_context.dn = str(nc_dn_str) + if nc_guid is not None: + req8.naming_context.guid = nc_guid + req8.highwatermark = drsuapi.DsReplicaHighWaterMark() + req8.highwatermark.tmp_highest_usn = 0 + req8.highwatermark.reserved_usn = 0 + req8.highwatermark.highest_usn = 0 + req8.uptodateness_vector = None + req8.replica_flags = replica_flags + req8.max_object_count = max_objects + req8.max_ndr_size = 402116 + req8.extended_op = exop + req8.fsmo_info = 0 + req8.partial_attribute_set = partial_attribute_set + req8.partial_attribute_set_ex = partial_attribute_set_ex + if mapping_ctr: + req8.mapping_ctr = mapping_ctr + else: + req8.mapping_ctr.num_mappings = 0 + req8.mapping_ctr.mappings = None + + return req8 + + def _getnc_req10(self, dest_dsa, invocation_id, nc_dn_str, exop, + replica_flags=0, max_objects=0, partial_attribute_set=None, + partial_attribute_set_ex=None, mapping_ctr=None, + more_flags=0, nc_guid=None): + req10 = drsuapi.DsGetNCChangesRequest10() + + req10.destination_dsa_guid = misc.GUID(dest_dsa) if dest_dsa else misc.GUID() + req10.source_dsa_invocation_id = misc.GUID(invocation_id) + req10.naming_context = drsuapi.DsReplicaObjectIdentifier() + req10.naming_context.dn = str(nc_dn_str) + if nc_guid is not None: + req10.naming_context.guid = nc_guid + req10.highwatermark = drsuapi.DsReplicaHighWaterMark() + req10.highwatermark.tmp_highest_usn = 0 + req10.highwatermark.reserved_usn = 0 + req10.highwatermark.highest_usn = 0 + req10.uptodateness_vector = None + req10.replica_flags = replica_flags + req10.max_object_count = max_objects + req10.max_ndr_size = 402116 + req10.extended_op = exop + req10.fsmo_info = 0 + req10.partial_attribute_set = partial_attribute_set + req10.partial_attribute_set_ex = partial_attribute_set_ex + if mapping_ctr: + req10.mapping_ctr = mapping_ctr + else: + req10.mapping_ctr.num_mappings = 0 + req10.mapping_ctr.mappings = None + req10.more_flags = more_flags + + return req10 + + def _ds_bind(self, server_name, creds=None, ip=None): + if ip is None: + binding_str = f"ncacn_ip_tcp:{server_name}[seal]" + else: + binding_str = f"ncacn_ip_tcp:{ip}[seal,target_hostname={server_name}]" + + if creds is None: + creds = self.get_credentials() + drs = drsuapi.drsuapi(binding_str, self.get_loadparm(), creds) + (drs_handle, supported_extensions) = drs_DsBind(drs) + return (drs, drs_handle) + + def get_partial_attribute_set(self, attids=None): + if attids is None: + attids = [drsuapi.DRSUAPI_ATTID_objectClass] + partial_attribute_set = drsuapi.DsPartialAttributeSet() + partial_attribute_set.attids = attids + partial_attribute_set.num_attids = len(attids) + return partial_attribute_set + + +class AbstractLink: + def __init__(self, attid, flags, identifier, targetGUID, + targetDN=""): + self.attid = attid + self.flags = flags + self.identifier = str(identifier) + self.selfGUID_blob = ndr_pack(identifier) + self.targetGUID = str(targetGUID) + self.targetGUID_blob = ndr_pack(targetGUID) + self.targetDN = targetDN + + def __repr__(self): + return "AbstractLink(0x%08x, 0x%08x, %s, %s)" % ( + self.attid, self.flags, self.identifier, self.targetGUID) + + def __internal_cmp__(self, other, verbose=False): + """See CompareLinks() in MS-DRSR section 4.1.10.5.17""" + if not isinstance(other, AbstractLink): + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => wrong type" % (self, other)) + return NotImplemented + + c = cmp(self.selfGUID_blob, other.selfGUID_blob) + if c != 0: + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => %d different identifier" % (self, other, c)) + return c + + c = other.attid - self.attid + if c != 0: + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => %d different attid" % (self, other, c)) + return c + + self_active = self.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + other_active = other.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + + c = self_active - other_active + if c != 0: + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => %d different FLAG_ACTIVE" % (self, other, c)) + return c + + c = cmp(self.targetGUID_blob, other.targetGUID_blob) + if c != 0: + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => %d different target" % (self, other, c)) + return c + + c = self.flags - other.flags + if c != 0: + if verbose: + print("AbstractLink.__internal_cmp__(%r, %r) => %d different flags" % (self, other, c)) + return c + + return 0 + + def __lt__(self, other): + c = self.__internal_cmp__(other) + if c == NotImplemented: + return NotImplemented + if c < 0: + return True + return False + + def __le__(self, other): + c = self.__internal_cmp__(other) + if c == NotImplemented: + return NotImplemented + if c <= 0: + return True + return False + + def __eq__(self, other): + c = self.__internal_cmp__(other, verbose=True) + if c == NotImplemented: + return NotImplemented + if c == 0: + return True + return False + + def __ne__(self, other): + c = self.__internal_cmp__(other) + if c == NotImplemented: + return NotImplemented + if c != 0: + return True + return False + + def __gt__(self, other): + c = self.__internal_cmp__(other) + if c == NotImplemented: + return NotImplemented + if c > 0: + return True + return False + + def __ge__(self, other): + c = self.__internal_cmp__(other) + if c == NotImplemented: + return NotImplemented + if c >= 0: + return True + return False + + def __hash__(self): + return hash((self.attid, self.flags, self.identifier, self.targetGUID)) diff --git a/source4/torture/drs/python/fsmo.py b/source4/torture/drs/python/fsmo.py new file mode 100644 index 0000000..55805b9 --- /dev/null +++ b/source4/torture/drs/python/fsmo.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Anatoliy Atanasov 2010 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN fsmo -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import sys +import time +import os + +sys.path.insert(0, "bin/python") + +from ldb import SCOPE_BASE + +import drs_base + + +class DrsFsmoTestCase(drs_base.DrsBaseTestCase): + + def setUp(self): + super(DrsFsmoTestCase, self).setUp() + + # we have to wait for the replication before we make the check + self.fsmo_wait_max_time = 20 + self.fsmo_wait_sleep_time = 0.2 + + # cache some of RootDSE props + self.dsServiceName_dc1 = self.info_dc1["dsServiceName"][0] + self.dsServiceName_dc2 = self.info_dc2["dsServiceName"][0] + self.infrastructure_dn = "CN=Infrastructure," + self.domain_dn + self.naming_dn = "CN=Partitions," + self.config_dn + self.rid_dn = "CN=RID Manager$,CN=System," + self.domain_dn + self.domain_dns_dn = ( + "CN=Infrastructure,DC=DomainDnsZones, %s" % self.domain_dn ) + self.forest_dns_dn = ( + "CN=Infrastructure,DC=ForestDnsZones, %s" % self.domain_dn ) + + def tearDown(self): + super(DrsFsmoTestCase, self).tearDown() + + def _net_fsmo_role_transfer(self, DC, role, noop=False): + # make command line credentials string + ccache_name = self.get_creds_ccache_name() + cmd_line_auth = "--use-krb5-ccache=%s" % ccache_name + (result, out, err) = self.runsubcmd("fsmo", "transfer", + "--role=%s" % role, + "-H", "ldap://%s:389" % DC, + cmd_line_auth) + + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + if not noop: + self.assertTrue("FSMO transfer of '%s' role successful" % role in out) + else: + self.assertTrue("This DC already has the '%s' FSMO role" % role in out) + + def _wait_for_role_transfer(self, ldb_dc, role_dn, master): + """Wait for role transfer for certain amount of time + + :return: (Result=True|False, CurrentMasterDnsName) tuple + """ + cur_master = '' + retries = int(self.fsmo_wait_max_time / self.fsmo_wait_sleep_time) + 1 + for i in range(0, retries): + # check if master has been transferred + res = ldb_dc.search(role_dn, + scope=SCOPE_BASE, attrs=["fSMORoleOwner"]) + assert len(res) == 1, "Only one fSMORoleOwner value expected!" + cur_master = res[0]["fSMORoleOwner"][0] + if master == cur_master: + return (True, cur_master) + # skip last sleep, if no need to wait anymore + if i != (retries - 1): + # wait a little bit before next retry + time.sleep(self.fsmo_wait_sleep_time) + return (False, cur_master) + + def _role_transfer(self, role, role_dn): + """Triggers transfer of role from DC1 to DC2 + and vice versa so the role goes back to the original dc""" + # dc2 gets the role from dc1 + print("Testing for %s role transfer from %s to %s" % (role, self.dnsname_dc1, self.dnsname_dc2)) + + self._net_fsmo_role_transfer(DC=self.dnsname_dc2, role=role) + # check if the role is transferred + (res, master) = self._wait_for_role_transfer(ldb_dc=self.ldb_dc2, + role_dn=role_dn, + master=self.dsServiceName_dc2) + self.assertTrue(res, + "Transferring %s role to %s has failed, master is: %s!" % (role, self.dsServiceName_dc2, master)) + + # dc1 gets back the role from dc2 + print("Testing for %s role transfer from %s to %s" % (role, self.dnsname_dc2, self.dnsname_dc1)) + self._net_fsmo_role_transfer(DC=self.dnsname_dc1, role=role) + # check if the role is transferred + (res, master) = self._wait_for_role_transfer(ldb_dc=self.ldb_dc1, + role_dn=role_dn, + master=self.dsServiceName_dc1) + self.assertTrue(res, + "Transferring %s role to %s has failed, master is: %s!" % (role, self.dsServiceName_dc1, master)) + + # dc1 keeps the role + print("Testing for no-op %s role transfer from %s to %s" % (role, self.dnsname_dc2, self.dnsname_dc1)) + self._net_fsmo_role_transfer(DC=self.dnsname_dc1, role=role, noop=True) + # check if the role is transferred + (res, master) = self._wait_for_role_transfer(ldb_dc=self.ldb_dc1, + role_dn=role_dn, + master=self.dsServiceName_dc1) + self.assertTrue(res, + "Transferring %s role to %s has failed, master is: %s!" % (role, self.dsServiceName_dc1, master)) + + def test_SchemaMasterTransfer(self): + self._role_transfer(role="schema", role_dn=self.schema_dn) + + def test_InfrastructureMasterTransfer(self): + self._role_transfer(role="infrastructure", role_dn=self.infrastructure_dn) + + def test_PDCMasterTransfer(self): + self._role_transfer(role="pdc", role_dn=self.domain_dn) + + def test_RIDMasterTransfer(self): + self._role_transfer(role="rid", role_dn=self.rid_dn) + + def test_NamingMasterTransfer(self): + self._role_transfer(role="naming", role_dn=self.naming_dn) + + def test_DomainDnsZonesMasterTransfer(self): + self._role_transfer(role="domaindns", role_dn=self.domain_dns_dn) + + def test_ForestDnsZonesMasterTransfer(self): + self._role_transfer(role="forestdns", role_dn=self.forest_dns_dn) diff --git a/source4/torture/drs/python/getnc_exop.py b/source4/torture/drs/python/getnc_exop.py new file mode 100644 index 0000000..0f12d9b --- /dev/null +++ b/source4/torture/drs/python/getnc_exop.py @@ -0,0 +1,1304 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests various schema replication scenarios +# +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN getnc_exop -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import random + +import drs_base +from drs_base import AbstractLink + +import samba.tests +from samba import werror, WERRORError + +import ldb +from ldb import SCOPE_BASE + +from samba.dcerpc import drsuapi, misc, drsblobs +from samba.drs_utils import drs_DsBind +from samba.ndr import ndr_unpack, ndr_pack +from functools import cmp_to_key +from samba.common import cmp + + +def _linked_attribute_compare(la1, la2): + """See CompareLinks() in MS-DRSR section 4.1.10.5.17""" + la1, la1_target = la1 + la2, la2_target = la2 + + # Ascending host object GUID + c = cmp(ndr_pack(la1.identifier.guid), ndr_pack(la2.identifier.guid)) + if c != 0: + return c + + # Ascending attribute ID + if la1.attid != la2.attid: + return -1 if la1.attid < la2.attid else 1 + + la1_active = la1.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + la2_active = la2.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + + # Ascending 'is present' + if la1_active != la2_active: + return 1 if la1_active else -1 + + # Ascending target object GUID + return cmp(ndr_pack(la1_target), ndr_pack(la2_target)) + + +class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase): + """Intended as a semi-black box test case for DsGetNCChanges + implementation for extended operations. It should be testing + how DsGetNCChanges handles different input params (mostly invalid). + Final goal is to make DsGetNCChanges as binary compatible to + Windows implementation as possible""" + + def setUp(self): + super(DrsReplicaSyncTestCase, self).setUp() + self.base_dn = self.ldb_dc1.get_default_basedn() + self.ou = "OU=test_getncchanges%d,%s" % (random.randint(0, 4294967295), + self.base_dn) + self.ldb_dc1.add({ + "dn": self.ou, + "objectclass": "organizationalUnit"}) + (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + (self.default_hwm, self.default_utdv) = self._get_highest_hwm_utdv(self.ldb_dc1) + + def tearDown(self): + try: + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + except ldb.LdbError as e: + (enum, string) = e.args + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + super(DrsReplicaSyncTestCase, self).tearDown() + + def _determine_fSMORoleOwner(self, fsmo_obj_dn): + """Returns (owner, not_owner) pair where: + owner: dns name for FSMO owner + not_owner: dns name for DC not owning the FSMO""" + # collect info to return later + fsmo_info_1 = {"dns_name": self.dnsname_dc1, + "invocation_id": self.ldb_dc1.get_invocation_id(), + "ntds_guid": self.ldb_dc1.get_ntds_GUID(), + "server_dn": self.ldb_dc1.get_serverName()} + fsmo_info_2 = {"dns_name": self.dnsname_dc2, + "invocation_id": self.ldb_dc2.get_invocation_id(), + "ntds_guid": self.ldb_dc2.get_ntds_GUID(), + "server_dn": self.ldb_dc2.get_serverName()} + + msgs = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=fsmo_info_1["server_dn"], attrs=["serverReference"]) + fsmo_info_1["server_acct_dn"] = ldb.Dn(self.ldb_dc1, msgs[0]["serverReference"][0].decode('utf8')) + fsmo_info_1["rid_set_dn"] = ldb.Dn(self.ldb_dc1, "CN=RID Set") + fsmo_info_1["server_acct_dn"] + + msgs = self.ldb_dc2.search(scope=ldb.SCOPE_BASE, base=fsmo_info_2["server_dn"], attrs=["serverReference"]) + fsmo_info_2["server_acct_dn"] = ldb.Dn(self.ldb_dc2, msgs[0]["serverReference"][0].decode('utf8')) + fsmo_info_2["rid_set_dn"] = ldb.Dn(self.ldb_dc2, "CN=RID Set") + fsmo_info_2["server_acct_dn"] + + # determine the owner dc + res = self.ldb_dc1.search(fsmo_obj_dn, + scope=SCOPE_BASE, attrs=["fSMORoleOwner"]) + assert len(res) == 1, "Only one fSMORoleOwner value expected for %s!" % fsmo_obj_dn + fsmo_owner = res[0]["fSMORoleOwner"][0] + if fsmo_owner == self.info_dc1["dsServiceName"][0]: + return (fsmo_info_1, fsmo_info_2) + return (fsmo_info_2, fsmo_info_1) + + def _check_exop_failed(self, ctr6, expected_failure): + self.assertEqual(ctr6.extended_ret, expected_failure) + #self.assertEqual(ctr6.object_count, 0) + #self.assertEqual(ctr6.first_object, None) + self.assertEqual(ctr6.more_data, False) + self.assertEqual(ctr6.nc_object_count, 0) + self.assertEqual(ctr6.nc_linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes, []) + self.assertEqual(ctr6.drs_error[0], 0) + + def test_do_single_repl(self): + """ + Make sure that DRSUAPI_EXOP_REPL_OBJ never replicates more than + one object, even when we use DRS_GET_ANC/GET_TGT. + """ + + ou1 = "OU=get_anc1,%s" % self.ou + self.ldb_dc1.add({ + "dn": ou1, + "objectclass": "organizationalUnit" + }) + ou1_id = self._get_identifier(self.ldb_dc1, ou1) + ou2 = "OU=get_anc2,%s" % ou1 + self.ldb_dc1.add({ + "dn": ou2, + "objectclass": "organizationalUnit" + }) + ou2_id = self._get_identifier(self.ldb_dc1, ou2) + dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2) + self.ldb_dc1.add({ + "dn": dc3, + "objectclass": "computer", + "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT) + }) + dc3_id = self._get_identifier(self.ldb_dc1, dc3) + + # Add some linked attributes (for checking GET_TGT behaviour) + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc2, ou1) + m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_ADD, "managedBy") + self.ldb_dc1.modify(m) + ou1_link = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + ou1_id.guid, ou2_id.guid) + + m.dn = ldb.Dn(self.ldb_dc2, dc3) + m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_ADD, "managedBy") + self.ldb_dc1.modify(m) + dc3_link = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + dc3_id.guid, ou2_id.guid) + + req = self._getnc_req10(dest_dsa=None, + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=ou1, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req) + self._check_ctr6(ctr, [ou1], expected_links=[ou1_link]) + + # DRSUAPI_DRS_WRIT_REP means that we should only replicate the dn we give (dc3). + # DRSUAPI_DRS_GET_ANC means that we should also replicate its ancestors, but + # Windows doesn't do this if we use both. + req = self._getnc_req10(dest_dsa=None, + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=dc3, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req) + self._check_ctr6(ctr, [dc3], expected_links=[dc3_link]) + + # Even though the ancestor of ou2 (ou1) has changed since last hwm, and we're + # sending DRSUAPI_DRS_GET_ANC, the expected response is that it will only try + # and replicate the single object still. + req = self._getnc_req10(dest_dsa=None, + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=ou2, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req) + self._check_ctr6(ctr, [ou2]) + + def test_do_full_repl_on_ou(self): + """ + Make sure that a full replication on a not-an-nc fails with + the right error code + """ + + non_nc_ou = "OU=not-an-NC,%s" % self.ou + self.ldb_dc1.add({ + "dn": non_nc_ou, + "objectclass": "organizationalUnit" + }) + req8 = self._exop_req8(dest_dsa=None, + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=non_nc_ou, + exop=drsuapi.DRSUAPI_EXOP_NONE, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP) + + try: + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8) + self.fail("Expected DsGetNCChanges to fail with WERR_DS_CANT_FIND_EXPECTED_NC") + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, werror.WERR_DS_CANT_FIND_EXPECTED_NC) + + def test_InvalidNC_DummyDN_InvalidGUID_REPL_OBJ(self): + """Test single object replication on a totally invalid GUID fails with the right error code""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, werror.WERR_DS_DRA_BAD_DN) + + def test_InvalidNC_DummyDN_InvalidGUID_REPL_SECRET(self): + """Test single object replication on a totally invalid GUID fails with the right error code""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, werror.WERR_DS_DRA_BAD_DN) + + def test_InvalidNC_DummyDN_InvalidGUID_RID_ALLOC(self): + """Test RID Allocation on a totally invalid GUID fails with the right error code""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC) + + (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, werror.WERR_DS_DRA_BAD_NC) + + def test_valid_GUID_only_REPL_OBJ(self): + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE, + attrs=["objectGUID"]) + + guid = misc.GUID(res[0]["objectGUID"][0]) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str="", + nc_guid=guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"Failed to call GetNCChanges with EXOP_REPL_OBJ and a GUID: {estr}") + + self.assertEqual(ctr.first_object.object.identifier.guid, guid) + + def test_DummyDN_valid_GUID_REPL_OBJ(self): + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE, + attrs=["objectGUID"]) + + guid = misc.GUID(res[0]["objectGUID"][0]) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str="DummyDN", + nc_guid=guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"Failed to call GetNCChanges with EXOP_REPL_OBJ, DummyDN and a GUID: {estr}") + + self.assertEqual(ctr.first_object.object.identifier.guid, guid) + + def test_DummyDN_valid_GUID_REPL_SECRET(self): + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE, + attrs=["objectGUID"]) + + guid = misc.GUID(res[0]["objectGUID"][0]) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str="DummyDN", + nc_guid=guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + + # We expect to get as far as failing on the missing dest_dsa + self.assertEqual(enum, werror.WERR_DS_DRA_DB_ERROR) + + def test_link_utdv_hwm(self): + """Test verify the DRS_GET_ANC behavior.""" + + ou1 = "OU=get_anc1,%s" % self.ou + self.ldb_dc1.add({ + "dn": ou1, + "objectclass": "organizationalUnit" + }) + ou1_id = self._get_identifier(self.ldb_dc1, ou1) + ou2 = "OU=get_anc2,%s" % ou1 + self.ldb_dc1.add({ + "dn": ou2, + "objectclass": "organizationalUnit" + }) + ou2_id = self._get_identifier(self.ldb_dc1, ou2) + dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2) + self.ldb_dc1.add({ + "dn": dc3, + "objectclass": "computer", + "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT) + }) + dc3_id = self._get_identifier(self.ldb_dc1, dc3) + + (hwm1, utdv1) = self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, ou1) + m["displayName"] = ldb.MessageElement("OU1", ldb.FLAG_MOD_ADD, "displayName") + self.ldb_dc1.modify(m) + + (hwm2, utdv2) = self._check_replication([ou2, dc3, ou1], + drsuapi.DRSUAPI_DRS_WRIT_REP) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([ou1], + drsuapi.DRSUAPI_DRS_WRIT_REP, + highwatermark=hwm1) + + self._check_replication([ou1], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + highwatermark=hwm1) + + self._check_replication([ou1], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + uptodateness_vector=utdv1) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, ou2) + m["displayName"] = ldb.MessageElement("OU2", ldb.FLAG_MOD_ADD, "displayName") + self.ldb_dc1.modify(m) + + (hwm3, utdv3) = self._check_replication([dc3, ou1, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([ou1, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP, + highwatermark=hwm1) + + self._check_replication([ou1, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + highwatermark=hwm1) + + self._check_replication([ou1, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + uptodateness_vector=utdv1) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, self.ou) + m["displayName"] = ldb.MessageElement("OU", ldb.FLAG_MOD_ADD, "displayName") + self.ldb_dc1.modify(m) + + (hwm4, utdv4) = self._check_replication([dc3, ou1, ou2, self.ou], + drsuapi.DRSUAPI_DRS_WRIT_REP) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([self.ou, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + uptodateness_vector=utdv2) + + cn3 = "CN=get_anc3,%s" % ou2 + self.ldb_dc1.add({ + "dn": cn3, + "objectclass": "container", + }) + + (hwm5, utdv5) = self._check_replication([dc3, ou1, ou2, self.ou, cn3], + drsuapi.DRSUAPI_DRS_WRIT_REP) + + self._check_replication([self.ou, ou1, ou2, dc3, cn3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, ou2) + m["managedBy"] = ldb.MessageElement(dc3, ldb.FLAG_MOD_ADD, "managedBy") + self.ldb_dc1.modify(m) + ou2_managedBy_dc3 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + ou2_id.guid, dc3_id.guid) + + (hwm6, utdv6) = self._check_replication([dc3, ou1, self.ou, cn3, ou2], + drsuapi.DRSUAPI_DRS_WRIT_REP, + expected_links=[ou2_managedBy_dc3]) + + # Can fail against Windows due to equal precedence of dc3, cn3 + self._check_replication([self.ou, ou1, ou2, dc3, cn3], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[ou2_managedBy_dc3]) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_WRIT_REP, + uptodateness_vector=utdv5, + expected_links=[ou2_managedBy_dc3]) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + uptodateness_vector=utdv5) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + uptodateness_vector=utdv5) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, dc3) + m["managedBy"] = ldb.MessageElement(ou1, ldb.FLAG_MOD_ADD, "managedBy") + self.ldb_dc1.modify(m) + dc3_managedBy_ou1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + dc3_id.guid, ou1_id.guid) + + (hwm7, utdv7) = self._check_replication([ou1, self.ou, cn3, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP, + expected_links=[ou2_managedBy_dc3, dc3_managedBy_ou1]) + + # Can fail against Windows due to equal precedence of dc3, cn3 + # self._check_replication([self.ou,ou1,ou2,dc3,cn3], + # drsuapi.DRSUAPI_DRS_WRIT_REP| + # drsuapi.DRSUAPI_DRS_GET_ANC, + # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1]) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + expected_links=[dc3_managedBy_ou1]) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + expected_links=[dc3_managedBy_ou1]) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1]) + + # GET_TGT seems to override DRS_CRITICAL_ONLY and also returns any + # object(s) that relate to the linked attributes (similar to GET_ANC) + self._check_replication([ou1, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT, + expected_links=[dc3_managedBy_ou1], dn_ordered=False) + + # Change DC3's managedBy to OU2 instead of OU1 + # Note that the OU1 managedBy linked attribute will still exist as + # a tombstone object (and so will be returned in the replication still) + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, dc3) + m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_REPLACE, "managedBy") + self.ldb_dc1.modify(m) + dc3_managedBy_ou1.flags &= ~drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + dc3_managedBy_ou2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + dc3_id.guid, ou2_id.guid) + + (hwm8, utdv8) = self._check_replication([ou1, self.ou, cn3, ou2, dc3], + drsuapi.DRSUAPI_DRS_WRIT_REP, + expected_links=[ou2_managedBy_dc3, dc3_managedBy_ou1, dc3_managedBy_ou2]) + + # Can fail against Windows due to equal precedence of dc3, cn3 + # self._check_replication([self.ou,ou1,ou2,dc3,cn3], + # drsuapi.DRSUAPI_DRS_WRIT_REP| + # drsuapi.DRSUAPI_DRS_GET_ANC, + # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2]) + + self._check_replication([dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2]) + + self._check_replication([self.ou, ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2]) + + # GET_TGT will also return any DNs referenced by the linked attributes + # (including the Tombstone attribute) + self._check_replication([ou1, ou2, dc3], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], dn_ordered=False) + + # Use the highwater-mark prior to changing ManagedBy - this should + # only return the old/Tombstone and new linked attributes (we already + # know all the DNs) + self._check_replication([], + drsuapi.DRSUAPI_DRS_WRIT_REP, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + highwatermark=hwm7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + highwatermark=hwm7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + highwatermark=hwm7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + highwatermark=hwm7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + highwatermark=hwm7) + + # Repeat the above set of tests using the uptodateness_vector + # instead of the highwater-mark + self._check_replication([], + drsuapi.DRSUAPI_DRS_WRIT_REP, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + uptodateness_vector=utdv7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + uptodateness_vector=utdv7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + uptodateness_vector=utdv7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | + drsuapi.DRSUAPI_DRS_GET_ANC, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + uptodateness_vector=utdv7) + + self._check_replication([], + drsuapi.DRSUAPI_DRS_CRITICAL_ONLY, + more_flags=drsuapi.DRSUAPI_DRS_GET_TGT, + expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], + uptodateness_vector=utdv7) + + def test_FSMONotOwner(self): + """Test role transfer with against DC not owner of the role""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa=fsmo_owner["ntds_guid"], + invocation_id=fsmo_not_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE) + + (drs, drs_handle) = self._ds_bind(fsmo_not_owner["dns_name"]) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER) + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_not_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_not_owner["invocation_id"])) + + def test_InvalidDestDSA(self): + """Test role transfer with invalid destination DSA guid""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER) + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + + def test_InvalidDestDSA_and_GUID(self): + """Test role transfer with invalid destination DSA guid""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"DsGetNCChanges failed with {estr}") + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER) + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + + def test_InvalidDestDSA_and_GUID_RID_ALLOC(self): + """Test role transfer with invalid destination DSA guid""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"DsGetNCChanges failed with {estr}") + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER) + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + + +class DrsReplicaPrefixMapTestCase(drs_base.DrsBaseTestCase): + def setUp(self): + super(DrsReplicaPrefixMapTestCase, self).setUp() + self.base_dn = self.ldb_dc1.get_default_basedn() + self.ou = "ou=pfm_exop%d,%s" % (random.randint(0, 4294967295), + self.base_dn) + self.ldb_dc1.add({ + "dn": self.ou, + "objectclass": "organizationalUnit"}) + self.user = "cn=testuser,%s" % self.ou + self.ldb_dc1.add({ + "dn": self.user, + "objectclass": "user"}) + + def tearDown(self): + super(DrsReplicaPrefixMapTestCase, self).tearDown() + try: + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + except ldb.LdbError as e2: + (enum, string) = e2.args + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + + def test_missing_prefix_map_dsa(self): + partial_attribute_set = self.get_partial_attribute_set() + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(ctr.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS) + except RuntimeError: + self.fail("Missing prefixmap shouldn't have triggered an error") + + def test_invalid_prefix_map_attid(self): + # Request for invalid attid + partial_attribute_set = self.get_partial_attribute_set([99999]) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + try: + pfm = self._samdb_fetch_pfm_and_schi() + except KeyError: + # On Windows, prefixMap isn't available over LDAP + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + pfm = ctr.mapping_ctr + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + mapping_ctr=pfm) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.fail("Invalid attid (99999) should have triggered an error") + except RuntimeError as e3: + (ecode, emsg) = e3.args + self.assertEqual(ecode, 0x000020E2, "Error code should have been " + "WERR_DS_DRA_SCHEMA_MISMATCH") + + def test_secret_prefix_map_attid(self): + # Request for a secret attid + partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd]) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + try: + pfm = self._samdb_fetch_pfm_and_schi() + except KeyError: + # On Windows, prefixMap isn't available over LDAP + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + pfm = ctr.mapping_ctr + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd: + found = True + break + + self.assertTrue(found, "Ensure we get the unicodePwd attribute back") + + for i, mapping in enumerate(pfm.mappings): + # OID: 2.5.4.* + # objectClass: 2.5.4.0 + if mapping.oid.binary_oid == [85, 4]: + idx1 = i + # OID: 1.2.840.113556.1.4.* + # unicodePwd: 1.2.840.113556.1.4.90 + elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]: + idx2 = i + + (pfm.mappings[idx1].id_prefix, + pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix, + pfm.mappings[idx1].id_prefix) + + tmp = pfm.mappings + tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1] + pfm.mappings = tmp + + # 90 for unicodePwd (with new prefix = 0) + # 589824, 589827 for objectClass and CN + # Use of three ensures sorting is correct + partial_attribute_set = self.get_partial_attribute_set([90, 589824, 589827]) + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd: + found = True + break + + self.assertTrue(found, "Ensure we get the unicodePwd attribute back") + + def test_regular_prefix_map_attid(self): + # Request for a regular (non-secret) attid + partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name]) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + try: + pfm = self._samdb_fetch_pfm_and_schi() + except KeyError: + # On Windows, prefixMap isn't available over LDAP + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + pfm = ctr.mapping_ctr + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_name: + found = True + break + + self.assertTrue(found, "Ensure we get the name attribute back") + + for i, mapping in enumerate(pfm.mappings): + # OID: 2.5.4.* + # objectClass: 2.5.4.0 + if mapping.oid.binary_oid == [85, 4]: + idx1 = i + # OID: 1.2.840.113556.1.4.* + # name: 1.2.840.113556.1.4.1 + elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]: + idx2 = i + + (pfm.mappings[idx1].id_prefix, + pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix, + pfm.mappings[idx1].id_prefix) + + tmp = pfm.mappings + tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1] + pfm.mappings = tmp + + # 1 for name (with new prefix = 0) + partial_attribute_set = self.get_partial_attribute_set([1]) + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_name: + found = True + break + + self.assertTrue(found, "Ensure we get the name attribute back") + + def test_regular_prefix_map_ex_attid(self): + # Request for a regular (non-secret) attid + partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name]) + partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd]) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + try: + pfm = self._samdb_fetch_pfm_and_schi() + except KeyError: + # On Windows, prefixMap isn't available over LDAP + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + pfm = ctr.mapping_ctr + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + partial_attribute_set_ex=partial_attribute_set_ex, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_name: + found = True + break + + self.assertTrue(found, "Ensure we get the name attribute back") + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd: + found = True + break + + self.assertTrue(found, "Ensure we get the unicodePwd attribute back") + + for i, mapping in enumerate(pfm.mappings): + # OID: 2.5.4.* + # objectClass: 2.5.4.0 + if mapping.oid.binary_oid == [85, 4]: + idx1 = i + # OID: 1.2.840.113556.1.4.* + # name: 1.2.840.113556.1.4.1 + # unicodePwd: 1.2.840.113556.1.4.90 + elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]: + idx2 = i + + (pfm.mappings[idx1].id_prefix, + pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix, + pfm.mappings[idx1].id_prefix) + + tmp = pfm.mappings + tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1] + pfm.mappings = tmp + + # 1 for name (with new prefix = 0) + partial_attribute_set = self.get_partial_attribute_set([1]) + # 90 for unicodePwd (with new prefix = 0) + # HOWEVER: Windows doesn't seem to respect incoming maps for PartialAttrSetEx + partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd]) + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.user, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + partial_attribute_set=partial_attribute_set, + partial_attribute_set_ex=partial_attribute_set_ex, + mapping_ctr=pfm) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_name: + found = True + break + + self.assertTrue(found, "Ensure we get the name attribute back") + + found = False + for attr in ctr.first_object.object.attribute_ctr.attributes: + if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd: + found = True + break + + self.assertTrue(found, "Ensure we get the unicodePwd attribute back") + + def _samdb_fetch_pfm_and_schi(self): + """Fetch prefixMap and schemaInfo stored in SamDB using LDB connection""" + samdb = self.ldb_dc1 + res = samdb.search(base=samdb.get_schema_basedn(), scope=SCOPE_BASE, + attrs=["prefixMap", "schemaInfo"]) + + pfm = ndr_unpack(drsblobs.prefixMapBlob, + res[0]['prefixMap'][0]) + + schi = drsuapi.DsReplicaOIDMapping() + schi.id_prefix = 0 + if 'schemaInfo' in res[0]: + binary_oid = [x if isinstance(x, int) else ord(x) for x in res[0]['schemaInfo'][0]] + schi.oid.length = len(binary_oid) + schi.oid.binary_oid = binary_oid + else: + schema_info = drsblobs.schemaInfoBlob() + schema_info.revision = 0 + schema_info.marker = 0xFF + schema_info.invocation_id = misc.GUID(samdb.get_invocation_id()) + + binary_oid = [x if isinstance(x, int) else ord(x) for x in ndr_pack(schema_info)] + # you have to set the length before setting binary_oid + schi.oid.length = len(binary_oid) + schi.oid.binary_oid = binary_oid + + pfm.ctr.mappings = pfm.ctr.mappings + [schi] + pfm.ctr.num_mappings += 1 + return pfm.ctr + + +class DrsReplicaSyncSortTestCase(drs_base.DrsBaseTestCase): + def setUp(self): + super(DrsReplicaSyncSortTestCase, self).setUp() + self.base_dn = self.ldb_dc1.get_default_basedn() + self.ou = "ou=sort_exop%d,%s" % (random.randint(0, 4294967295), + self.base_dn) + self.ldb_dc1.add({ + "dn": self.ou, + "objectclass": "organizationalUnit"}) + + def tearDown(self): + super(DrsReplicaSyncSortTestCase, self).tearDown() + # tidyup groups and users + try: + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + except ldb.LdbError as e4: + (enum, string) = e4.args + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + + def add_linked_attribute(self, src, dest, attr='member'): + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, src) + m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_ADD, attr) + self.ldb_dc1.modify(m) + + def remove_linked_attribute(self, src, dest, attr='member'): + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, src) + m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_DELETE, attr) + self.ldb_dc1.modify(m) + + def test_sort_behaviour_single_object(self): + """Testing sorting behaviour on single objects""" + + user1_dn = "cn=test_user1,%s" % self.ou + user2_dn = "cn=test_user2,%s" % self.ou + user3_dn = "cn=test_user3,%s" % self.ou + group_dn = "cn=test_group,%s" % self.ou + + self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"}) + self.ldb_dc1.add({"dn": user2_dn, "objectclass": "user"}) + self.ldb_dc1.add({"dn": user3_dn, "objectclass": "user"}) + self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"}) + + u1_guid = misc.GUID(self.ldb_dc1.search(base=user1_dn, + attrs=["objectGUID"])[0]['objectGUID'][0]) + u2_guid = misc.GUID(self.ldb_dc1.search(base=user2_dn, + attrs=["objectGUID"])[0]['objectGUID'][0]) + u3_guid = misc.GUID(self.ldb_dc1.search(base=user3_dn, + attrs=["objectGUID"])[0]['objectGUID'][0]) + g_guid = misc.GUID(self.ldb_dc1.search(base=group_dn, + attrs=["objectGUID"])[0]['objectGUID'][0]) + + self.add_linked_attribute(group_dn, user1_dn, + attr='member') + self.add_linked_attribute(group_dn, user2_dn, + attr='member') + self.add_linked_attribute(group_dn, user3_dn, + attr='member') + self.add_linked_attribute(group_dn, user1_dn, + attr='managedby') + self.add_linked_attribute(group_dn, user2_dn, + attr='nonSecurityMember') + self.add_linked_attribute(group_dn, user3_dn, + attr='nonSecurityMember') + + set_inactive = AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, u3_guid) + + expected_links = set([set_inactive, + AbstractLink(drsuapi.DRSUAPI_ATTID_member, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, + u1_guid), + AbstractLink(drsuapi.DRSUAPI_ATTID_member, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, + u2_guid), + AbstractLink(drsuapi.DRSUAPI_ATTID_member, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, + u3_guid), + AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, + u1_guid), + AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + g_guid, + u2_guid), + ]) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=group_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + no_inactive = [] + for link in ctr.linked_attributes: + target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + link.value.blob).guid + no_inactive.append((link, target_guid)) + self.assertTrue(AbstractLink(link.attid, link.flags, + link.identifier.guid, + target_guid) in expected_links) + + no_inactive.sort(key=cmp_to_key(_linked_attribute_compare)) + + # assert the two arrays are the same + self.assertEqual(len(expected_links), ctr.linked_attributes_count) + self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes) + + self.remove_linked_attribute(group_dn, user3_dn, + attr='nonSecurityMember') + + # Set the link inactive + expected_links.remove(set_inactive) + set_inactive.flags = 0 + expected_links.add(set_inactive) + + has_inactive = [] + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + for link in ctr.linked_attributes: + target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + link.value.blob).guid + has_inactive.append((link, target_guid)) + self.assertTrue(AbstractLink(link.attid, link.flags, + link.identifier.guid, + target_guid) in expected_links) + + has_inactive.sort(key=cmp_to_key(_linked_attribute_compare)) + + # assert the two arrays are the same + self.assertEqual(len(expected_links), ctr.linked_attributes_count) + self.assertEqual([x[0] for x in has_inactive], ctr.linked_attributes) + + def test_sort_behaviour_ncchanges(self): + """Testing sorting behaviour on a group of objects.""" + user1_dn = "cn=test_user1,%s" % self.ou + group_dn = "cn=test_group,%s" % self.ou + self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"}) + self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"}) + + self.add_linked_attribute(group_dn, user1_dn, + attr='member') + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + # Make sure the max objects count is high enough + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.base_dn, + replica_flags=0, + max_objects=100, + exop=drsuapi.DRSUAPI_EXOP_NONE) + + # Loop until we get linked attributes, or we get to the end. + # Samba sends linked attributes at the end, unlike Windows. + while True: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + if ctr.more_data == 0 or ctr.linked_attributes_count != 0: + break + req8.highwatermark = ctr.new_highwatermark + + self.assertTrue(ctr.linked_attributes_count != 0) + + no_inactive = [] + for link in ctr.linked_attributes: + try: + target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + link.value.blob).guid + except: + target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary, + link.value.blob).guid + no_inactive.append((link, target_guid)) + + no_inactive.sort(key=cmp_to_key(_linked_attribute_compare)) + + # assert the two arrays are the same + self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes) diff --git a/source4/torture/drs/python/getnc_schema.py b/source4/torture/drs/python/getnc_schema.py new file mode 100644 index 0000000..60062f9 --- /dev/null +++ b/source4/torture/drs/python/getnc_schema.py @@ -0,0 +1,304 @@ +import drs_base +import ldb +import time +import random +import os + +break_me = os.getenv("PLEASE_BREAK_MY_WINDOWS") == "1" +assert break_me, ("This test breaks Windows active directory after " + "a few runs. Set PLEASE_BREAK_MY_WINDOWS=1 to run.") + +# This test runs against Windows. To run, set up two Windows AD DCs, join one +# to the other, and make sure the passwords are the same. SMB_CONF_PATH must +# also be set to any smb.conf file. Set DC1 to the PDC's hostname, and DC2 to +# the join'd DC's hostname. Example: +# PLEASE_BREAK_MY_WINDOWS=1 +# DC1=pdc DC2=joindc +# SMB_CONF_PATH=st/ad_dc/etc/smb.conf +# PYTHONPATH=$PYTHONPATH:./source4/torture/drs/python +# python3 ./source4/scripting/bin/subunitrun getnc_schema +# -UAdministrator%Password + +class SchemaReplicationTests(drs_base.DrsBaseTestCase): + + def setUp(self): + super(SchemaReplicationTests, self).setUp() + self.creds = self.get_credentials() + self.cmdline_auth = "-U{}%{}".format(self.creds.get_username(), + self.creds.get_password()) + + self.from_ldb = self.ldb_dc1 + self.dest_ldb = self.ldb_dc2 + self._disable_inbound_repl(self.url_dc1) + self._disable_all_repl(self.url_dc1) + self.free_offset = 0 + + def tearDown(self): + self._enable_inbound_repl(self.url_dc1) + self._enable_all_repl(self.url_dc1) + + def do_repl(self, partition_dn): + self._enable_inbound_repl(self.url_dc1) + self._enable_all_repl(self.url_dc1) + + samba_tool_cmd = ["drs", "replicate", self.url_dc2, self.url_dc1] + samba_tool_cmd += [partition_dn] + username = self.creds.get_username() + password = self.creds.get_password() + samba_tool_cmd += ["-U{0}%{1}".format(username, password)] + + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + + try: + self.assertCmdSuccess(result, out, err) + except AssertionError: + print("Failed repl, retrying in 10s") + time.sleep(10) + (result, out, err) = self.runsubcmd(*samba_tool_cmd) + + self._disable_inbound_repl(self.url_dc1) + self._disable_all_repl(self.url_dc1) + + self.assertCmdSuccess(result, out, err) + + # Get a unique prefix for some search expression like "([att]=[pref]{i}*)" + def get_unique(self, expr_templ): + found = True + while found: + i = random.randint(0, 65535) + res = self.from_ldb.search(base=self.schema_dn, + scope=ldb.SCOPE_SUBTREE, + expression=expr_templ.format(i=i)) + found = len(res) > 0 + + return str(i) + + def unique_gov_id_prefix(self): + prefix = "1.3.6.1.4.1.7165.4.6.2.8." + return prefix + self.get_unique("(governsId=" + prefix + "{i}.*)") + + def unique_cn_prefix(self, prefix="testobj"): + return prefix + self.get_unique("(cn=" + prefix + "{i}x*)") + "x" + + # Make test schema classes linked to each other in a line, then modify + # them in reverse order so when we repl, a link crosses the chunk + # boundary. Chunk size is 133 by default so we do 150. + def test_poss_superiors_across_chunk(self): + num_schema_objects_to_add = 150 + class_name = self.unique_cn_prefix() + + ldif_template = """ +dn: CN={class_name}{i},{schema_dn} +objectClass: top +objectClass: classSchema +adminDescription: {class_name}{i} +adminDisplayName: {class_name}{i} +cn: {class_name}{i} +governsId: {gov_id}.{i} +instanceType: 4 +objectClassCategory: 1 +systemFlags: 16 +systemOnly: FALSE +""" + + ldif_kwargs = {'class_name': class_name, + 'schema_dn': self.schema_dn} + gov_id = self.unique_gov_id_prefix() + ldif = ldif_template.format(i=0, gov_id=gov_id, **ldif_kwargs) + self.from_ldb.add_ldif(ldif) + + ldif_template += "systemPossSuperiors: {possSup}\n" + + ids = list(range(num_schema_objects_to_add)) + got_no_such_attrib = False + for i in ids[1:]: + last_class_name = class_name + str(i-1) + ldif = ldif_template.format(i=i, gov_id=gov_id, + possSup=last_class_name, + **ldif_kwargs) + + try: + self.from_ldb.add_ldif(ldif) + if got_no_such_attrib: + self.from_ldb.set_schema_update_now() + except ldb.LdbError as e: + if e.args[0] != ldb.ERR_NO_SUCH_ATTRIBUTE: + self.fail(e) + if got_no_such_attrib: + self.fail(("got NO_SUCH_ATTRIB even after " + "setting schemaUpdateNow", str(e))) + print("got NO_SUCH_ATTRIB, trying schemaUpdateNow") + got_no_such_attrib = True + self.from_ldb.set_schema_update_now() + self.from_ldb.add_ldif(ldif) + self.from_ldb.set_schema_update_now() + + ldif_template = """ +dn: CN={class_name}{i},{schema_dn} +changetype: modify +replace: adminDescription +adminDescription: new_description +""" + + for i in reversed(ids): + ldif = ldif_template.format(i=i, **ldif_kwargs) + self.from_ldb.modify_ldif(ldif) + + self.do_repl(self.schema_dn) + + dn_templ = "CN={class_name}{i},{schema_dn}" + for i in ids: + dn = dn_templ.format(i=i, **ldif_kwargs) + res = self.dest_ldb.search(base=dn, scope=ldb.SCOPE_BASE) + self.assertEqual(len(res), 1) + + # Test for method of adding linked attributes in schema partition + # required by other tests. + def test_create_linked_attribute_in_schema(self): + # Make an object outside of the schema partition that we can link to + user_name = self.unique_cn_prefix("user") + user_dn = "CN={},CN=Users,{}".format(user_name, self.domain_dn) + + ldif_template = """ +dn: {user_dn} +objectClass: person +objectClass: user""" + ldif = ldif_template.format(user_dn=user_dn) + self.from_ldb.add_ldif(ldif) + + # Make test class name unique so test can run multiple times + class_name = self.unique_cn_prefix("class") + + kwargs = {'class_name': class_name, + 'schema_dn': self.schema_dn, + 'user_dn': user_dn} + + # Add an auxiliary schemaClass (cat 3) class and give it managedBy + # so we can create schema objects with linked attributes. + ldif_template = """ +dn: CN={class_name},{schema_dn} +objectClass: classSchema +governsId: {gov_id}.0 +instanceType: 4 +systemFlags: 16 +systemOnly: FALSE +objectClassCategory: 3 +mayContain: managedBy +""" + + gov_id = self.unique_gov_id_prefix() + ldif = ldif_template.format(gov_id=gov_id, **kwargs) + self.from_ldb.add_ldif(ldif) + + # Now make an instance that points back to the user with managedBy, + # thus creating an object in the schema with a linked attribute + ldif_template = """ +dn: CN=link{class_name},{schema_dn} +objectClass: classSchema +objectClass: {class_name} +instanceType: 4 +governsId: {gov_id}.0 +systemFlags: 16 +managedBy: {user_dn} +""" + + gov_id = self.unique_gov_id_prefix() + ldif = ldif_template.format(gov_id=gov_id, **kwargs) + self.from_ldb.add_ldif(ldif) + + # Check link exists on test schema object + dn_templ = "CN=link{class_name},{schema_dn}" + dn = dn_templ.format(**kwargs) + res = self.from_ldb.search(base=dn, scope=ldb.SCOPE_BASE) + self.assertEqual(len(res), 1) + self.assertIsNotNone(res[0].get("managedBy")) + self.assertEqual(str(res[0].get("managedBy")[0]), user_dn) + + # Check backlink on user object + res = self.from_ldb.search(base=user_dn, scope=ldb.SCOPE_BASE) + self.assertEqual(len(res), 1) + managed_objs = res[0].get("managedObjects") + self.assertEqual(len(managed_objs), 1) + managed_objs = [str(o) for o in managed_objs] + self.assertEqual(managed_objs, [dn_templ.format(**kwargs)]) + + def test_schema_linked_attributes(self): + num_test_objects = 9 + + # Make an object outside of the schema partition that we can link to + user_name = self.unique_cn_prefix("user") + user_dn = "CN={},CN=Users,{}".format(user_name, self.domain_dn) + + ldif_template = """ +dn: {user_dn} +objectClass: person +objectClass: user""" + ldif = ldif_template.format(user_dn=user_dn) + self.from_ldb.add_ldif(ldif) + + self.do_repl(self.domain_dn) + + # Make test object name prefixes unique so test can run multiple times + # in a single testenv (can't delete schema objects) + class_name = self.unique_cn_prefix("class") + link_class_name = self.unique_cn_prefix("linkClass") + + kwargs = {'class_name': class_name, + 'schema_dn': self.schema_dn, + 'link_class_name': link_class_name, + 'user_dn': user_dn} + + # Add an auxiliary schemaClass (cat 3) class and give it managedBy + # so we can create schema objects with linked attributes. + ldif_template = """ +dn: CN={class_name},{schema_dn} +objectClass: classSchema +governsId: {gov_id}.0 +instanceType: 4 +systemFlags: 16 +systemOnly: FALSE +objectClassCategory: 3 +mayContain: managedBy +""" + + gov_id = self.unique_gov_id_prefix() + ldif = ldif_template.format(gov_id=gov_id, **kwargs) + self.from_ldb.add_ldif(ldif) + + # Now make instances that point back to the user with managedBy, + # thus creating objects in the schema with linked attributes + ldif_template = """ +dn: CN={link_class_name}{i},{schema_dn} +objectClass: classSchema +objectClass: {class_name} +instanceType: 4 +governsId: {gov_id}.0 +systemFlags: 16 +managedBy: {user_dn} +""" + + id_range = list(range(num_test_objects)) + for i in id_range: + gov_id = self.unique_gov_id_prefix() + ldif = ldif_template.format(i=i, gov_id=gov_id, **kwargs) + self.from_ldb.add_ldif(ldif) + + self.do_repl(self.schema_dn) + + # Check link exists in each test schema objects at destination DC + dn_templ = "CN={link_class_name}{i},{schema_dn}" + for i in id_range: + dn = dn_templ.format(i=i, **kwargs) + res = self.dest_ldb.search(base=dn, scope=ldb.SCOPE_BASE) + self.assertEqual(len(res), 1) + self.assertIsNotNone(res[0].get("managedBy")) + self.assertEqual(str(res[0].get("managedBy")[0]), user_dn) + + # Check backlinks list on user object contains DNs of test objects. + res = self.dest_ldb.search(base=user_dn, scope=ldb.SCOPE_BASE) + self.assertEqual(len(res), 1) + managed_objs = res[0].get("managedObjects") + self.assertIsNotNone(managed_objs) + managed_objs_set = {str(el) for el in managed_objs} + expected = {dn_templ.format(i=i, **kwargs) for i in id_range} + self.assertEqual(managed_objs_set, expected) diff --git a/source4/torture/drs/python/getnc_unpriv.py b/source4/torture/drs/python/getnc_unpriv.py new file mode 100644 index 0000000..c53906a --- /dev/null +++ b/source4/torture/drs/python/getnc_unpriv.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests replication scenarios with different user privileges. +# We want to test every replication scenario we can think of against: +# - users with only GET_CHANGES privileges +# - users with only GET_ALL_CHANGES privileges +# - users with both GET_CHANGES and GET_ALL_CHANGES privileges +# - users with no privileges +# +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN getnc_unpriv -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +from samba import werror, WERRORError + +from samba import sd_utils +import ldb +from ldb import SCOPE_BASE +import random + +from samba.dcerpc import drsuapi, security +from samba.credentials import DONT_USE_KERBEROS + + +class DrsReplicaSyncUnprivTestCase(drs_base.DrsBaseTestCase): + """Confirm the behaviour of DsGetNCChanges for unprivileged users""" + + def setUp(self): + super(DrsReplicaSyncUnprivTestCase, self).setUp() + self.get_changes_user = "get-changes-user" + self.base_dn = self.ldb_dc1.get_default_basedn() + self.user_pass = samba.generate_random_password(12, 16) + + # add some randomness to the test OU. (Deletion of the last test's + # objects can be slow to replicate out. So the OU created by a previous + # testenv may still exist at this point). + rand = random.randint(1, 10000000) + test_ou = "OU=test_getnc_unpriv%d" % rand + self.ou = "%s,%s" % (test_ou, self.base_dn) + self.ldb_dc1.add({ + "dn": self.ou, + "objectclass": "organizationalUnit"}) + self.ldb_dc1.newuser(self.get_changes_user, self.user_pass, + userou=test_ou) + (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) + + self.sd_utils = sd_utils.SDUtils(self.ldb_dc1) + self.user_dn = "cn=%s,%s" % (self.get_changes_user, self.ou) + user_sid = self.sd_utils.get_object_sid(self.user_dn) + self.acl_mod_get_changes = "(OA;;CR;%s;;%s)" % (security.GUID_DRS_GET_CHANGES, + str(user_sid)) + self.acl_mod_get_all_changes = "(OA;;CR;%s;;%s)" % (security.GUID_DRS_GET_ALL_CHANGES, + str(user_sid)) + self.desc_sddl = self.sd_utils.get_sd_as_sddl(self.base_dn) + + # We set DONT_USE_KERBEROS to avoid a race with getting the + # user replicated to our selected KDC + self.user_creds = self.insta_creds(template=self.get_credentials(), + username=self.get_changes_user, + userpass=self.user_pass, + kerberos_state=DONT_USE_KERBEROS) + (self.user_drs, self.user_drs_handle) = self._ds_bind(self.dnsname_dc1, + self.user_creds) + + def tearDown(self): + self.sd_utils.modify_sd_on_dn(self.base_dn, self.desc_sddl) + try: + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + except ldb.LdbError as e1: + (enum, string) = e1.args + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + super(DrsReplicaSyncUnprivTestCase, self).tearDown() + + def _test_repl_exop(self, exop, repl_obj, expected_error, dest_dsa=None, + partial_attribute_set=None): + """ + Common function to send a replication request and check the result + matches what's expected. + """ + req8 = self._exop_req8(dest_dsa=dest_dsa, + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=repl_obj, + exop=exop, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP, + partial_attribute_set=partial_attribute_set) + + if expected_error is None: + # user is OK, request should be accepted without throwing an error + (level, ctr) = self.user_drs.DsGetNCChanges(self.user_drs_handle, + 8, req8) + else: + # check the request is rejected (with the error we're expecting) + try: + (level, ctr) = self.user_drs.DsGetNCChanges(self.user_drs_handle, + 8, req8) + self.fail("Should have failed with user denied access") + except WERRORError as e: + (enum, estr) = e.args + self.assertTrue(enum in expected_error, + "Got unexpected error: %s" % estr) + + def _test_repl_single_obj(self, repl_obj, expected_error, + partial_attribute_set=None): + """ + Checks that replication on a single object either succeeds or fails as + expected (based on the user's access rights) + """ + self._test_repl_exop(exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + repl_obj=repl_obj, + expected_error=expected_error, + partial_attribute_set=partial_attribute_set) + + def _test_repl_secret(self, repl_obj, expected_error, dest_dsa=None): + """ + Checks that REPL_SECRET on an object either succeeds or fails as + expected (based on the user's access rights) + """ + self._test_repl_exop(exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + repl_obj=repl_obj, + expected_error=expected_error, + dest_dsa=dest_dsa) + + def _test_repl_full(self, expected_error, partial_attribute_set=None): + """ + Checks that a full replication either succeeds or fails as expected + (based on the user's access rights) + """ + self._test_repl_exop(exop=drsuapi.DRSUAPI_EXOP_NONE, + repl_obj=self.ldb_dc1.get_default_basedn(), + expected_error=expected_error, + partial_attribute_set=partial_attribute_set) + + def _test_repl_full_on_ou(self, repl_obj, expected_error): + """ + Full replication on a specific OU should always fail (it should be done + against a base NC). The error may vary based on the user's access rights + """ + # Just try against the OU created in the test setup + self._test_repl_exop(exop=drsuapi.DRSUAPI_EXOP_NONE, + repl_obj=repl_obj, + expected_error=expected_error) + + def test_repl_getchanges_userpriv(self): + """ + Tests various replication requests made by a user with only GET_CHANGES + rights. Some requests will be accepted, but most will be rejected. + """ + + # Assign the user GET_CHANGES rights + self.sd_utils.dacl_add_ace(self.base_dn, self.acl_mod_get_changes) + + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + bad_ou = "OU=bad_obj,%s" % self.ou + self._test_repl_single_obj(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_DN, + werror.WERR_DS_DRA_ACCESS_DENIED]) + + self._test_repl_secret(repl_obj=self.ou, + expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_secret(repl_obj=self.user_dn, + expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_secret(repl_obj=self.user_dn, + dest_dsa=self.ldb_dc1.get_ntds_GUID(), + expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_secret(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_DN]) + + self._test_repl_full(expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_full_on_ou(repl_obj=self.ou, + expected_error=[werror.WERR_DS_CANT_FIND_EXPECTED_NC, + werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_full_on_ou(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_NC, + werror.WERR_DS_DRA_ACCESS_DENIED]) + + # Partial Attribute Sets don't require GET_ALL_CHANGES rights, so we + # expect the following to succeed + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=None, + partial_attribute_set=self.get_partial_attribute_set()) + self._test_repl_full(expected_error=None, + partial_attribute_set=self.get_partial_attribute_set()) + + def test_repl_getallchanges_userpriv(self): + """ + Tests various replication requests made by a user with only + GET_ALL_CHANGES rights. Note that assigning these rights is possible, + but doesn't make a lot of sense. We test it anyway for consistency. + """ + + # Assign the user GET_ALL_CHANGES rights + self.sd_utils.dacl_add_ace(self.base_dn, self.acl_mod_get_all_changes) + + # We can expect to get the same responses as an unprivileged user, + # i.e. we have permission to see the results, but don't have permission + # to ask + self.test_repl_no_userpriv() + + def test_repl_both_userpriv(self): + """ + Tests various replication requests made by a privileged user (i.e. has + both GET_CHANGES and GET_ALL_CHANGES). We expect any valid requests + to be accepted. + """ + + # Assign the user both GET_CHANGES and GET_ALL_CHANGES rights + both_rights = self.acl_mod_get_changes + self.acl_mod_get_all_changes + self.sd_utils.dacl_add_ace(self.base_dn, both_rights) + + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=None) + bad_ou = "OU=bad_obj,%s" % self.ou + self._test_repl_single_obj(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_DN]) + + # Microsoft returns DB_ERROR, Samba returns ACCESS_DENIED + self._test_repl_secret(repl_obj=self.ou, + expected_error=[werror.WERR_DS_DRA_DB_ERROR, + werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_secret(repl_obj=self.user_dn, + expected_error=[werror.WERR_DS_DRA_DB_ERROR, + werror.WERR_DS_DRA_ACCESS_DENIED]) + # Note that Windows accepts this but Samba rejects it + self._test_repl_secret(repl_obj=self.user_dn, + dest_dsa=self.ldb_dc1.get_ntds_GUID(), + expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + + self._test_repl_secret(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_DN]) + + self._test_repl_full(expected_error=None) + self._test_repl_full_on_ou(repl_obj=self.ou, + expected_error=[werror.WERR_DS_CANT_FIND_EXPECTED_NC]) + self._test_repl_full_on_ou(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_NC, + werror.WERR_DS_DRA_BAD_DN]) + + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=None, + partial_attribute_set=self.get_partial_attribute_set()) + self._test_repl_full(expected_error=None, + partial_attribute_set=self.get_partial_attribute_set()) + + def test_repl_no_userpriv(self): + """ + Tests various replication requests made by a unprivileged user. + We expect all these requests to be rejected. + """ + + # Microsoft usually returns BAD_DN, Samba returns ACCESS_DENIED + usual_error = [werror.WERR_DS_DRA_BAD_DN, werror.WERR_DS_DRA_ACCESS_DENIED] + + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=usual_error) + bad_ou = "OU=bad_obj,%s" % self.ou + self._test_repl_single_obj(repl_obj=bad_ou, + expected_error=usual_error) + + self._test_repl_secret(repl_obj=self.ou, + expected_error=usual_error) + self._test_repl_secret(repl_obj=self.user_dn, + expected_error=usual_error) + self._test_repl_secret(repl_obj=self.user_dn, + dest_dsa=self.ldb_dc1.get_ntds_GUID(), + expected_error=usual_error) + self._test_repl_secret(repl_obj=bad_ou, + expected_error=usual_error) + + self._test_repl_full(expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED]) + self._test_repl_full_on_ou(repl_obj=self.ou, + expected_error=usual_error) + self._test_repl_full_on_ou(repl_obj=bad_ou, + expected_error=[werror.WERR_DS_DRA_BAD_NC, + werror.WERR_DS_DRA_ACCESS_DENIED]) + + self._test_repl_single_obj(repl_obj=self.ou, + expected_error=usual_error, + partial_attribute_set=self.get_partial_attribute_set()) + self._test_repl_full(expected_error=[werror.WERR_DS_DRA_ACCESS_DENIED], + partial_attribute_set=self.get_partial_attribute_set()) diff --git a/source4/torture/drs/python/getncchanges.py b/source4/torture/drs/python/getncchanges.py new file mode 100644 index 0000000..6b5456a --- /dev/null +++ b/source4/torture/drs/python/getncchanges.py @@ -0,0 +1,1427 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests various schema replication scenarios +# +# Copyright (C) Catalyst.Net Ltd. 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN \ +# getncchanges -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +import ldb +from ldb import SCOPE_BASE +import random + +from samba.dcerpc import drsuapi, misc +from samba import WERRORError +from samba import werror + +class DrsReplicaSyncIntegrityTestCase(drs_base.DrsBaseTestCase): + def setUp(self): + super(DrsReplicaSyncIntegrityTestCase, self).setUp() + + self.init_test_state() + + # Note that DC2 is the DC with the testenv-specific quirks (e.g. it's + # the vampire_dc), so we point this test directly at that DC + self.set_test_ldb_dc(self.ldb_dc2) + + self.ou = str(samba.tests.create_test_ou(self.test_ldb_dc, + "getncchanges." + self.id().rsplit(".", 1)[1])) + + self.addCleanup(self.ldb_dc2.delete, self.ou, ["tree_delete:1"]) + + self.base_dn = self.test_ldb_dc.get_default_basedn() + + self.default_conn = DcConnection(self, self.ldb_dc2, self.dnsname_dc2) + self.set_dc_connection(self.default_conn) + + def init_test_state(self): + self.rxd_dn_list = [] + self.rxd_links = [] + self.rxd_guids = [] + self.last_ctr = None + + # 100 is the minimum max_objects that Microsoft seems to honour + # (the max honoured is 400ish), so we use that in these tests + self.max_objects = 100 + + # store whether we used GET_TGT/GET_ANC flags in the requests + self.used_get_tgt = False + self.used_get_anc = False + + def add_object(self, dn, objectclass="organizationalunit"): + """Adds an OU object""" + self.test_ldb_dc.add({"dn": dn, "objectclass": objectclass}) + res = self.test_ldb_dc.search(base=dn, scope=SCOPE_BASE) + self.assertEqual(len(res), 1) + + def modify_object(self, dn, attr, value): + """Modifies an object's USN by adding an attribute value to it""" + m = ldb.Message() + m.dn = ldb.Dn(self.test_ldb_dc, dn) + m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_ADD, attr) + self.test_ldb_dc.modify(m) + + def delete_attribute(self, dn, attr, value): + """Deletes an attribute from an object""" + m = ldb.Message() + m.dn = ldb.Dn(self.test_ldb_dc, dn) + m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_DELETE, attr) + self.test_ldb_dc.modify(m) + + def start_new_repl_cycle(self): + """Resets enough state info to start a new replication cycle""" + # reset rxd_links, but leave rxd_guids and rxd_dn_list alone so we know + # whether a parent/target is unknown and needs GET_ANC/GET_TGT to + # resolve + self.rxd_links = [] + + self.used_get_tgt = False + self.used_get_anc = False + # mostly preserve self.last_ctr, so that we use the last HWM + if self.last_ctr is not None: + self.last_ctr.more_data = True + + def create_object_range(self, start, end, prefix="", + children=None, parent_list=None): + """ + Creates a block of objects. Object names are numbered sequentially, + using the optional prefix supplied. If the children parameter is + supplied it will create a parent-child hierarchy and return the + top-level parents separately. + """ + dn_list = [] + + # Use dummy/empty lists if we're not creating a parent/child hierarchy + if children is None: + children = [] + + if parent_list is None: + parent_list = [] + + # Create the parents first, then the children. + # This makes it easier to see in debug when GET_ANC takes effect + # because the parent/children become interleaved (by default, + # this approach means the objects are organized into blocks of + # parents and blocks of children together) + for x in range(start, end): + ou = "OU=test_ou_%s%d,%s" % (prefix, x, self.ou) + self.add_object(ou) + dn_list.append(ou) + + # keep track of the top-level parents (if needed) + parent_list.append(ou) + + # create the block of children (if needed) + for x in range(start, end): + for child in children: + ou = "OU=test_ou_child%s%d,%s" % (child, x, parent_list[x]) + self.add_object(ou) + dn_list.append(ou) + + return dn_list + + def assert_expected_data(self, expected_list): + """ + Asserts that we received all the DNs that we expected and + none are missing. + """ + received_list = self.rxd_dn_list + + # Note that with GET_ANC Windows can end up sending the same parent + # object multiple times, so this might be noteworthy but doesn't + # warrant failing the test + num_received = len(received_list) + num_expected = len(expected_list) + if num_received != num_expected: + print("Note: received %d objects but expected %d" % (num_received, + num_expected)) + + # Check that we received every object that we were expecting + for dn in expected_list: + self.assertTrue(dn in received_list, + "DN '%s' missing from replication." % dn) + + def test_repl_integrity(self): + """ + Modify the objects being replicated while the replication is still + in progress and check that no object loss occurs. + """ + + # The server behaviour differs between samba and Windows. Samba returns + # the objects in the original order (up to the pre-modify HWM). Windows + # incorporates the modified objects and returns them in the new order + # (i.e. modified objects last), up to the post-modify HWM. The + # Microsoft docs state the Windows behaviour is optional. + + # Create a range of objects to replicate. + expected_dn_list = self.create_object_range(0, 400) + (orig_hwm, unused) = self._get_highest_hwm_utdv(self.test_ldb_dc) + + # We ask for the first page of 100 objects. + # For this test, we don't care what order we receive the objects in, + # so long as by the end we've received everything + self.repl_get_next() + + # Modify some of the second page of objects. This should bump the + # highwatermark + for x in range(100, 200): + self.modify_object(expected_dn_list[x], "displayName", "OU%d" % x) + + (post_modify_hwm, _) = self._get_highest_hwm_utdv(self.test_ldb_dc) + self.assertTrue(post_modify_hwm.highest_usn > orig_hwm.highest_usn) + + # Get the remaining blocks of data + while not self.replication_complete(): + self.repl_get_next() + + # Check we still receive all the objects we're expecting + self.assert_expected_data(expected_dn_list) + + def is_parent_known(self, dn, known_dn_list): + """ + Returns True if the parent of the dn specified is in known_dn_list + """ + + # we can sometimes get system objects like the RID Manager returned. + # Ignore anything that is not under the test OU we created + if self.ou not in dn: + return True + + # Remove the child portion from the name to get the parent's DN + name_substrings = dn.split(",") + del name_substrings[0] + + parent_dn = ",".join(name_substrings) + + # check either this object is a parent (it's parent is the top-level + # test object), or its parent has been seen previously + return parent_dn == self.ou or parent_dn in known_dn_list + + def _repl_send_request(self, get_anc=False, get_tgt=False): + """ + Sends a GetNCChanges request for the next block of replication data. + """ + + # we're just trying to mimic regular client behaviour here, so just + # use the highwatermark in the last response we received + if self.last_ctr: + highwatermark = self.last_ctr.new_highwatermark + uptodateness_vector = self.last_ctr.uptodateness_vector + else: + # this is the first replication chunk + highwatermark = None + uptodateness_vector = None + + # Ask for the next block of replication data + replica_flags = drsuapi.DRSUAPI_DRS_WRIT_REP + more_flags = 0 + + if get_anc: + replica_flags |= drsuapi.DRSUAPI_DRS_GET_ANC + self.used_get_anc = True + + if get_tgt: + more_flags = drsuapi.DRSUAPI_DRS_GET_TGT + self.used_get_tgt = True + + # return the response from the DC + return self._get_replication(replica_flags, + max_objects=self.max_objects, + highwatermark=highwatermark, + uptodateness_vector=uptodateness_vector, + + more_flags=more_flags) + + def repl_get_next(self, get_anc=False, get_tgt=False, assert_links=False): + """ + Requests the next block of replication data. This tries to simulate + client behaviour - if we receive a replicated object that we don't know + the parent of, then re-request the block with the GET_ANC flag set. + If we don't know the target object for a linked attribute, then + re-request with GET_TGT. + """ + + # send a request to the DC and get the response + ctr6 = self._repl_send_request(get_anc=get_anc, get_tgt=get_tgt) + + # extract the object DNs and their GUIDs from the response + rxd_dn_list = self._get_ctr6_dn_list(ctr6) + rxd_guid_list = self._get_ctr6_object_guids(ctr6) + + # we'll add new objects as we discover them, so take a copy of the + # ones we already know about, so we can modify these lists safely + known_objects = self.rxd_dn_list[:] + known_guids = self.rxd_guids[:] + + # check that we know the parent for every object received + for i in range(0, len(rxd_dn_list)): + + dn = rxd_dn_list[i] + guid = rxd_guid_list[i] + + if self.is_parent_known(dn, known_objects): + + # the new DN is now known so add it to the list. + # It may be the parent of another child in this block + known_objects.append(dn) + known_guids.append(guid) + else: + # If we've already set the GET_ANC flag then it should mean + # we receive the parents before the child + self.assertFalse(get_anc, "Unknown parent for object %s" % dn) + + print("Unknown parent for %s - try GET_ANC" % dn) + + # try the same thing again with the GET_ANC flag set this time + return self.repl_get_next(get_anc=True, get_tgt=get_tgt, + assert_links=assert_links) + + # check we know about references to any objects in the linked attrs + received_links = self._get_ctr6_links(ctr6) + + # This is so that older versions of Samba fail - we want the links to + # be sent roughly with the objects, rather than getting all links at + # the end + if assert_links: + self.assertTrue(len(received_links) > 0, + "Links were expected in the GetNCChanges response") + + for link in received_links: + + # skip any links that aren't part of the test + if self.ou not in link.targetDN: + continue + + # check the source object is known (Windows can actually send links + # where we don't know the source object yet). Samba shouldn't ever + # hit this case because it gets the links based on the source + if link.identifier not in known_guids: + + # If we've already set the GET_ANC flag then it should mean + # this case doesn't happen + self.assertFalse(get_anc, "Unknown source object for GUID %s" + % link.identifier) + + print("Unknown source GUID %s - try GET_ANC" % link.identifier) + + # try the same thing again with the GET_ANC flag set this time + return self.repl_get_next(get_anc=True, get_tgt=get_tgt, + assert_links=assert_links) + + # check we know the target object + if link.targetGUID not in known_guids: + + # If we've already set the GET_TGT flag then we should have + # already received any objects we need to know about + self.assertFalse(get_tgt, "Unknown linked target for object %s" + % link.targetDN) + + print("Unknown target for %s - try GET_TGT" % link.targetDN) + + # try the same thing again with the GET_TGT flag set this time + return self.repl_get_next(get_anc=get_anc, get_tgt=True, + assert_links=assert_links) + + # store the last successful result so we know what HWM to request next + self.last_ctr = ctr6 + + # store the objects, GUIDs, and links we received + self.rxd_dn_list += self._get_ctr6_dn_list(ctr6) + self.rxd_links += self._get_ctr6_links(ctr6) + self.rxd_guids += self._get_ctr6_object_guids(ctr6) + + return ctr6 + + def replication_complete(self): + """Returns True if the current/last replication cycle is complete""" + + if self.last_ctr is None or self.last_ctr.more_data: + return False + else: + return True + + def test_repl_integrity_get_anc(self): + """ + Modify the parent objects being replicated while the replication is + still in progress (using GET_ANC) and check that no object loss occurs. + """ + + # Note that GET_ANC behaviour varies between Windows and Samba. + # On Samba GET_ANC results in the replication restarting from the very + # beginning. After that, Samba remembers GET_ANC and also sends the + # parents in subsequent requests (regardless of whether GET_ANC is + # specified in the later request). + # Windows only sends the parents if GET_ANC was specified in the last + # request. It will also resend a parent, even if it's already sent the + # parent in a previous response (whereas Samba doesn't). + + # Create a small block of 50 parents, each with 2 children (A and B) + # This is so that we receive some children in the first block, so we + # can resend with GET_ANC before we learn too many parents + parent_dn_list = [] + expected_dn_list = self.create_object_range(0, 50, prefix="parent", + children=("A", "B"), + parent_list=parent_dn_list) + + # create the remaining parents and children + expected_dn_list += self.create_object_range(50, 150, prefix="parent", + children=("A", "B"), + parent_list=parent_dn_list) + + # We've now got objects in the following order: + # [50 parents][100 children][100 parents][200 children] + + # Modify the first parent so that it's now ordered last by USN + # This means we set the GET_ANC flag pretty much straight away + # because we receive the first child before the first parent + self.modify_object(parent_dn_list[0], "displayName", "OU0") + + # modify a later block of parents so they also get reordered + for x in range(50, 100): + self.modify_object(parent_dn_list[x], "displayName", "OU%d" % x) + + # Get the first block of objects - this should resend the request with + # GET_ANC set because we won't know about the first child's parent. + # On samba GET_ANC essentially starts the sync from scratch again, so + # we get this over with early before we learn too many parents + self.repl_get_next() + + # modify the last chunk of parents. They should now have a USN higher + # than the highwater-mark for the replication cycle + for x in range(100, 150): + self.modify_object(parent_dn_list[x], "displayName", "OU%d" % x) + + # Get the remaining blocks of data - this will resend the request with + # GET_ANC if it encounters an object it doesn't have the parent for. + while not self.replication_complete(): + self.repl_get_next() + + # The way the test objects have been created should force + # self.repl_get_next() to use the GET_ANC flag. If this doesn't + # actually happen, then the test isn't doing its job properly + self.assertTrue(self.used_get_anc, + "Test didn't use the GET_ANC flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(expected_dn_list) + + def assert_expected_links(self, objects_with_links, link_attr="managedBy", + num_expected=None): + """ + Asserts that a GetNCChanges response contains any expected links + for the objects it contains. + """ + received_links = self.rxd_links + + if num_expected is None: + num_expected = len(objects_with_links) + + self.assertTrue(len(received_links) == num_expected, + "Received %d links but expected %d" + % (len(received_links), num_expected)) + + for dn in objects_with_links: + self.assert_object_has_link(dn, link_attr, received_links) + + def assert_object_has_link(self, dn, link_attr, received_links): + """ + Queries the object in the DB and asserts there is a link in the + GetNCChanges response that matches. + """ + + # Look up the link attribute in the DB + # The extended_dn option will dump the GUID info for the link + # attribute (as a hex blob) + res = self.test_ldb_dc.search(ldb.Dn(self.test_ldb_dc, dn), + attrs=[link_attr], + controls=['extended_dn:1:0'], + scope=ldb.SCOPE_BASE) + + # We didn't find the expected link attribute in the DB for the object. + # Something has gone wrong somewhere... + self.assertTrue(link_attr in res[0], + "%s in DB doesn't have attribute %s" % (dn, link_attr)) + + # find the received link in the list and assert that the target and + # source GUIDs match what's in the DB + for val in [str(val) for val in res[0][link_attr]]: + # Work out the expected source and target GUIDs for the DB link + target_dn = ldb.Dn(self.test_ldb_dc, val) + targetGUID_blob = target_dn.get_extended_component("GUID") + sourceGUID_blob = res[0].dn.get_extended_component("GUID") + + found = False + + for link in received_links: + if link.selfGUID_blob == sourceGUID_blob and \ + link.targetGUID_blob == targetGUID_blob: + + found = True + + if self._debug: + print("Link %s --> %s" % (dn[:25], link.targetDN[:25])) + break + + self.assertTrue(found, + "Did not receive expected link for DN %s" % dn) + + def test_repl_get_tgt(self): + """ + Creates a scenario where we should receive the linked attribute before + we know about the target object, and therefore need to use GET_TGT. + Note: Samba currently avoids this problem by sending all its links last + """ + + # create the test objects + reportees = self.create_object_range(0, 100, prefix="reportee") + managers = self.create_object_range(0, 100, prefix="manager") + all_objects = managers + reportees + expected_links = reportees + + # add a link attribute to each reportee object that points to the + # corresponding manager object as the target + for i in range(0, 100): + self.modify_object(reportees[i], "managedBy", managers[i]) + + # touch the managers (the link-target objects) again to make sure the + # reportees (link source objects) get returned first by the replication + for i in range(0, 100): + self.modify_object(managers[i], "displayName", "OU%d" % i) + + links_expected = True + + # Get all the replication data - this code should resend the requests + # with GET_TGT + while not self.replication_complete(): + + # get the next block of replication data (this sets GET_TGT + # if needed) + self.repl_get_next(assert_links=links_expected) + links_expected = len(self.rxd_links) < len(expected_links) + + # The way the test objects have been created should force + # self.repl_get_next() to use the GET_TGT flag. If this doesn't + # actually happen, then the test isn't doing its job properly + self.assertTrue(self.used_get_tgt, + "Test didn't use the GET_TGT flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(all_objects) + + # Check we received links for all the reportees + self.assert_expected_links(expected_links) + + def test_repl_get_tgt_chain(self): + """ + Tests the behaviour of GET_TGT with a more complicated scenario. + Here we create a chain of objects linked together, so if we follow + the link target, then we'd traverse ~200 objects each time. + """ + + # create the test objects + objectsA = self.create_object_range(0, 100, prefix="AAA") + objectsB = self.create_object_range(0, 100, prefix="BBB") + objectsC = self.create_object_range(0, 100, prefix="CCC") + + # create a complex set of object links: + # A0-->B0-->C1-->B2-->C3-->B4-->and so on... + # Basically each object-A should link to a circular chain of 200 B/C + # objects. We create the links in separate chunks here, as it makes it + # clearer what happens with the USN (links on Windows have their own + # USN, so this approach means the A->B/B->C links aren't interleaved) + for i in range(0, 100): + self.modify_object(objectsA[i], "managedBy", objectsB[i]) + + for i in range(0, 100): + self.modify_object(objectsB[i], "managedBy", + objectsC[(i + 1) % 100]) + + for i in range(0, 100): + self.modify_object(objectsC[i], "managedBy", + objectsB[(i + 1) % 100]) + + all_objects = objectsA + objectsB + objectsC + expected_links = all_objects + + # the default order the objects now get returned in should be: + # [A0-A99][B0-B99][C0-C99] + + links_expected = True + + # Get all the replication data - this code should resend the requests + # with GET_TGT + while not self.replication_complete(): + + # get the next block of replication data (this sets GET_TGT + # if needed) + self.repl_get_next(assert_links=links_expected) + links_expected = len(self.rxd_links) < len(expected_links) + + # The way the test objects have been created should force + # self.repl_get_next() to use the GET_TGT flag. If this doesn't + # actually happen, then the test isn't doing its job properly + self.assertTrue(self.used_get_tgt, + "Test didn't use the GET_TGT flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(all_objects) + + # Check we received links for all the reportees + self.assert_expected_links(expected_links) + + def test_repl_integrity_link_attr(self): + """ + Tests adding links to new objects while a replication is in progress. + """ + + # create some source objects for the linked attributes, sandwiched + # between 2 blocks of filler objects + filler = self.create_object_range(0, 100, prefix="filler") + reportees = self.create_object_range(0, 100, prefix="reportee") + filler += self.create_object_range(100, 200, prefix="filler") + + # Start the replication and get the first block of filler objects + # (We're being mean here and setting the GET_TGT flag right from the + # start. On earlier Samba versions, if the client encountered an + # unknown target object and retried with GET_TGT, it would restart the + # replication cycle from scratch, which avoids the problem). + self.repl_get_next(get_tgt=True) + + # create the target objects and add the links. These objects should be + # outside the scope of the Samba replication cycle, but the links + # should still get sent with the source object + managers = self.create_object_range(0, 100, prefix="manager") + + for i in range(0, 100): + self.modify_object(reportees[i], "managedBy", managers[i]) + + expected_objects = managers + reportees + filler + expected_links = reportees + + # complete the replication + while not self.replication_complete(): + self.repl_get_next(get_tgt=True) + + # If we didn't receive the most recently created objects in the last + # replication cycle, then kick off another replication to get them + if len(self.rxd_dn_list) < len(expected_objects): + self.repl_get_next() + + while not self.replication_complete(): + self.repl_get_next() + + # Check we get all the objects we're expecting + self.assert_expected_data(expected_objects) + + # Check we received links for all the parents + self.assert_expected_links(expected_links) + + def test_repl_get_anc_link_attr(self): + """ + A basic GET_ANC test where the parents have linked attributes + """ + + # Create a block of 100 parents and 100 children + parent_dn_list = [] + expected_dn_list = self.create_object_range(0, 100, prefix="parent", + children=("A"), + parent_list=parent_dn_list) + + # Add links from the parents to the children + for x in range(0, 100): + self.modify_object(parent_dn_list[x], "managedBy", + expected_dn_list[x + 100]) + + # add some filler objects at the end. This allows us to easily see + # which chunk the links get sent in + expected_dn_list += self.create_object_range(0, 100, prefix="filler") + + # We've now got objects in the following order: + # [100 x children][100 x parents][100 x filler] + + # Get the replication data - because the block of children come first, + # this should retry the request with GET_ANC + while not self.replication_complete(): + self.repl_get_next() + + self.assertTrue(self.used_get_anc, + "Test didn't use the GET_ANC flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(expected_dn_list) + + # Check we received links for all the parents + self.assert_expected_links(parent_dn_list) + + def test_repl_get_tgt_and_anc(self): + """ + Check we can resolve an unknown ancestor when fetching the link target, + i.e. tests using GET_TGT and GET_ANC in combination + """ + + # Create some parent/child objects (the child will be the link target) + parents = [] + all_objects = self.create_object_range(0, 100, prefix="parent", + children=["la_tgt"], + parent_list=parents) + + children = [item for item in all_objects if item not in parents] + + # create the link source objects and link them to the child/target + la_sources = self.create_object_range(0, 100, prefix="la_src") + all_objects += la_sources + + for i in range(0, 100): + self.modify_object(la_sources[i], "managedBy", children[i]) + + expected_links = la_sources + + # modify the children/targets so they come after the link source + for x in range(0, 100): + self.modify_object(children[x], "displayName", "OU%d" % x) + + # modify the parents, so they now come last in the replication + for x in range(0, 100): + self.modify_object(parents[x], "displayName", "OU%d" % x) + + # We've now got objects in the following order: + # [100 la_source][100 la_target][100 parents (of la_target)] + + links_expected = True + + # Get all the replication data - this code should resend the requests + # with GET_TGT and GET_ANC + while not self.replication_complete(): + + # get the next block of replication data (this sets + # GET_TGT/GET_ANC) + self.repl_get_next(assert_links=links_expected) + links_expected = len(self.rxd_links) < len(expected_links) + + # The way the test objects have been created should force + # self.repl_get_next() to use the GET_TGT/GET_ANC flags. If this + # doesn't actually happen, then the test isn't doing its job properly + self.assertTrue(self.used_get_tgt, + "Test didn't use the GET_TGT flag as expected") + self.assertTrue(self.used_get_anc, + "Test didn't use the GET_ANC flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(all_objects) + + # Check we received links for all the link sources + self.assert_expected_links(expected_links) + + # Second part of test. Add some extra objects and kick off another + # replication. The test code will use the HWM from the last replication + # so we'll only receive the objects we modify below + self.start_new_repl_cycle() + + # add an extra level of grandchildren that hang off a child + # that got created last time + new_parent = "OU=test_new_parent,%s" % children[0] + self.add_object(new_parent) + new_children = [] + + for x in range(0, 50): + dn = "OU=test_new_la_tgt%d,%s" % (x, new_parent) + self.add_object(dn) + new_children.append(dn) + + # replace half of the links to point to the new children + for x in range(0, 50): + self.delete_attribute(la_sources[x], "managedBy", children[x]) + self.modify_object(la_sources[x], "managedBy", new_children[x]) + + # add some filler objects to fill up the 1st chunk + filler = self.create_object_range(0, 100, prefix="filler") + + # modify the new children/targets so they come after the link source + for x in range(0, 50): + self.modify_object(new_children[x], "displayName", "OU-%d" % x) + + # modify the parent, so it now comes last in the replication + self.modify_object(new_parent, "displayName", "OU%d" % x) + + # We should now get the modified objects in the following order: + # [50 links (x 2)][100 filler][50 new children][new parent] + # Note that the link sources aren't actually sent (their new linked + # attributes are sent, but apart from that, nothing has changed) + all_objects = filler + new_children + [new_parent] + expected_links = la_sources[:50] + + links_expected = True + + while not self.replication_complete(): + self.repl_get_next(assert_links=links_expected) + links_expected = len(self.rxd_links) < len(expected_links) + + self.assertTrue(self.used_get_tgt, + "Test didn't use the GET_TGT flag as expected") + self.assertTrue(self.used_get_anc, + "Test didn't use the GET_ANC flag as expected") + + # Check we get all the objects we're expecting + self.assert_expected_data(all_objects) + + # Check we received links (50 deleted links and 50 new) + self.assert_expected_links(expected_links, num_expected=100) + + def _repl_integrity_obj_deletion(self, delete_link_source=True): + """ + Tests deleting link objects while a replication is in progress. + """ + + # create some objects and link them together, with some filler + # object in between the link sources + la_sources = self.create_object_range(0, 100, prefix="la_source") + la_targets = self.create_object_range(0, 100, prefix="la_targets") + + for i in range(0, 50): + self.modify_object(la_sources[i], "managedBy", la_targets[i]) + + filler = self.create_object_range(0, 100, prefix="filler") + + for i in range(50, 100): + self.modify_object(la_sources[i], "managedBy", la_targets[i]) + + # touch the targets so that the sources get replicated first + for i in range(0, 100): + self.modify_object(la_targets[i], "displayName", "OU%d" % i) + + # objects should now be in the following USN order: + # [50 la_source][100 filler][50 la_source][100 la_target] + + # Get the first block containing 50 link sources + self.repl_get_next() + + # delete either the link targets or link source objects + if delete_link_source: + objects_to_delete = la_sources + # in GET_TGT testenvs we only receive the first 50 source objects + expected_objects = la_sources[:50] + la_targets + filler + else: + objects_to_delete = la_targets + expected_objects = la_sources + filler + + for obj in objects_to_delete: + self.ldb_dc2.delete(obj) + + # complete the replication + while not self.replication_complete(): + self.repl_get_next() + + # Check we get all the objects we're expecting + self.assert_expected_data(expected_objects) + + # we can't use assert_expected_links() here because it tries to check + # against the deleted objects on the DC. (Although we receive some + # links from the first block processed, the Samba client should end up + # deleting these, as the source/target object involved is deleted) + self.assertTrue(len(self.rxd_links) == 50, + "Expected 50 links, not %d" % len(self.rxd_links)) + + def test_repl_integrity_src_obj_deletion(self): + self._repl_integrity_obj_deletion(delete_link_source=True) + + def test_repl_integrity_tgt_obj_deletion(self): + self._repl_integrity_obj_deletion(delete_link_source=False) + + def restore_deleted_object(self, guid, new_dn): + """Re-animates a deleted object""" + + guid_str = self._GUID_string(guid) + res = self.test_ldb_dc.search(base="" % guid_str, + attrs=["isDeleted"], + controls=['show_deleted:1'], + scope=ldb.SCOPE_BASE) + if len(res) != 1: + return + + msg = ldb.Message() + msg.dn = res[0].dn + msg["isDeleted"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, + "isDeleted") + msg["distinguishedName"] = ldb.MessageElement([new_dn], + ldb.FLAG_MOD_REPLACE, + "distinguishedName") + self.test_ldb_dc.modify(msg, ["show_deleted:1"]) + + def sync_DCs(self, nc_dn=None): + # make sure DC1 has all the changes we've made to DC2 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, + nc_dn=nc_dn) + + def get_object_guid(self, dn): + res = self.test_ldb_dc.search(base=dn, attrs=["objectGUID"], + scope=ldb.SCOPE_BASE) + return res[0]['objectGUID'][0] + + def set_dc_connection(self, conn): + """ + Switches over the connection state info that the underlying drs_base + class uses so that we replicate with a different DC. + """ + self.default_hwm = conn.default_hwm + self.default_utdv = conn.default_utdv + self.drs = conn.drs + self.drs_handle = conn.drs_handle + self.set_test_ldb_dc(conn.ldb_dc) + + def assert_DCs_replication_is_consistent(self, peer_conn, all_objects, + expected_links): + """ + Replicates against both the primary and secondary DCs in the testenv + and checks that both return the expected results. + """ + print("Checking replication against primary test DC...") + + # get the replication data from the test DC first + while not self.replication_complete(): + self.repl_get_next() + + # Check we get all the objects and links we're expecting + self.assert_expected_data(all_objects) + self.assert_expected_links(expected_links) + + # switch over the DC state info so we now talk to the peer DC + self.set_dc_connection(peer_conn) + self.init_test_state() + + print("Checking replication against secondary test DC...") + + # check that we get the same information from the 2nd DC + while not self.replication_complete(): + self.repl_get_next() + + self.assert_expected_data(all_objects) + self.assert_expected_links(expected_links) + + # switch back to using the default connection + self.set_dc_connection(self.default_conn) + + def test_repl_integrity_obj_reanimation(self): + """ + Checks receiving links for a re-animated object doesn't lose links. + We test this against the peer DC to make sure it doesn't drop links. + """ + + # This test is a little different in that we're particularly interested + # in exercising the replmd client code on the second DC. + # First, make sure the peer DC has the base OU, then connect to it (so + # we store its initial HWM) + self.sync_DCs() + peer_conn = DcConnection(self, self.ldb_dc1, self.dnsname_dc1) + + # create the link source/target objects + la_sources = self.create_object_range(0, 100, prefix="la_src") + la_targets = self.create_object_range(0, 100, prefix="la_tgt") + + # store the target object's GUIDs (we need to know these to + # reanimate them) + target_guids = [] + + for dn in la_targets: + target_guids.append(self.get_object_guid(dn)) + + # delete the link target + for x in range(0, 100): + self.ldb_dc2.delete(la_targets[x]) + + # sync the DCs, then disable replication. We want the peer DC to get + # all the following changes in a single replication cycle + self.sync_DCs() + self._disable_all_repl(self.dnsname_dc2) + + # restore the target objects for the linked attributes again + for x in range(0, 100): + self.restore_deleted_object(target_guids[x], la_targets[x]) + + # add the links + for x in range(0, 100): + self.modify_object(la_sources[x], "managedBy", la_targets[x]) + + # create some additional filler objects + filler = self.create_object_range(0, 100, prefix="filler") + + # modify the targets so they now come last + for x in range(0, 100): + self.modify_object(la_targets[x], "displayName", "OU-%d" % x) + + # the objects should now be sent in the following order: + # [la sources + links][filler][la targets] + all_objects = la_sources + la_targets + filler + expected_links = la_sources + + # Enable replication again make sure the 2 DCs are back in sync + self._enable_all_repl(self.dnsname_dc2) + self.sync_DCs() + + # Get the replication data from each DC in turn. + # Check that both give us all the objects and links we're expecting, + # i.e. no links were lost + self.assert_DCs_replication_is_consistent(peer_conn, all_objects, + expected_links) + + def _test_repl_integrity_cross_partition_links(self, get_tgt=False): + """ + Checks that a cross-partition link to an unknown target object does + not result in missing links. + """ + + # check the peer DC is up-to-date, then connect (storing its HWM) + self.sync_DCs() + peer_conn = DcConnection(self, self.ldb_dc1, self.dnsname_dc1) + + # stop replication so the peer gets the following objects in one go + self._disable_all_repl(self.dnsname_dc2) + + # optionally force the client-side to use GET_TGT locally, by adding a + # one-way link to a missing/deleted target object + if get_tgt: + missing_target = "OU=missing_tgt,%s" % self.ou + self.add_object(missing_target) + get_tgt_source = "CN=get_tgt_src,%s" % self.ou + self.add_object(get_tgt_source, + objectclass="msExchConfigurationContainer") + self.modify_object(get_tgt_source, "addressBookRoots2", + missing_target) + self.test_ldb_dc.delete(missing_target) + + # create a link source object in the main NC + la_source = "OU=cross_nc_src,%s" % self.ou + self.add_object(la_source) + + # create the link target (a server object) in the config NC + sites_dn = "CN=Sites,%s" % self.config_dn + servers_dn = "CN=Servers,CN=Default-First-Site-Name,%s" % sites_dn + rand = random.randint(1, 10000000) + la_target = "CN=getncchanges-%d,%s" % (rand, servers_dn) + self.add_object(la_target, objectclass="server") + + # add a cross-partition link between the two + self.modify_object(la_source, "managedBy", la_target) + + # First, sync to the peer the NC containing the link source object + self.sync_DCs() + + # Now, before the peer has received the partition containing the target + # object, try replicating from the peer. It will only know about half + # of the link at this point, but it should be a valid scenario + self.set_dc_connection(peer_conn) + + while not self.replication_complete(): + # pretend we've received other link targets out of order and that's + # forced us to use GET_TGT. This checks the peer doesn't fail + # trying to fetch a cross-partition target object that doesn't + # exist + self.repl_get_next(get_tgt=True) + + self.set_dc_connection(self.default_conn) + + # delete the GET_TGT test object. We're not interested in asserting its + # links - it was just there to make the client use GET_TGT (and it + # creates an inconsistency because one DC correctly ignores the link, + # because it points to a deleted object) + if get_tgt: + self.test_ldb_dc.delete(get_tgt_source) + + self.init_test_state() + + # Now sync across the partition containing the link target object + self.sync_DCs(nc_dn=self.config_dn) + self._enable_all_repl(self.dnsname_dc2) + + # Get the replication data from each DC in turn. + # Check that both return the cross-partition link (note we're not + # checking the config domain NC here for simplicity) + self.assert_DCs_replication_is_consistent(peer_conn, + all_objects=[la_source], + expected_links=[la_source]) + + # the cross-partition linked attribute has a missing backlink. Check + # that we can still delete it successfully + self.delete_attribute(la_source, "managedBy", la_target) + self.sync_DCs() + + res = self.test_ldb_dc.search(ldb.Dn(self.ldb_dc1, la_source), + attrs=["managedBy"], + controls=['extended_dn:1:0'], + scope=ldb.SCOPE_BASE) + self.assertFalse("managedBy" in res[0], + "%s in DB still has managedBy attribute" % la_source) + res = self.test_ldb_dc.search(ldb.Dn(self.ldb_dc2, la_source), + attrs=["managedBy"], + controls=['extended_dn:1:0'], + scope=ldb.SCOPE_BASE) + self.assertFalse("managedBy" in res[0], + "%s in DB still has managedBy attribute" % la_source) + + # Check receiving a cross-partition link to a deleted target. + # Delete the target and make sure the deletion is sync'd between DCs + target_guid = self.get_object_guid(la_target) + self.test_ldb_dc.delete(la_target) + self.sync_DCs(nc_dn=self.config_dn) + self._disable_all_repl(self.dnsname_dc2) + + # re-animate the target + self.restore_deleted_object(target_guid, la_target) + self.modify_object(la_source, "managedBy", la_target) + + # now sync the link - because the target is in another partition, the + # peer DC receives a link for a deleted target, which it should accept + self.sync_DCs() + res = self.test_ldb_dc.search(ldb.Dn(self.ldb_dc1, la_source), + attrs=["managedBy"], + controls=['extended_dn:1:0'], + scope=ldb.SCOPE_BASE) + self.assertTrue("managedBy" in res[0], + "%s in DB missing managedBy attribute" % la_source) + + # cleanup the server object we created in the Configuration partition + self.test_ldb_dc.delete(la_target) + self._enable_all_repl(self.dnsname_dc2) + + def test_repl_integrity_cross_partition_links(self): + self._test_repl_integrity_cross_partition_links(get_tgt=False) + + def test_repl_integrity_cross_partition_links_with_tgt(self): + self._test_repl_integrity_cross_partition_links(get_tgt=True) + + def test_repl_get_tgt_multivalued_links(self): + """Tests replication with multi-valued link attributes.""" + + # create the target/source objects and link them together + la_targets = self.create_object_range(0, 500, prefix="la_tgt") + la_source = "CN=la_src,%s" % self.ou + self.add_object(la_source, objectclass="msExchConfigurationContainer") + + for tgt in la_targets: + self.modify_object(la_source, "addressBookRoots2", tgt) + + filler = self.create_object_range(0, 100, prefix="filler") + + # We should receive the objects/links in the following order: + # [500 targets + 1 source][500 links][100 filler] + expected_objects = la_targets + [la_source] + filler + link_only_chunk = False + + # First do the replication without needing GET_TGT + while not self.replication_complete(): + ctr6 = self.repl_get_next() + + if ctr6.object_count == 0 and ctr6.linked_attributes_count != 0: + link_only_chunk = True + + # we should receive one chunk that contains only links + self.assertTrue(link_only_chunk, + "Expected to receive a chunk containing only links") + + # check we received all the expected objects/links + self.assert_expected_data(expected_objects) + self.assert_expected_links([la_source], link_attr="addressBookRoots2", + num_expected=500) + + # Do the replication again, forcing the use of GET_TGT this time + self.init_test_state() + + for x in range(0, 500): + self.modify_object(la_targets[x], "displayName", "OU-%d" % x) + + # The objects/links should get sent in the following order: + # [1 source][500 targets][500 links][100 filler] + + while not self.replication_complete(): + ctr6 = self.repl_get_next() + + self.assertTrue(self.used_get_tgt, + "Test didn't use the GET_TGT flag as expected") + + # check we received all the expected objects/links + self.assert_expected_data(expected_objects) + self.assert_expected_links([la_source], link_attr="addressBookRoots2", + num_expected=500) + + + def test_InvalidNC_DummyDN_InvalidGUID_full_repl(self): + """Test full replication on a totally invalid GUID fails with the right error code""" + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=dc_guid_1, + nc_dn_str="DummyDN", + nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"), + exop=drsuapi.DRSUAPI_EXOP_NONE, + max_objects=1) + + (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, werror.WERR_DS_DRA_BAD_NC) + + def test_DummyDN_valid_GUID_full_repl(self): + dc_guid_1 = self.ldb_dc1.get_invocation_id() + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + res = self.ldb_dc1.search(base=self.base_dn, scope=SCOPE_BASE, + attrs=["objectGUID"]) + + guid = misc.GUID(res[0]["objectGUID"][0]) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str="DummyDN", + nc_guid=guid, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_GET_ANC, + exop=drsuapi.DRSUAPI_EXOP_NONE, + max_objects=1) + + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"Failed to call GetNCChanges with DummyDN and a GUID: {estr}") + + # The NC should be the first object returned due to GET_ANC + self.assertEqual(ctr.first_object.object.identifier.guid, guid) + + def _test_do_full_repl_no_overlap(self, mix=True, get_anc=False): + self.default_hwm = drsuapi.DsReplicaHighWaterMark() + + # We set get_anc=True so we can assert the BASE DN will be the + # first object + ctr6 = self._repl_send_request(get_anc=get_anc) + guid_list_1 = self._get_ctr6_object_guids(ctr6) + + if mix: + dc_guid_1 = self.ldb_dc1.get_invocation_id() + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=self.ldb_dc1.get_default_basedn(), + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + (level, ctr_repl_obj) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8) + + self.assertEqual(ctr_repl_obj.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS) + + repl_obj_guid_list = self._get_ctr6_object_guids(ctr_repl_obj) + + self.assertEqual(len(repl_obj_guid_list), 1) + + # This should be the first object in the main replication due + # to get_anc=True above in one case, and a rule that the NC must be first regardless otherwise + self.assertEqual(repl_obj_guid_list[0], guid_list_1[0]) + + self.last_ctr = ctr6 + ctr6 = self._repl_send_request(get_anc=True) + guid_list_2 = self._get_ctr6_object_guids(ctr6) + + self.assertNotEqual(guid_list_1, guid_list_2) + + def test_do_full_repl_no_overlap_get_anc(self): + """ + Make sure that a full replication on an nc succeeds to the goal despite needing multiple passes + """ + self._test_do_full_repl_no_overlap(mix=False, get_anc=True) + + def test_do_full_repl_no_overlap(self): + """ + Make sure that a full replication on an nc succeeds to the goal despite needing multiple passes + """ + self._test_do_full_repl_no_overlap(mix=False) + + def test_do_full_repl_mix_no_overlap(self): + """ + Make sure that a full replication on an nc succeeds to the goal despite needing multiple passes + + Assert this is true even if we do a REPL_OBJ in between the replications + + """ + self._test_do_full_repl_no_overlap(mix=True) + + def nc_change(self): + old_base_msg = self.default_conn.ldb_dc.search(base=self.base_dn, + scope=SCOPE_BASE, + attrs=["oEMInformation"]) + rec_cleanup = {"dn": self.base_dn, + "oEMInformation": old_base_msg[0]["oEMInformation"][0]} + m_cleanup = ldb.Message.from_dict(self.default_conn.ldb_dc, + rec_cleanup, + ldb.FLAG_MOD_REPLACE) + + self.addCleanup(self.default_conn.ldb_dc.modify, m_cleanup) + + rec = {"dn": self.base_dn, + "oEMInformation": f"Tortured by Samba's getncchanges.py {self.id()} against {self.default_conn.dnsname_dc}"} + m = ldb.Message.from_dict(self.default_conn.ldb_dc, rec, ldb.FLAG_MOD_REPLACE) + self.default_conn.ldb_dc.modify(m) + + def _test_repl_nc_is_first(self, start_at_zero=True, nc_change=True, ou_change=True, mid_change=False): + """Tests that the NC is always replicated first, but does not move the + tmp_highest_usn at that point, just like 'early' GET_ANC objects. + """ + + # create objects, twice more than the page size of 133 + objs = self.create_object_range(0, 300, prefix="obj") + + if nc_change: + self.nc_change() + + if mid_change: + # create even more objects + objs = self.create_object_range(301, 450, prefix="obj2") + + base_msg = self.default_conn.ldb_dc.search(base=self.base_dn, + scope=SCOPE_BASE, + attrs=["uSNChanged", + "objectGUID"]) + + base_guid = misc.GUID(base_msg[0]["objectGUID"][0]) + base_usn = int(base_msg[0]["uSNChanged"][0]) + + if ou_change: + # Make one more modification. We want to assert we have + # caught up to the base DN, but Windows both promotes the NC + # to the front and skips including it in the tmp_highest_usn, + # so we make a later modification that will be to show we get + # this change. + rec = {"dn": self.ou, + "postalCode": "0"} + m = ldb.Message.from_dict(self.default_conn.ldb_dc, rec, ldb.FLAG_MOD_REPLACE) + self.default_conn.ldb_dc.modify(m) + + ou_msg = self.default_conn.ldb_dc.search(base=self.ou, + scope=SCOPE_BASE, + attrs=["uSNChanged", + "objectGUID"]) + + ou_guid = misc.GUID(ou_msg[0]["objectGUID"][0]) + ou_usn = int(ou_msg[0]["uSNChanged"][0]) + + # Check some predicates about USN ordering that the below tests will rely on + if ou_change and nc_change: + self.assertGreater(ou_usn, base_usn) + elif not ou_change and nc_change: + self.assertGreater(base_usn, ou_usn) + + ctr6 = self.repl_get_next() + + guid_list_1 = self._get_ctr6_object_guids(ctr6) + if nc_change or start_at_zero: + self.assertEqual(base_guid, misc.GUID(guid_list_1[0])) + self.assertIn(str(base_guid), guid_list_1) + self.assertNotIn(str(base_guid), guid_list_1[1:]) + else: + self.assertNotEqual(base_guid, misc.GUID(guid_list_1[0])) + self.assertNotIn(str(base_guid), guid_list_1) + + self.assertTrue(ctr6.more_data) + + if not ou_change and nc_change: + self.assertLess(ctr6.new_highwatermark.tmp_highest_usn, base_usn) + + i = 0 + while not self.replication_complete(): + i = i + 1 + last_tmp_highest_usn = ctr6.new_highwatermark.tmp_highest_usn + ctr6 = self.repl_get_next() + guid_list_2 = self._get_ctr6_object_guids(ctr6) + if len(guid_list_2) > 0: + self.assertNotEqual(last_tmp_highest_usn, ctr6.new_highwatermark.tmp_highest_usn) + + if (nc_change or start_at_zero) and base_usn > last_tmp_highest_usn: + self.assertEqual(base_guid, misc.GUID(guid_list_2[0]), + f"pass={i} more_data={ctr6.more_data} base_usn={base_usn} tmp_highest_usn={ctr6.new_highwatermark.tmp_highest_usn} last_tmp_highest_usn={last_tmp_highest_usn}") + self.assertIn(str(base_guid), guid_list_2, + f"pass {i}·more_data={ctr6.more_data} base_usn={base_usn} tmp_highest_usn={ctr6.new_highwatermark.tmp_highest_usn} last_tmp_highest_usn={last_tmp_highest_usn}") + else: + self.assertNotIn(str(base_guid), guid_list_2, + f"pass {i}·more_data={ctr6.more_data} base_usn={base_usn} tmp_highest_usn={ctr6.new_highwatermark.tmp_highest_usn} last_tmp_highest_usn={last_tmp_highest_usn}") + + if ou_change: + # The modification to the base OU should be in the final chunk + self.assertIn(str(ou_guid), guid_list_2) + self.assertGreaterEqual(ctr6.new_highwatermark.highest_usn, + ou_usn) + else: + # Show that the NC root change does not show up in the + # highest_usn. We either get the change before or after + # it. + self.assertNotEqual(ctr6.new_highwatermark.highest_usn, + base_usn) + self.assertEqual(ctr6.new_highwatermark.highest_usn, + ctr6.new_highwatermark.tmp_highest_usn) + + self.assertFalse(ctr6.more_data) + + def test_repl_nc_is_first_start_zero_nc_change(self): + self.default_hwm = drsuapi.DsReplicaHighWaterMark() + self._test_repl_nc_is_first(start_at_zero=True, nc_change=True, ou_change=True) + + def test_repl_nc_is_first_start_zero(self): + # Get the NC change in the middle of the replication stream, certainly not at the start or end + self.nc_change() + self.default_hwm = drsuapi.DsReplicaHighWaterMark() + self._test_repl_nc_is_first(start_at_zero=True, nc_change=False, ou_change=False) + + def test_repl_nc_is_first_mid(self): + # This is a modification of the next test, that Samba + # will pass as it will always include the NC in the + # tmp_highest_usn at the point where it belongs + self._test_repl_nc_is_first(start_at_zero=False, + nc_change=True, + ou_change=True, + mid_change=True) + + def test_repl_nc_is_first(self): + # This is a modification of the next test, that Samba + # will pass as it will always include the NC in the + # tmp_highest_usn at the point where it belongs + self._test_repl_nc_is_first(start_at_zero=False, nc_change=True, ou_change=True) + + def test_repl_nc_is_first_nc_change_only(self): + # This shows that the NC change is not reflected in the tmp_highest_usn + self._test_repl_nc_is_first(start_at_zero=False, nc_change=True, ou_change=False) + + def test_repl_nc_is_first_no_change(self): + # The NC should not be present in this replication + self._test_repl_nc_is_first(start_at_zero=False, nc_change=False, ou_change=False) + +class DcConnection: + """Helper class to track a connection to another DC""" + + def __init__(self, drs_base, ldb_dc, dnsname_dc): + self.ldb_dc = ldb_dc + (self.drs, self.drs_handle) = drs_base._ds_bind(dnsname_dc) + (self.default_hwm, utdv) = drs_base._get_highest_hwm_utdv(ldb_dc) + self.default_utdv = utdv + self.dnsname_dc = dnsname_dc diff --git a/source4/torture/drs/python/link_conflicts.py b/source4/torture/drs/python/link_conflicts.py new file mode 100644 index 0000000..d344b7e --- /dev/null +++ b/source4/torture/drs/python/link_conflicts.py @@ -0,0 +1,763 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests replication scenarios that involve conflicting linked attribute +# information between the 2 DCs. +# +# Copyright (C) Catalyst.Net Ltd. 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN \ +# link_conflicts -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +import ldb +from ldb import SCOPE_BASE +import random +import time + +from drs_base import AbstractLink +from samba.dcerpc import drsuapi, misc +from samba.dcerpc.drsuapi import DRSUAPI_EXOP_ERR_SUCCESS + +# specifies the order to sync DCs in +DC1_TO_DC2 = 1 +DC2_TO_DC1 = 2 + + +class DrsReplicaLinkConflictTestCase(drs_base.DrsBaseTestCase): + def setUp(self): + super(DrsReplicaLinkConflictTestCase, self).setUp() + + self.ou = samba.tests.create_test_ou(self.ldb_dc1, + "test_link_conflict") + self.base_dn = self.ldb_dc1.get_default_basedn() + + (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) + (self.drs2, self.drs2_handle) = self._ds_bind(self.dnsname_dc2) + + # disable replication for the tests so we can control at what point + # the DCs try to replicate + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + def tearDown(self): + # re-enable replication + self._enable_inbound_repl(self.dnsname_dc1) + self._enable_inbound_repl(self.dnsname_dc2) + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + super(DrsReplicaLinkConflictTestCase, self).tearDown() + + def get_guid(self, samdb, dn): + """Returns an object's GUID (in string format)""" + res = samdb.search(base=dn, attrs=["objectGUID"], scope=ldb.SCOPE_BASE) + return self._GUID_string(res[0]['objectGUID'][0]) + + def add_object(self, samdb, dn, objectclass="organizationalunit"): + """Adds an object""" + samdb.add({"dn": dn, "objectclass": objectclass}) + return self.get_guid(samdb, dn) + + def modify_object(self, samdb, dn, attr, value): + """Modifies an attribute for an object""" + m = ldb.Message() + m.dn = ldb.Dn(samdb, dn) + m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_ADD, attr) + samdb.modify(m) + + def add_link_attr(self, samdb, source_dn, attr, target_dn): + """Adds a linked attribute between 2 objects""" + # add the specified attribute to the source object + self.modify_object(samdb, source_dn, attr, target_dn) + + def del_link_attr(self, samdb, src, attr, target): + m = ldb.Message() + m.dn = ldb.Dn(samdb, src) + m[attr] = ldb.MessageElement(target, ldb.FLAG_MOD_DELETE, attr) + samdb.modify(m) + + def sync_DCs(self, sync_order=DC1_TO_DC2): + """Manually syncs the 2 DCs to ensure they're in sync""" + if sync_order == DC1_TO_DC2: + # sync DC1-->DC2, then DC2-->DC1 + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1) + self._net_drs_replicate(DC=self.dnsname_dc1, + fromDC=self.dnsname_dc2) + else: + # sync DC2-->DC1, then DC1-->DC2 + self._net_drs_replicate(DC=self.dnsname_dc1, + fromDC=self.dnsname_dc2) + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1) + + def ensure_unique_timestamp(self): + """Waits a second to ensure a unique timestamp between 2 objects""" + time.sleep(1) + + def unique_dn(self, obj_name): + """Returns a unique object DN""" + # Because we run each test case twice, we need to create a unique DN so + # that the 2nd run doesn't hit objects that already exist. Add some + # randomness to the object DN to make it unique + rand = random.randint(1, 10000000) + return "%s-%d,%s" % (obj_name, rand, self.ou) + + def assert_attrs_match(self, res1, res2, attr, expected_count): + """ + Asserts that the search results contain the expected number of + attributes and the results match on both DCs + """ + actual_len = len(res1[0][attr]) + self.assertTrue(actual_len == expected_count, + "Expected %u %s attributes, got %u" % (expected_count, + attr, + actual_len)) + actual_len = len(res2[0][attr]) + self.assertTrue(actual_len == expected_count, + "Expected %u %s attributes, got %u" % (expected_count, + attr, + actual_len)) + + # check DCs both agree on the same linked attributes + for val in res1[0][attr]: + self.assertTrue(val in res2[0][attr], + "%s '%s' not found on DC2" % (attr, val)) + + def zero_highwatermark(self): + """Returns a zeroed highwatermark so that all DRS data gets returned""" + hwm = drsuapi.DsReplicaHighWaterMark() + hwm.tmp_highest_usn = 0 + hwm.reserved_usn = 0 + hwm.highest_usn = 0 + return hwm + + def _check_replicated_links(self, src_obj_dn, expected_links): + """Checks that replication sends back the expected linked attributes""" + self._check_replication([src_obj_dn], + drsuapi.DRSUAPI_DRS_WRIT_REP, + dest_dsa=None, + drs_error=drsuapi.DRSUAPI_EXOP_ERR_SUCCESS, + nc_dn_str=src_obj_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + expected_links=expected_links, + highwatermark=self.zero_highwatermark()) + + # Check DC2 as well + self.set_test_ldb_dc(self.ldb_dc2) + + self._check_replication([src_obj_dn], + drsuapi.DRSUAPI_DRS_WRIT_REP, + dest_dsa=None, + drs_error=drsuapi.DRSUAPI_EXOP_ERR_SUCCESS, + nc_dn_str=src_obj_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + expected_links=expected_links, + highwatermark=self.zero_highwatermark(), + drs=self.drs2, drs_handle=self.drs2_handle) + self.set_test_ldb_dc(self.ldb_dc1) + + def _test_conflict_single_valued_link(self, sync_order): + """ + Tests a simple single-value link conflict, i.e. each DC adds a link to + the same source object but linking to different targets. + """ + src_ou = self.unique_dn("OU=src") + src_guid = self.add_object(self.ldb_dc1, src_ou) + self.sync_DCs() + + # create a unique target on each DC + target1_ou = self.unique_dn("OU=target1") + target2_ou = self.unique_dn("OU=target2") + + target1_guid = self.add_object(self.ldb_dc1, target1_ou) + target2_guid = self.add_object(self.ldb_dc2, target2_ou) + + # link the test OU to the respective targets created + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_ou, "managedBy", target2_ou) + + # sync the 2 DCs + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + + # check the object has only have one occurrence of the single-valued + # attribute and it matches on both DCs + self.assert_attrs_match(res1, res2, "managedBy", 1) + + self.assertTrue(str(res1[0]["managedBy"][0]) == target2_ou, + "Expected most recent update to win conflict") + + # we can't query the deleted links over LDAP, but we can check DRS + # to make sure the DC kept a copy of the conflicting link + link1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, 0, + misc.GUID(src_guid), misc.GUID(target1_guid)) + link2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + misc.GUID(src_guid), misc.GUID(target2_guid)) + self._check_replicated_links(src_ou, [link1, link2]) + + def test_conflict_single_valued_link(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_conflict_single_valued_link(sync_order=DC1_TO_DC2) + self._test_conflict_single_valued_link(sync_order=DC2_TO_DC1) + + def _test_duplicate_single_valued_link(self, sync_order): + """ + Adds the same single-valued link on 2 DCs and checks we don't end up + with 2 copies of the link. + """ + # create unique objects for the link + target_ou = self.unique_dn("OU=target") + self.add_object(self.ldb_dc1, target_ou) + src_ou = self.unique_dn("OU=src") + src_guid = self.add_object(self.ldb_dc1, src_ou) + self.sync_DCs() + + # link the same test OU to the same target on both DCs + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target_ou) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_ou, "managedBy", target_ou) + + # sync the 2 DCs + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + + # check the object has only have one occurrence of the single-valued + # attribute and it matches on both DCs + self.assert_attrs_match(res1, res2, "managedBy", 1) + + def test_duplicate_single_valued_link(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_duplicate_single_valued_link(sync_order=DC1_TO_DC2) + self._test_duplicate_single_valued_link(sync_order=DC2_TO_DC1) + + def _test_conflict_multi_valued_link(self, sync_order): + """ + Tests a simple multi-valued link conflict. This adds 2 objects with the + same username on 2 different DCs and checks their group membership is + preserved after the conflict is resolved. + """ + + # create a common link source + src_dn = self.unique_dn("CN=src") + src_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + self.sync_DCs() + + # create the same user (link target) on each DC. + # Note that the GUIDs will differ between the DCs + target_dn = self.unique_dn("CN=target") + target1_guid = self.add_object(self.ldb_dc1, target_dn, + objectclass="user") + self.ensure_unique_timestamp() + target2_guid = self.add_object(self.ldb_dc2, target_dn, + objectclass="user") + + # link the src group to the respective target created + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + + # sync the 2 DCs. We expect the more recent target2 object to win + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + target1_conflict = False + + # we expect exactly 2 members in our test group (both DCs should agree) + self.assert_attrs_match(res1, res2, "member", 2) + + for val in [str(val) for val in res1[0]["member"]]: + # check the expected conflicting object was renamed + self.assertFalse("CNF:%s" % target2_guid in val) + if "CNF:%s" % target1_guid in val: + target1_conflict = True + + self.assertTrue(target1_conflict, + "Expected link to conflicting target object not found") + + def test_conflict_multi_valued_link(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_conflict_multi_valued_link(sync_order=DC1_TO_DC2) + self._test_conflict_multi_valued_link(sync_order=DC2_TO_DC1) + + def _test_duplicate_multi_valued_link(self, sync_order): + """ + Adds the same multivalued link on 2 DCs and checks we don't end up + with 2 copies of the link. + """ + + # create the link source/target objects + src_dn = self.unique_dn("CN=src") + src_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + target_dn = self.unique_dn("CN=target") + self.add_object(self.ldb_dc1, target_dn, objectclass="user") + self.sync_DCs() + + # link the src group to the same target user separately on each DC + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + + # we expect to still have only 1 member in our test group + self.assert_attrs_match(res1, res2, "member", 1) + + def test_duplicate_multi_valued_link(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_duplicate_multi_valued_link(sync_order=DC1_TO_DC2) + self._test_duplicate_multi_valued_link(sync_order=DC2_TO_DC1) + + def _test_conflict_backlinks(self, sync_order): + """ + Tests that resolving a source object conflict fixes up any backlinks, + e.g. the same user is added to a conflicting group. + """ + + # create a common link target + target_dn = self.unique_dn("CN=target") + target_guid = self.add_object(self.ldb_dc1, target_dn, + objectclass="user") + self.sync_DCs() + + # create the same group (link source) on each DC. + # Note that the GUIDs will differ between the DCs + src_dn = self.unique_dn("CN=src") + src1_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + self.ensure_unique_timestamp() + src2_guid = self.add_object(self.ldb_dc2, src_dn, objectclass="group") + + # link the src group to the respective target created + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + + # sync the 2 DCs. We expect the more recent src2 object to win + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % target_guid, + scope=SCOPE_BASE, attrs=["memberOf"]) + res2 = self.ldb_dc2.search(base="" % target_guid, + scope=SCOPE_BASE, attrs=["memberOf"]) + src1_backlink = False + + # our test user should still be a member of 2 groups (check both + # DCs agree) + self.assert_attrs_match(res1, res2, "memberOf", 2) + + for val in [str(val) for val in res1[0]["memberOf"]]: + # check the conflicting object was renamed + self.assertFalse("CNF:%s" % src2_guid in val) + if "CNF:%s" % src1_guid in val: + src1_backlink = True + + self.assertTrue(src1_backlink, + "Backlink to conflicting source object not found") + + def test_conflict_backlinks(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_conflict_backlinks(sync_order=DC1_TO_DC2) + self._test_conflict_backlinks(sync_order=DC2_TO_DC1) + + def _test_link_deletion_conflict(self, sync_order): + """ + Checks that a deleted link conflicting with an active link is + resolved correctly. + """ + + # Add the link objects + target_dn = self.unique_dn("CN=target") + self.add_object(self.ldb_dc1, target_dn, objectclass="user") + src_dn = self.unique_dn("CN=src") + src_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + self.sync_DCs() + + # add the same link on both DCs, and resolve any conflict + self.add_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.sync_DCs(sync_order=sync_order) + + # delete and re-add the link on one DC + self.del_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + + # just delete it on the other DC + self.ensure_unique_timestamp() + self.del_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + # sanity-check the link is gone on this DC + res1 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + self.assertFalse("member" in res1[0], "Couldn't delete member attr") + + # sync the 2 DCs. We expect the more older DC1 attribute to win + # because it has a higher version number (even though it's older) + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + + # our test user should still be a member of the group (check both + # DCs agree) + self.assertTrue("member" in res1[0], + "Expected member attribute missing") + self.assert_attrs_match(res1, res2, "member", 1) + + def test_link_deletion_conflict(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_link_deletion_conflict(sync_order=DC1_TO_DC2) + self._test_link_deletion_conflict(sync_order=DC2_TO_DC1) + + def _test_obj_deletion_conflict(self, sync_order, del_target): + """ + Checks that a receiving a new link for a deleted object gets + resolved correctly. + """ + + target_dn = self.unique_dn("CN=target") + target_guid = self.add_object(self.ldb_dc1, target_dn, + objectclass="user") + src_dn = self.unique_dn("CN=src") + src_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + + self.sync_DCs() + + # delete the object on one DC + if del_target: + search_guid = src_guid + self.ldb_dc2.delete(target_dn) + else: + search_guid = target_guid + self.ldb_dc2.delete(src_dn) + + # add a link on the other DC + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + + self.sync_DCs(sync_order=sync_order) + + # the object deletion should trump the link addition. + # Check the link no longer exists on the remaining object + res1 = self.ldb_dc1.search(base="" % search_guid, + scope=SCOPE_BASE, + attrs=["member", "memberOf"]) + res2 = self.ldb_dc2.search(base="" % search_guid, + scope=SCOPE_BASE, + attrs=["member", "memberOf"]) + + self.assertFalse("member" in res1[0], "member attr shouldn't exist") + self.assertFalse("member" in res2[0], "member attr shouldn't exist") + self.assertFalse("memberOf" in res1[0], "member attr shouldn't exist") + self.assertFalse("memberOf" in res2[0], "member attr shouldn't exist") + + def test_obj_deletion_conflict(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_obj_deletion_conflict(sync_order=DC1_TO_DC2, + del_target=True) + self._test_obj_deletion_conflict(sync_order=DC2_TO_DC1, + del_target=True) + + # and also try deleting the source object instead of the link target + self._test_obj_deletion_conflict(sync_order=DC1_TO_DC2, + del_target=False) + self._test_obj_deletion_conflict(sync_order=DC2_TO_DC1, + del_target=False) + + def _test_full_sync_link_conflict(self, sync_order): + """ + Checks that doing a full sync doesn't affect how conflicts get resolved + """ + + # create the objects for the linked attribute + src_dn = self.unique_dn("CN=src") + src_guid = self.add_object(self.ldb_dc1, src_dn, objectclass="group") + target_dn = self.unique_dn("CN=target") + self.add_object(self.ldb_dc1, target_dn, objectclass="user") + self.sync_DCs() + + # add the same link on both DCs + self.add_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + + # Do a couple of full syncs which should resolve the conflict + # (but only for one DC) + if sync_order == DC1_TO_DC2: + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + full_sync=True) + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + full_sync=True) + else: + self._net_drs_replicate(DC=self.dnsname_dc1, + fromDC=self.dnsname_dc2, + full_sync=True) + self._net_drs_replicate(DC=self.dnsname_dc1, + fromDC=self.dnsname_dc2, + full_sync=True) + + # delete and re-add the link on one DC + self.del_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc1, src_dn, "member", target_dn) + + # just delete the link on the 2nd DC + self.ensure_unique_timestamp() + self.del_link_attr(self.ldb_dc2, src_dn, "member", target_dn) + + # sync the 2 DCs. We expect DC1 to win based on version number + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["member"]) + + # check the membership still exits (and both DCs agree) + self.assertTrue("member" in res1[0], + "Expected member attribute missing") + self.assert_attrs_match(res1, res2, "member", 1) + + def test_full_sync_link_conflict(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_full_sync_link_conflict(sync_order=DC1_TO_DC2) + self._test_full_sync_link_conflict(sync_order=DC2_TO_DC1) + + def _singleval_link_conflict_deleted_winner(self, sync_order): + """ + Tests a single-value link conflict where the more-up-to-date link value + is deleted. + """ + src_ou = self.unique_dn("OU=src") + src_guid = self.add_object(self.ldb_dc1, src_ou) + self.sync_DCs() + + # create a unique target on each DC + target1_ou = self.unique_dn("OU=target1") + target2_ou = self.unique_dn("OU=target2") + + target1_guid = self.add_object(self.ldb_dc1, target1_ou) + target2_guid = self.add_object(self.ldb_dc2, target2_ou) + + # add the links for the respective targets, and delete one of the links + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.add_link_attr(self.ldb_dc2, src_ou, "managedBy", target2_ou) + self.ensure_unique_timestamp() + self.del_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + + # sync the 2 DCs + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + + # Although the more up-to-date link value is deleted, this shouldn't + # trump DC1's active link + self.assert_attrs_match(res1, res2, "managedBy", 1) + + self.assertTrue(str(res1[0]["managedBy"][0]) == target2_ou, + "Expected active link win conflict") + + # we can't query the deleted links over LDAP, but we can check that + # the deleted links exist using DRS + link1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, 0, + misc.GUID(src_guid), misc.GUID(target1_guid)) + link2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + misc.GUID(src_guid), misc.GUID(target2_guid)) + self._check_replicated_links(src_ou, [link1, link2]) + + def test_conflict_single_valued_link_deleted_winner(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._singleval_link_conflict_deleted_winner(sync_order=DC1_TO_DC2) + self._singleval_link_conflict_deleted_winner(sync_order=DC2_TO_DC1) + + def _singleval_link_conflict_deleted_loser(self, sync_order): + """ + Tests a single-valued link conflict, where the losing link value is + deleted. + """ + src_ou = self.unique_dn("OU=src") + src_guid = self.add_object(self.ldb_dc1, src_ou) + self.sync_DCs() + + # create a unique target on each DC + target1_ou = self.unique_dn("OU=target1") + target2_ou = self.unique_dn("OU=target2") + + target1_guid = self.add_object(self.ldb_dc1, target1_ou) + target2_guid = self.add_object(self.ldb_dc2, target2_ou) + + # add the links - we want the link to end up deleted on DC2, but active + # on DC1. DC1 has the better version and DC2 has the better timestamp - + # the better version should win + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.del_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_ou, "managedBy", target2_ou) + self.del_link_attr(self.ldb_dc2, src_ou, "managedBy", target2_ou) + + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + + # check the object has only have one occurrence of the single-valued + # attribute and it matches on both DCs + self.assert_attrs_match(res1, res2, "managedBy", 1) + + self.assertTrue(str(res1[0]["managedBy"][0]) == target1_ou, + "Expected most recent update to win conflict") + + # we can't query the deleted links over LDAP, but we can check DRS + # to make sure the DC kept a copy of the conflicting link + link1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + misc.GUID(src_guid), misc.GUID(target1_guid)) + link2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, 0, + misc.GUID(src_guid), misc.GUID(target2_guid)) + self._check_replicated_links(src_ou, [link1, link2]) + + def test_conflict_single_valued_link_deleted_loser(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._singleval_link_conflict_deleted_loser(sync_order=DC1_TO_DC2) + self._singleval_link_conflict_deleted_loser(sync_order=DC2_TO_DC1) + + def _test_conflict_existing_single_valued_link(self, sync_order): + """ + Tests a single-valued link conflict, where the conflicting link value + already exists (as inactive) on both DCs. + """ + # create the link objects + src_ou = self.unique_dn("OU=src") + src_guid = self.add_object(self.ldb_dc1, src_ou) + + target1_ou = self.unique_dn("OU=target1") + target2_ou = self.unique_dn("OU=target2") + target1_guid = self.add_object(self.ldb_dc1, target1_ou) + target2_guid = self.add_object(self.ldb_dc1, target2_ou) + + # add the links, but then delete them + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.del_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target2_ou) + self.del_link_attr(self.ldb_dc1, src_ou, "managedBy", target2_ou) + self.sync_DCs() + + # re-add the links independently on each DC + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + self.ensure_unique_timestamp() + self.add_link_attr(self.ldb_dc2, src_ou, "managedBy", target2_ou) + + # try to sync the 2 DCs + self.sync_DCs(sync_order=sync_order) + + res1 = self.ldb_dc1.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + res2 = self.ldb_dc2.search(base="" % src_guid, + scope=SCOPE_BASE, attrs=["managedBy"]) + + # check the object has only have one occurrence of the single-valued + # attribute and it matches on both DCs + self.assert_attrs_match(res1, res2, "managedBy", 1) + + # here we expect DC2 to win because it has the more recent link + self.assertTrue(str(res1[0]["managedBy"][0]) == target2_ou, + "Expected most recent update to win conflict") + + # we can't query the deleted links over LDAP, but we can check DRS + # to make sure the DC kept a copy of the conflicting link + link1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, 0, + misc.GUID(src_guid), misc.GUID(target1_guid)) + link2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, + drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, + misc.GUID(src_guid), misc.GUID(target2_guid)) + self._check_replicated_links(src_ou, [link1, link2]) + + def test_conflict_existing_single_valued_link(self): + # repeat the test twice, to give each DC a chance to resolve + # the conflict + self._test_conflict_existing_single_valued_link(sync_order=DC1_TO_DC2) + self._test_conflict_existing_single_valued_link(sync_order=DC2_TO_DC1) + + def test_link_attr_version(self): + """ + Checks the link attribute version starts from the correct value + """ + # create some objects and add a link + src_ou = self.unique_dn("OU=src") + self.add_object(self.ldb_dc1, src_ou) + target1_ou = self.unique_dn("OU=target1") + self.add_object(self.ldb_dc1, target1_ou) + self.add_link_attr(self.ldb_dc1, src_ou, "managedBy", target1_ou) + + # get the link info via replication + ctr6 = self._get_replication(drsuapi.DRSUAPI_DRS_WRIT_REP, + dest_dsa=None, + drs_error=DRSUAPI_EXOP_ERR_SUCCESS, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + highwatermark=self.zero_highwatermark(), + nc_dn_str=src_ou) + + self.assertTrue(ctr6.linked_attributes_count == 1, + "DRS didn't return a link") + link = ctr6.linked_attributes[0] + rcvd_version = link.meta_data.version + self.assertTrue(rcvd_version == 1, + "Link version started from %u, not 1" % rcvd_version) diff --git a/source4/torture/drs/python/linked_attributes_drs.py b/source4/torture/drs/python/linked_attributes_drs.py new file mode 100644 index 0000000..93ad313 --- /dev/null +++ b/source4/torture/drs/python/linked_attributes_drs.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Originally based on ./sam.py +import sys + +sys.path.insert(0, "bin/python") +import ldb + +from samba.dcerpc import drsuapi, misc +from samba.ndr import ndr_unpack, ndr_pack + +import drs_base + + +class LATestException(Exception): + pass + + +class LATests(drs_base.DrsBaseTestCase): + + def setUp(self): + super(LATests, self).setUp() + # DrsBaseTestCase sets up self.ldb_dc1, self.ldb_dc2 + # we're only using one + self.samdb = self.ldb_dc1 + + self.base_dn = self.samdb.domain_dn() + self.ou = "OU=la,%s" % self.base_dn + if True: + try: + self.samdb.delete(self.ou, ['tree_delete:1']) + except ldb.LdbError as e: + pass + self.samdb.add({'objectclass': 'organizationalUnit', + 'dn': self.ou}) + + self.dc_guid = self.samdb.get_invocation_id() + self.drs, self.drs_handle = self._ds_bind(self.dnsname_dc1) + + def tearDown(self): + super(LATests, self).tearDown() + try: + self.samdb.delete(self.ou, ['tree_delete:1']) + except ldb.LdbError as e: + pass + + def delete_user(self, user): + self.samdb.delete(user['dn']) + del self.users[self.users.index(user)] + + def add_object(self, cn, objectclass): + dn = "CN=%s,%s" % (cn, self.ou) + self.samdb.add({'cn': cn, + 'objectclass': objectclass, + 'dn': dn}) + + return dn + + def add_objects(self, n, objectclass, prefix=None): + if prefix is None: + prefix = objectclass + dns = [] + for i in range(n): + dns.append(self.add_object("%s%d" % (prefix, i + 1), + objectclass)) + return dns + + def add_linked_attribute(self, src, dest, attr='member'): + m = ldb.Message() + m.dn = ldb.Dn(self.samdb, src) + m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_ADD, attr) + self.samdb.modify(m) + + def remove_linked_attribute(self, src, dest, attr='member'): + m = ldb.Message() + m.dn = ldb.Dn(self.samdb, src) + m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_DELETE, attr) + self.samdb.modify(m) + + def attr_search(self, obj, expected, attr, scope=ldb.SCOPE_BASE): + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=self.dc_guid, + nc_dn_str=obj, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) + + level, ctr = self.drs.DsGetNCChanges(self.drs_handle, 8, req8) + expected_attid = getattr(drsuapi, 'DRSUAPI_ATTID_' + attr) + + links = [] + for link in ctr.linked_attributes: + if link.attid == expected_attid: + unpacked = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, + link.value.blob) + active = link.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + links.append((str(unpacked.dn), bool(active))) + + return links + + def assert_forward_links(self, obj, expected, attr='member'): + results = self.attr_search(obj, expected, attr) + self.assertEqual(len(results), len(expected)) + + for k, v in results: + self.assertTrue(k in expected) + self.assertEqual(expected[k], v, "%s active flag should be %d, not %d" % + (k, expected[k], v)) + + def get_object_guid(self, dn): + res = self.samdb.search(dn, + scope=ldb.SCOPE_BASE, + attrs=['objectGUID']) + return str(misc.GUID(res[0]['objectGUID'][0])) + + def test_links_all_delete_group(self): + u1, u2 = self.add_objects(2, 'user', 'u_all_del_group') + g1, g2 = self.add_objects(2, 'group', 'g_all_del_group') + g2guid = self.get_object_guid(g2) + + self.add_linked_attribute(g1, u1) + self.add_linked_attribute(g2, u1) + self.add_linked_attribute(g2, u2) + + self.samdb.delete(g2) + self.assert_forward_links(g1, {u1: True}) + res = self.samdb.search('' % g2guid, + scope=ldb.SCOPE_BASE, + controls=['show_deleted:1']) + new_dn = res[0].dn + self.assert_forward_links(new_dn, {}) + + def test_la_links_delete_link(self): + u1, u2 = self.add_objects(2, 'user', 'u_del_link') + g1, g2 = self.add_objects(2, 'group', 'g_del_link') + + self.add_linked_attribute(g1, u1) + self.add_linked_attribute(g2, u1) + self.add_linked_attribute(g2, u2) + + self.remove_linked_attribute(g2, u1) + + self.assert_forward_links(g1, {u1: True}) + self.assert_forward_links(g2, {u1: False, u2: True}) + + self.add_linked_attribute(g2, u1) + self.remove_linked_attribute(g2, u2) + self.assert_forward_links(g2, {u1: True, u2: False}) + self.remove_linked_attribute(g2, u1) + self.assert_forward_links(g2, {u1: False, u2: False}) + + def test_la_links_delete_user(self): + u1, u2 = self.add_objects(2, 'user', 'u_del_user') + g1, g2 = self.add_objects(2, 'group', 'g_del_user') + + self.add_linked_attribute(g1, u1) + self.add_linked_attribute(g2, u1) + self.add_linked_attribute(g2, u2) + + self.samdb.delete(u1) + + self.assert_forward_links(g1, {}) + self.assert_forward_links(g2, {u2: True}) diff --git a/source4/torture/drs/python/repl_move.py b/source4/torture/drs/python/repl_move.py new file mode 100644 index 0000000..c206ab8 --- /dev/null +++ b/source4/torture/drs/python/repl_move.py @@ -0,0 +1,2608 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Kamen Mazdrashki 2010 +# Copyright (C) Andrew Bartlett 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN repl_move -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import time +import samba.tests + +from samba.ndr import ndr_unpack +from samba.dcerpc import drsblobs +from samba.dcerpc import misc +from samba.drs_utils import drs_DsBind + +from ldb import ( + SCOPE_BASE, + SCOPE_SUBTREE, +) + +import drs_base +import ldb +from samba.dcerpc.drsuapi import ( + drsuapi, + DRSUAPI_ATTID_accountExpires, + DRSUAPI_ATTID_cn, + DRSUAPI_ATTID_codePage, + DRSUAPI_ATTID_countryCode, + DRSUAPI_ATTID_dBCSPwd, + DRSUAPI_ATTID_description, + DRSUAPI_ATTID_instanceType, + DRSUAPI_ATTID_isDeleted, + DRSUAPI_ATTID_isRecycled, + DRSUAPI_ATTID_lastKnownParent, + DRSUAPI_ATTID_lmPwdHistory, + DRSUAPI_ATTID_logonHours, + DRSUAPI_ATTID_name, + DRSUAPI_ATTID_ntPwdHistory, + DRSUAPI_ATTID_ntSecurityDescriptor, + DRSUAPI_ATTID_objectCategory, + DRSUAPI_ATTID_objectClass, + DRSUAPI_ATTID_objectSid, + DRSUAPI_ATTID_ou, + DRSUAPI_ATTID_primaryGroupID, + DRSUAPI_ATTID_pwdLastSet, + DRSUAPI_ATTID_sAMAccountName, + DRSUAPI_ATTID_sAMAccountType, + DRSUAPI_ATTID_unicodePwd, + DRSUAPI_ATTID_userAccountControl, + DRSUAPI_ATTID_userPrincipalName, + DRSUAPI_ATTID_whenCreated, + DRSUAPI_DRS_SYNC_FORCED, + DRSUAPI_EXOP_REPL_OBJ, + DsGetNCChangesRequest8, + DsReplicaHighWaterMark, + DsReplicaObjectIdentifier) + + +class DrsMoveObjectTestCase(drs_base.DrsBaseTestCase): + + def setUp(self): + super(DrsMoveObjectTestCase, self).setUp() + # disable automatic replication temporary + self._disable_all_repl(self.dnsname_dc1) + self._disable_all_repl(self.dnsname_dc2) + + # make sure DCs are synchronized before the test + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + self.top_ou = samba.tests.create_test_ou(self.ldb_dc1, + "replica_move") + + self.ou1_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU1") + self.ou1_dn.add_base(self.top_ou) + ou1 = {} + ou1["dn"] = self.ou1_dn + ou1["objectclass"] = "organizationalUnit" + ou1["ou"] = self.ou1_dn.get_component_value(0) + self.ldb_dc1.add(ou1) + + self.ou2_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2") + self.ou2_dn.add_base(self.top_ou) + ou2 = {} + ou2["dn"] = self.ou2_dn + ou2["objectclass"] = "organizationalUnit" + ou2["ou"] = self.ou2_dn.get_component_value(0) + self.ldb_dc1.add(ou2) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self.dc1_guid = self.ldb_dc1.get_invocation_id() + self.dc2_guid = self.ldb_dc2.get_invocation_id() + + self.drs_dc1 = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + self.drs_dc2 = self._ds_bind(self.dnsname_dc2, ip=self.url_dc2) + + def tearDown(self): + try: + self.ldb_dc1.delete(self.top_ou, ["tree_delete:1"]) + except ldb.LdbError as e: + (enum, string) = e.args + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + + self._enable_all_repl(self.dnsname_dc1) + self._enable_all_repl(self.dnsname_dc2) + super(DrsMoveObjectTestCase, self).tearDown() + + def _make_username(self): + return "DrsMoveU_" + time.strftime("%s", time.gmtime()) + + def _check_metadata(self, user_dn, sam_ldb, drs, metadata, expected): + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, metadata[0]) + + self.assertEqual(len(repl.ctr.array), len(expected)) + + i = 0 + for o in repl.ctr.array: + e = expected[i] + (attid, orig_dsa, version) = e + self.assertEqual(attid, o.attid, + "(LDAP) Wrong attid " + "for expected value %d, wanted 0x%08x got 0x%08x" + % (i, attid, o.attid)) + self.assertEqual(o.originating_invocation_id, + misc.GUID(orig_dsa), + "(LDAP) Wrong originating_invocation_id " + "for expected value %d, attid 0x%08x, wanted %s got %s" + % (i, o.attid, + misc.GUID(orig_dsa), + o.originating_invocation_id)) + # Allow version to be skipped when it does not matter + if version is not None: + self.assertEqual(o.version, version, + "(LDAP) Wrong version for expected value %d, " + "attid 0x%08x, " + "wanted %d got %d" + % (i, o.attid, + version, o.version)) + i = i + 1 + + if drs is None: + return + + req8 = DsGetNCChangesRequest8() + + req8.source_dsa_invocation_id = misc.GUID(sam_ldb.get_invocation_id()) + req8.naming_context = DsReplicaObjectIdentifier() + req8.naming_context.dn = str(user_dn) + req8.highwatermark = DsReplicaHighWaterMark() + req8.highwatermark.tmp_highest_usn = 0 + req8.highwatermark.reserved_usn = 0 + req8.highwatermark.highest_usn = 0 + req8.uptodateness_vector = None + req8.replica_flags = DRSUAPI_DRS_SYNC_FORCED + req8.max_object_count = 1 + req8.max_ndr_size = 402116 + req8.extended_op = DRSUAPI_EXOP_REPL_OBJ + req8.fsmo_info = 0 + req8.partial_attribute_set = None + req8.partial_attribute_set_ex = None + req8.mapping_ctr.num_mappings = 0 + req8.mapping_ctr.mappings = None + + (drs_conn, drs_handle) = drs + + (level, drs_ctr) = drs_conn.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6) + self.assertEqual(drs_ctr.object_count, 1) + + self.assertEqual(len(drs_ctr.first_object.meta_data_ctr.meta_data), len(expected) - 1) + att_idx = 0 + for o in drs_ctr.first_object.meta_data_ctr.meta_data: + i = 0 + drs_attid = drs_ctr.first_object.object.attribute_ctr.attributes[att_idx] + e = expected[i] + (attid, orig_dsa, version) = e + + # Skip the RDN from the expected set, it is not sent over DRS + if (user_dn.get_rdn_name().upper() == "CN" + and attid == DRSUAPI_ATTID_cn) \ + or (user_dn.get_rdn_name().upper() == "OU" + and attid == DRSUAPI_ATTID_ou): + i = i + 1 + e = expected[i] + (attid, orig_dsa, version) = e + + self.assertEqual(attid, drs_attid.attid, + "(DRS) Wrong attid " + "for expected value %d, wanted 0x%08x got 0x%08x" + % (i, attid, drs_attid.attid)) + + self.assertEqual(o.originating_invocation_id, + misc.GUID(orig_dsa), + "(DRS) Wrong originating_invocation_id " + "for expected value %d, attid 0x%08x, wanted %s got %s" + % (i, attid, + misc.GUID(orig_dsa), + o.originating_invocation_id)) + # Allow version to be skipped when it does not matter + if version is not None: + self.assertEqual(o.version, version, + "(DRS) Wrong version for expected value %d, " + "attid 0x%08x, " + "wanted %d got %d" + % (i, attid, version, o.version)) + break + i = i + 1 + att_idx = att_idx + 1 + + # now also used to check the group + def _check_obj(self, sam_ldb, obj_orig, is_deleted, expected_metadata=None, drs=None): + # search the user by guid as it may be deleted + guid_str = self._GUID_string(obj_orig["objectGUID"][0]) + res = sam_ldb.search(base='' % guid_str, + controls=["show_deleted:1"], + attrs=["*", "parentGUID", + "replPropertyMetaData"]) + self.assertEqual(len(res), 1) + user_cur = res[0] + rdn_orig = str(obj_orig[user_cur.dn.get_rdn_name()][0]) + rdn_cur = str(user_cur[user_cur.dn.get_rdn_name()][0]) + name_orig = str(obj_orig["name"][0]) + name_cur = str(user_cur["name"][0]) + dn_orig = obj_orig["dn"] + dn_cur = user_cur["dn"] + # now check properties of the user + if is_deleted: + self.assertTrue("isDeleted" in user_cur) + self.assertEqual(rdn_cur.split('\n')[0], rdn_orig) + self.assertEqual(name_cur.split('\n')[0], name_orig) + self.assertEqual(dn_cur.get_rdn_value().split('\n')[0], + dn_orig.get_rdn_value()) + self.assertEqual(name_cur, rdn_cur) + else: + self.assertFalse("isDeleted" in user_cur) + self.assertEqual(rdn_cur, rdn_orig) + self.assertEqual(name_cur, name_orig) + self.assertEqual(dn_cur, dn_orig) + self.assertEqual(name_cur, rdn_cur) + parent_cur = user_cur["parentGUID"][0] + try: + parent_orig = obj_orig["parentGUID"][0] + self.assertEqual(parent_orig, parent_cur) + except KeyError: + pass + self.assertEqual(name_cur, user_cur.dn.get_rdn_value()) + + if expected_metadata is not None: + self._check_metadata(dn_cur, sam_ldb, drs, user_cur["replPropertyMetaData"], + expected_metadata) + + return user_cur + + def test_ReplicateMoveObject1(self): + """Verifies how a moved container with a user inside is replicated between two DCs. + This test should verify that: + - the OU is replicated properly + - the OU is renamed + - We verify that after replication, + that the user has the correct DN (under OU2) + - the OU is deleted + - the OU is modified on DC2 + - We verify that after replication, + that the user has the correct DN (deleted) and has not description + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + initial_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + moved_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC1 after rename - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + moved_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata_dc2) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + deleted_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True, expected_metadata=deleted_metadata) + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = new_dn + msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + modified_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_description, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=modified_metadata) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + deleted_modified_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 2), + (DRSUAPI_ATTID_description, self.dc2_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC2 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_modified_metadata_dc2) + self.assertFalse("description" in user_cur) + + # trigger replication from DC2 to DC1, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + deleted_modified_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_description, self.dc2_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_modified_metadata_dc1) + self.assertFalse("description" in user_cur) + + def test_ReplicateMoveObject2(self): + """Verifies how a moved container with a user inside is not + replicated between two DCs as no replication is triggered + This test should verify that: + - the OU is not replicated + - the user is not replicated + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + initial_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_moved_orig = ldb_res[0] + + moved_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC1 after rename - should be valid user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata) + + # check user info on DC2 - should not be there, we have not done replication + ldb_res = self.ldb_dc2.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 0) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + deleted_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + deleted_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC2 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc2) + + # trigger replication from DC2 to DC1, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + def test_ReplicateMoveObject3(self): + """Verifies how a moved container with a user inside is replicated between two DCs. + This test should verify that: + - the OU is created on DC1 + - the OU is renamed on DC1 + - We verify that after replication, + that the user has the correct DN (under OU2). + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + initial_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + moved_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC1 after rename - should be valid user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + deleted_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + deleted_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC2 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc2) + + def test_ReplicateMoveObject3b(self): + """Verifies how a moved container with a user inside is replicated between two DCs. + This test should verify that: + - the OU is created on DC1 + - the OU is renamed on DC1 + - We verify that after replication, + that the user has the correct DN (under OU2). + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + initial_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC2 (Which has never seen the object) to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + moved_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC1 after rename - should be valid user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + deleted_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + # check user info on DC1 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + deleted_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC2 - should be deleted user + self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc2) + + def test_ReplicateMoveObject4(self): + """Verifies how a moved container with a user inside is replicated between two DCs. + This test should verify that: + - the OU is replicated properly + - the user is modified on DC2 + - the OU is renamed on DC1 + - We verify that after replication DC1 -> DC2, + that the user has the correct DN (under OU2), and the description + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + initial_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + initial_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC2 - should still be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_orig, is_deleted=False, + expected_metadata=initial_metadata_dc2) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + moved_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC1 after rename - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=moved_metadata) + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = user_dn + msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + modified_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 1), + (DRSUAPI_ATTID_description, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 1), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_orig, + is_deleted=False, + expected_metadata=modified_metadata) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + modified_renamed_metadata = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 2), + (DRSUAPI_ATTID_description, self.dc2_guid, 1), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 2), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 1), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)] + + # check user info on DC2 - should still be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=modified_renamed_metadata) + + self.assertTrue("description" in user_cur) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + deleted_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should still be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=False, + expected_metadata=modified_renamed_metadata) + + self.assertTrue("description" in user_cur) + + deleted_metadata_dc1 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc1_guid, 2), + (DRSUAPI_ATTID_description, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC1 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc1) + + self.assertFalse("description" in user_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + deleted_metadata_dc2 = [ + (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1), + (DRSUAPI_ATTID_cn, self.dc2_guid, 3), + (DRSUAPI_ATTID_description, self.dc1_guid, 2), + (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1), + (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1), + (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1), + (DRSUAPI_ATTID_name, self.dc1_guid, 3), + (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None), + (DRSUAPI_ATTID_codePage, self.dc1_guid, 2), + (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2), + (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1), + (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1), + (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2), + (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2), + (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1), + (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2), + (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1), + (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2), + (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2), + (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1), + (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2), + (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)] + + # check user info on DC2 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2, + obj_orig=user_moved_orig, + is_deleted=True, + expected_metadata=deleted_metadata_dc2) + + self.assertFalse("description" in user_cur) + + def test_ReplicateMoveObject5(self): + """Verifies how a moved container with a user inside is replicated between two DCs. + This test should verify that: + - the OU is replicated properly + - the user is modified on DC2 + - the OU is renamed on DC1 + - We verify that after replication DC2 -> DC1, + that the user has the correct DN (under OU2), and the description + + """ + # work-out unique username to test with + username = self._make_username() + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = user_dn + msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should still be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=False) + self.assertTrue("description" in user_cur) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + self.assertTrue("description" in user_cur) + + # delete user on DC2 + self.ldb_dc2.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 for cleanup + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + # check user info on DC1 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True) + self.assertFalse("description" in user_cur) + + def test_ReplicateMoveObject6(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is renamed on DC1 + - We verify that after replication DC1 -> DC2, + that the OU1 has the correct DN (under OU2), and the description + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou1_dn.get_component_value(0)) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(ou_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=new_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + ou_moved_orig = ldb_res[0] + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=False) + self.assertTrue("description" in ou_cur) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + def test_ReplicateMoveObject7(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is renamed on DC1 to be under OU2 + - We verify that after replication DC2 -> DC1, + that the OU1 has the correct DN (under OU2), and the description + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou1_dn.get_component_value(0)) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(ou_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=new_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + ou_moved_orig = ldb_res[0] + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=False) + self.assertTrue("description" in ou_cur) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + def test_ReplicateMoveObject8(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is renamed on DC1 to OU1-renamed + - We verify that after replication DC1 -> DC2, + that the OU1 has the correct DN (OU1-renamed), and the description + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "OU=%s-renamed" % self.ou1_dn.get_component_value(0)) + new_dn.add_base(self.ou1_dn.parent()) + self.ldb_dc1.rename(ou_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=new_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + ou_moved_orig = ldb_res[0] + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=False) + self.assertTrue("description" in ou_cur) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + def test_ReplicateMoveObject9(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is renamed on DC1 to be under OU2 + - the OU1 is renamed on DC1 to OU1-renamed + - We verify that after replication DC1 -> DC2, + that the OU1 has the correct DN (OU1-renamed), and the description + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "OU=%s-renamed" % self.ou1_dn.get_component_value(0)) + new_dn.add_base(self.ou1_dn.parent()) + self.ldb_dc1.rename(ou_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=new_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + ou_moved_orig = ldb_res[0] + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=False) + self.assertTrue("description" in ou_cur) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check user info on DC2 - should be deleted user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + def test_ReplicateMoveObject10(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is deleted on DC1 + - We verify that after replication DC1 -> DC2, + that the OU1 is deleted, and the description has gone away + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be deleted OU + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + # check user info on DC2 - should be deleted OU + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + def test_ReplicateMoveObject11(self): + """Verifies how a moved container is replicated between two DCs. + This test should verify that: + - the OU1 is replicated properly + - the OU1 is modified on DC2 + - the OU1 is deleted on DC1 + - We verify that after replication DC2 -> DC1, + that the OU1 is deleted, and the description has gone away + + """ + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_BASE, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + ou_orig = ldb_res[0] + ou_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should still be valid user + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False) + + # Modify description on DC2. This triggers a replication, but + # not of 'name' and so a bug in Samba regarding the DN. + msg = ldb.Message() + msg.dn = ou_dn + msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # delete OU on DC1 + self.ldb_dc1.delete('' % self._GUID_string(ou_orig["objectGUID"][0])) + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC2 - should be deleted OU + ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # check user info on DC2 - should be deleted OU + ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_orig, is_deleted=True) + self.assertFalse("description" in ou_cur) + + +class DrsMoveBetweenTreeOfObjectTestCase(drs_base.DrsBaseTestCase): + + def setUp(self): + super(DrsMoveBetweenTreeOfObjectTestCase, self).setUp() + # disable automatic replication temporary + self._disable_all_repl(self.dnsname_dc1) + self._disable_all_repl(self.dnsname_dc2) + + self.top_ou = samba.tests.create_test_ou(self.ldb_dc1, + "replica_move") + + # make sure DCs are synchronized before the test + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + self.ou1_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU1") + self.ou1_dn.add_base(self.top_ou) + self.ou1 = {} + self.ou1["dn"] = self.ou1_dn + self.ou1["objectclass"] = "organizationalUnit" + self.ou1["ou"] = self.ou1_dn.get_component_value(0) + + self.ou2_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2,OU=DrsOU1") + self.ou2_dn.add_base(self.top_ou) + self.ou2 = {} + self.ou2["dn"] = self.ou2_dn + self.ou2["objectclass"] = "organizationalUnit" + self.ou2["ou"] = self.ou2_dn.get_component_value(0) + + self.ou2b_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2B,OU=DrsOU1") + self.ou2b_dn.add_base(self.top_ou) + self.ou2b = {} + self.ou2b["dn"] = self.ou2b_dn + self.ou2b["objectclass"] = "organizationalUnit" + self.ou2b["ou"] = self.ou2b_dn.get_component_value(0) + + self.ou2c_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2C,OU=DrsOU1") + self.ou2c_dn.add_base(self.top_ou) + + self.ou3_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU3,OU=DrsOU2,OU=DrsOU1") + self.ou3_dn.add_base(self.top_ou) + self.ou3 = {} + self.ou3["dn"] = self.ou3_dn + self.ou3["objectclass"] = "organizationalUnit" + self.ou3["ou"] = self.ou3_dn.get_component_value(0) + + self.ou4_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1") + self.ou4_dn.add_base(self.top_ou) + self.ou4 = {} + self.ou4["dn"] = self.ou4_dn + self.ou4["objectclass"] = "organizationalUnit" + self.ou4["ou"] = self.ou4_dn.get_component_value(0) + + self.ou5_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU5,OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1") + self.ou5_dn.add_base(self.top_ou) + self.ou5 = {} + self.ou5["dn"] = self.ou5_dn + self.ou5["objectclass"] = "organizationalUnit" + self.ou5["ou"] = self.ou5_dn.get_component_value(0) + + self.ou6_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU6,OU=DrsOU5,OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1") + self.ou6_dn.add_base(self.top_ou) + self.ou6 = {} + self.ou6["dn"] = self.ou6_dn + self.ou6["objectclass"] = "organizationalUnit" + self.ou6["ou"] = self.ou6_dn.get_component_value(0) + + def tearDown(self): + self.ldb_dc1.delete(self.top_ou, ["tree_delete:1"]) + self._enable_all_repl(self.dnsname_dc1) + self._enable_all_repl(self.dnsname_dc2) + super(DrsMoveBetweenTreeOfObjectTestCase, self).tearDown() + + def _make_username(self): + return "DrsTreeU_" + time.strftime("%s", time.gmtime()) + + # now also used to check the group + def _check_obj(self, sam_ldb, obj_orig, is_deleted): + # search the user by guid as it may be deleted + guid_str = self._GUID_string(obj_orig["objectGUID"][0]) + res = sam_ldb.search(base='' % guid_str, + controls=["show_deleted:1"], + attrs=["*", "parentGUID"]) + self.assertEqual(len(res), 1) + user_cur = res[0] + cn_orig = str(obj_orig["cn"][0]) + cn_cur = str(user_cur["cn"][0]) + name_orig = str(obj_orig["name"][0]) + name_cur = str(user_cur["name"][0]) + dn_orig = obj_orig["dn"] + dn_cur = user_cur["dn"] + # now check properties of the user + if is_deleted: + self.assertTrue("isDeleted" in user_cur) + self.assertEqual(cn_cur.split('\n')[0], cn_orig) + self.assertEqual(name_cur.split('\n')[0], name_orig) + self.assertEqual(dn_cur.get_rdn_value().split('\n')[0], + dn_orig.get_rdn_value()) + self.assertEqual(name_cur, cn_cur) + else: + self.assertFalse("isDeleted" in user_cur) + self.assertEqual(cn_cur, cn_orig) + self.assertEqual(name_cur, name_orig) + self.assertEqual(dn_cur, dn_orig) + self.assertEqual(name_cur, cn_cur) + self.assertEqual(name_cur, user_cur.dn.get_rdn_value()) + + return user_cur + + def test_ReplicateMoveInTree1(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - a complex OU tree can be replicated correctly + - the user is in the correct spot (renamed into) within the tree + on both DCs + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + self.ldb_dc1.add(self.ou2) + self.ldb_dc1.add(self.ou3) + self.ldb_dc1.add(self.ou4) + self.ldb_dc1.add(self.ou5) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou5_dn) + self.ldb_dc1.rename(user_dn, new_dn) + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateMoveInTree2(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - a complex OU tree can be replicated correctly + - the user is in the correct spot (renamed into) within the tree + on both DCs + - that a rename back works correctly, and is replicated + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + self.ldb_dc1.add(self.ou2) + self.ldb_dc1.add(self.ou2b) + self.ldb_dc1.add(self.ou3) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou3_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + new_dn3 = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou3_dn.get_component_value(0)) + new_dn3.add_base(self.ou2b_dn) + self.ldb_dc1.rename(self.ou3_dn, new_dn3) + + ldb_res = self.ldb_dc1.search(base=new_dn3, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + user_moved_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + # Rename on DC1 + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou1_dn) + self.ldb_dc1.rename(user_moved_dn, new_dn) + + # Modify description on DC2 + msg = ldb.Message() + msg.dn = user_moved_dn + msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + user_moved_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + self.assertTrue("description" in user_cur) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # check user info on DC1 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True) + self.assertFalse("description" in user_cur) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be deleted user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=True) + self.assertFalse("description" in user_cur) + + def test_ReplicateMoveInTree3(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - a complex OU tree can be replicated correctly + - the user is in the correct spot (renamed into) within the tree + on both DCs + - that a rename back works correctly, and is replicated + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + self.ldb_dc1.add(self.ou2) + self.ldb_dc1.add(self.ou2b) + self.ldb_dc1.add(self.ou3) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou3_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + new_dn3 = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou3_dn.get_component_value(0)) + new_dn3.add_base(self.ou2b_dn) + self.ldb_dc1.rename(self.ou3_dn, new_dn3) + + ldb_res = self.ldb_dc1.search(base=new_dn3, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + user_moved_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_moved_dn, new_dn) + + self.ldb_dc1.rename(self.ou2_dn, self.ou2c_dn) + self.ldb_dc1.rename(self.ou2b_dn, self.ou2_dn) + self.ldb_dc1.rename(self.ou2c_dn, self.ou2b_dn) + + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + user_moved_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + self.assertEqual(user_cur["parentGUID"], user_moved_orig["parentGUID"]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateMoveInTree3b(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - a complex OU tree can be replicated correctly + - the user is in the correct spot (renamed into) within the tree + on both DCs + - that a rename back works correctly, and is replicated + - that a complex rename suffle, combined with unrelated changes to the object, + is replicated correctly. The aim here is the send the objects out-of-order + when sorted by usnChanged. + - confirm that the OU tree and (in particular the user DN) is identical between + the DCs once this has been replicated. + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + self.ldb_dc1.add(self.ou2) + self.ldb_dc1.add(self.ou2b) + self.ldb_dc1.add(self.ou3) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + msg = ldb.Message() + msg.dn = new_dn + msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + # The sleep(1) calls here ensure that the name objects get a + # new 1-sec based timestamp, and so we select how the conflict + # resolution resolves. + self.ldb_dc1.rename(self.ou2_dn, self.ou2c_dn) + time.sleep(1) + self.ldb_dc1.rename(self.ou2b_dn, self.ou2_dn) + time.sleep(1) + self.ldb_dc1.rename(self.ou2c_dn, self.ou2b_dn) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename('' % self._GUID_string(user_orig["objectGUID"][0]), new_dn) + + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + msg = ldb.Message() + msg.dn = self.ou2b_dn + msg["description"] = ldb.MessageElement("OU2b Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + self.assertEqual(user_cur["parentGUID"][0], user_moved_orig["parentGUID"][0]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateMoveInTree4(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - an OU and user can be replicated correctly, even after a rename + - The creation and rename of the OU has been combined with unrelated changes to the object, + The aim here is the send the objects out-of-order when sorted by usnChanged. + - That is, the OU will be sorted by usnChanged after the user that is within that OU. + - That will cause the client to need to get the OU first, by use of the GET_ANC flag + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # check user info on DC1 + print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0]))) + self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False) + + self.ldb_dc1.add(self.ou2) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + ldb_res = self.ldb_dc1.search(base=self.ou2_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username) + self.assertEqual(len(ldb_res), 1) + + user_moved_orig = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateAddInOU(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - an OU and user can be replicated correctly + - The creation of the OU has been combined with unrelated changes to the object, + The aim here is the send the objects out-of-order when sorted by usnChanged. + - That is, the OU will be sorted by usnChanged after the user that is within that OU. + - That will cause the client to need to get the OU first, by use of the GET_ANC flag + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + + msg = ldb.Message() + msg.dn = self.ou1_dn + msg["description"] = ldb.MessageElement("OU1 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False) + + self.assertEqual(user_cur["parentGUID"], user_orig["parentGUID"]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateAddInMovedOU(self): + """Verifies how an object is replicated between two DCs. + This test should verify that: + - an OU and user can be replicated correctly + - The creation of the OU has been combined with unrelated changes to the object, + The aim here is the send the objects out-of-order when sorted by usnChanged. + - That is, the OU will be sorted by usnChanged after the user that is within that OU. + - That will cause the client to need to get the OU first, by use of the GET_ANC flag + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + self.ldb_dc1.add(self.ou2) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + self.ldb_dc1.rename(self.ou2_dn, self.ou2b_dn) + + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_moved = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be valid user + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False) + + self.assertEqual(user_cur["parentGUID"], user_moved["parentGUID"]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateAddInConflictOU_time(self): + """Verifies how an object is replicated between two DCs, when created in an ambiguous location + This test should verify that: + - Without replication, two conflicting objects can be created + - force the conflict resolution algorithm so we know which copy will win + (by sleeping while creating the objects, therefore increasing that timestamp on 'name') + - confirm that the user object, created on DC1, ends up in the right place on DC2 + - therefore confirm that the conflict algorithm worked correctly, and that parentGUID was used. + + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # Now create two, conflicting objects. This gives the user + # object something to be under on both DCs. + + # We sleep between the two adds so that DC1 adds second, and + # so wins the conflict resolution due to a later creation time + # (modification timestamp on the name attribute). + self.ldb_dc2.add(self.ou2) + time.sleep(1) + self.ldb_dc1.add(self.ou2) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + # Now that we have renamed the user (and so bumped the + # usnChanged), bump the value on the OUs. + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_moved = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be under the OU2 from DC1 + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False) + + self.assertEqual(user_cur["parentGUID"], user_moved["parentGUID"]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + def test_ReplicateAddInConflictOU2(self): + """Verifies how an object is replicated between two DCs, when created in an ambiguous location + This test should verify that: + - Without replication, two conflicting objects can be created + - force the conflict resolution algorithm so we know which copy will win + (by changing the description twice, therefore increasing that version count) + - confirm that the user object, created on DC1, ends up in the right place on DC2 + - therefore confirm that the conflict algorithm worked correctly, and that parentGUID was used. + """ + # work-out unique username to test with + username = self._make_username() + + self.ldb_dc1.add(self.ou1) + + # create user on DC1 + self.ldb_dc1.newuser(username=username, + userou="ou=%s,ou=%s" + % (self.ou1_dn.get_component_value(0), + self.top_ou.get_component_value(0)), + password=None, setpassword=False) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_orig = ldb_res[0] + user_dn = ldb_res[0]["dn"] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # Now create two, conflicting objects. This gives the user + # object something to be under on both DCs. We create it on + # DC1 1sec later so that it will win the conflict resolution. + + self.ldb_dc2.add(self.ou2) + time.sleep(1) + self.ldb_dc1.add(self.ou2) + + new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username) + new_dn.add_base(self.ou2_dn) + self.ldb_dc1.rename(user_dn, new_dn) + + # Now that we have renamed the user (and so bumped the + # usnChanged), bump the value on the OUs. + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc1.modify(msg) + + msg = ldb.Message() + msg.dn = self.ou2_dn + msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description") + self.ldb_dc2.modify(msg) + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + ldb_res = self.ldb_dc1.search(base=self.ou1_dn, + scope=SCOPE_SUBTREE, + expression="(samAccountName=%s)" % username, + attrs=["*", "parentGUID"]) + self.assertEqual(len(ldb_res), 1) + user_moved = ldb_res[0] + + # trigger replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + # check user info on DC2 - should be under the OU2 from DC1 + user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False) + + self.assertEqual(user_cur["parentGUID"], user_moved["parentGUID"]) + + # delete user on DC1 + self.ldb_dc1.delete('' % self._GUID_string(user_orig["objectGUID"][0])) + + # trigger replication from DC1 to DC2, for cleanup + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) diff --git a/source4/torture/drs/python/repl_rodc.py b/source4/torture/drs/python/repl_rodc.py new file mode 100644 index 0000000..ab3d6fa --- /dev/null +++ b/source4/torture/drs/python/repl_rodc.py @@ -0,0 +1,735 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Test replication scenarios involving an RODC +# +# Copyright (C) Catalyst.Net Ltd. 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc1_dns_name [this is unused for the test, but it'll still try to connect] +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN repl_rodc -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +import ldb + +from samba import WERRORError +from samba.join import DCJoinContext +from samba.dcerpc import drsuapi, misc, drsblobs, security +from samba.ndr import ndr_unpack, ndr_pack +from samba.samdb import dsdb_Dn +from samba.credentials import Credentials + +import random +import time + + +def drs_get_rodc_partial_attribute_set(samdb, samdb1, exceptions=None): + '''get a list of attributes for RODC replication''' + if exceptions is None: + exceptions = [] + + partial_attribute_set = drsuapi.DsPartialAttributeSet() + partial_attribute_set.version = 1 + + attids = [] + + # the exact list of attids we send is quite critical. Note that + # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING + # to zero them out + schema_dn = samdb.get_schema_basedn() + res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE, + expression="objectClass=attributeSchema", + attrs=["lDAPDisplayName", "systemFlags", + "searchFlags"]) + + for r in res: + ldap_display_name = str(r["lDAPDisplayName"][0]) + if "systemFlags" in r: + system_flags = str(r["systemFlags"][0]) + if (int(system_flags) & (samba.dsdb.DS_FLAG_ATTR_NOT_REPLICATED | + samba.dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED)): + continue + if "searchFlags" in r: + search_flags = str(r["searchFlags"][0]) + if (int(search_flags) & samba.dsdb.SEARCH_FLAG_RODC_ATTRIBUTE): + continue + try: + attid = samdb1.get_attid_from_lDAPDisplayName(ldap_display_name) + if attid not in exceptions: + attids.append(int(attid)) + except: + pass + + # the attids do need to be sorted, or windows doesn't return + # all the attributes we need + attids.sort() + partial_attribute_set.attids = attids + partial_attribute_set.num_attids = len(attids) + return partial_attribute_set + + +class DrsRodcTestCase(drs_base.DrsBaseTestCase): + """Intended as a semi-black box test case for replication involving + an RODC.""" + + def setUp(self): + super(DrsRodcTestCase, self).setUp() + self.base_dn = self.ldb_dc1.get_default_basedn() + + self.ou = samba.tests.create_test_ou(self.ldb_dc1, "test_drs_rodc") + self.allowed_group = "CN=Allowed RODC Password Replication Group,CN=Users,%s" % self.base_dn + + self.site = self.ldb_dc1.server_site_name() + self.rodc_name = "TESTRODCDRS%s" % random.randint(1, 10000000) + self.rodc_pass = "password12#" + self.computer_dn = "CN=%s,OU=Domain Controllers,%s" % (self.rodc_name, self.base_dn) + + self.rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(), + creds=self.get_credentials(), + lp=self.get_loadparm(), site=self.site, + netbios_name=self.rodc_name, + targetdir=None, domain=None, + machinepass=self.rodc_pass) + self._create_rodc(self.rodc_ctx) + self.rodc_ctx.create_tmp_samdb() + self.tmp_samdb = self.rodc_ctx.tmp_samdb + + rodc_creds = Credentials() + rodc_creds.guess(self.rodc_ctx.lp) + rodc_creds.set_username(self.rodc_name + '$') + rodc_creds.set_password(self.rodc_pass) + self.rodc_creds = rodc_creds + + (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) + (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, rodc_creds) + + def tearDown(self): + self.rodc_ctx.cleanup_old_join() + super(DrsRodcTestCase, self).tearDown() + + def test_admin_repl_secrets(self): + """ + When a secret attribute is set to be replicated to an RODC with the + admin credentials, it should always replicate regardless of whether + or not it's in the Allowed RODC Password Replication Group. + """ + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + user_name = "test_rodcA_%s" % rand + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # Check that the user has been added to msDSRevealedUsers + self._assert_in_revealed_users(user_dn, expected_user_attributes) + + def test_admin_repl_secrets_DummyDN_GUID(self): + """ + When a secret attribute is set to be replicated to an RODC with the + admin credentials, it should always replicate regardless of whether + or not it's in the Allowed RODC Password Replication Group. + """ + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + user_name = "test_rodcA_%s" % rand + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + res = self.ldb_dc1.search(base=user_dn, scope=ldb.SCOPE_BASE, + attrs=["objectGUID"]) + + user_guid = misc.GUID(res[0]["objectGUID"][0]) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str="DummyDN", + nc_guid=user_guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + try: + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + except WERRORError as e1: + (enum, estr) = e1.args + self.fail(f"DsGetNCChanges failed with {estr}") + + # Check that the user has been added to msDSRevealedUsers + self._assert_in_revealed_users(user_dn, expected_user_attributes) + + def test_rodc_repl_secrets(self): + """ + When a secret attribute is set to be replicated to an RODC with + the RODC account credentials, it should not replicate if it's in + the Allowed RODC Password Replication Group. Once it is added to + the group, it should replicate. + """ + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + user_name = "test_rodcB_%s" % rand + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e: + (enum, estr) = e.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + # send the same request again and we should get the same response + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e1: + (enum, estr) = e1.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + # Retry with Administrator credentials, ignores password replication groups + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # Check that the user has been added to msDSRevealedUsers + self._assert_in_revealed_users(user_dn, expected_user_attributes) + + def test_rodc_repl_secrets_follow_on_req(self): + """ + Checks that an RODC can't subvert an existing (valid) GetNCChanges + request to reveal secrets it shouldn't have access to. + """ + + # send an acceptable request that will match as many GUIDs as possible. + # Here we set the SPECIAL_SECRET_PROCESSING flag so that the request gets accepted. + # (On the server, this builds up the getnc_state->guids array) + req8 = self._exop_req8(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=self.ldb_dc1.domain_dn(), + exop=drsuapi.DRSUAPI_EXOP_NONE, + max_objects=1, + replica_flags=drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 8, req8) + + # Get the next replication chunk, but set REPL_SECRET this time. This + # is following on the the previous accepted request, but we've changed + # exop to now request secrets. This request should fail + try: + req8 = self._exop_req8(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=self.ldb_dc1.domain_dn(), + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET) + req8.highwatermark = ctr.new_highwatermark + + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 8, req8) + + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except RuntimeError as e2: + (enum, estr) = e2.args + pass + + def test_msDSRevealedUsers_admin(self): + """ + When a secret attribute is to be replicated to an RODC, the contents + of the attribute should be added to the msDSRevealedUsers attribute + of the computer object corresponding to the RODC. + """ + + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + # Add a user on DC1, add it to allowed password replication + # group, and replicate to RODC with EXOP_REPL_SECRETS + user_name = "test_rodcC_%s" % rand + password = "password12#" + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) + + self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", + [user_name], + add_members_operation=True) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # Check that the user has been added to msDSRevealedUsers + (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + + # Change the user's password on DC1 + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password + "1", False, user_name) + + (packed_attrs_2, unpacked_attrs_2) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + self._assert_attrlist_equals(unpacked_attrs_1, unpacked_attrs_2) + + # Replicate to RODC again with EXOP_REPL_SECRETS + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # This is important for Windows, because the entry won't have been + # updated in time if we don't have it. Even with this sleep, it only + # passes some of the time... + time.sleep(5) + + # Check that the entry in msDSRevealedUsers has been updated + (packed_attrs_3, unpacked_attrs_3) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + self._assert_attrlist_changed(unpacked_attrs_2, unpacked_attrs_3, expected_user_attributes) + + # We should be able to delete the user + self.ldb_dc1.deleteuser(user_name) + + res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, + attrs=["msDS-RevealedUsers"]) + self.assertFalse("msDS-RevealedUsers" in res[0]) + + def test_msDSRevealedUsers(self): + """ + When a secret attribute is to be replicated to an RODC, the contents + of the attribute should be added to the msDSRevealedUsers attribute + of the computer object corresponding to the RODC. + """ + + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + # Add a user on DC1, add it to allowed password replication + # group, and replicate to RODC with EXOP_REPL_SECRETS + user_name = "test_rodcD_%s" % rand + password = "password12#" + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) + + self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", + [user_name], + add_members_operation=True) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # Check that the user has been added to msDSRevealedUsers + (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + + # Change the user's password on DC1 + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password + "1", False, user_name) + + (packed_attrs_2, unpacked_attrs_2) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + self._assert_attrlist_equals(unpacked_attrs_1, unpacked_attrs_2) + + # Replicate to RODC again with EXOP_REPL_SECRETS + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + + # This is important for Windows, because the entry won't have been + # updated in time if we don't have it. Even with this sleep, it only + # passes some of the time... + time.sleep(5) + + # Check that the entry in msDSRevealedUsers has been updated + (packed_attrs_3, unpacked_attrs_3) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + self._assert_attrlist_changed(unpacked_attrs_2, unpacked_attrs_3, expected_user_attributes) + + # We should be able to delete the user + self.ldb_dc1.deleteuser(user_name) + + res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, + attrs=["msDS-RevealedUsers"]) + self.assertFalse("msDS-RevealedUsers" in res[0]) + + def test_msDSRevealedUsers_pas(self): + """ + If we provide a Partial Attribute Set when replicating to an RODC, + we should ignore it and replicate all of the secret attributes anyway + msDSRevealedUsers attribute. + """ + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + pas_exceptions = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + # Add a user on DC1, add it to allowed password replication + # group, and replicate to RODC with EXOP_REPL_SECRETS + user_name = "test_rodcE_%s" % rand + password = "password12#" + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) + + self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", + [user_name], + add_members_operation=True) + + pas = drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb, exceptions=pas_exceptions) + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=pas, + max_objects=133, + replica_flags=0) + (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) + + # Make sure that we still replicate the secrets + for attribute in ctr.first_object.object.attribute_ctr.attributes: + if attribute.attid in pas_exceptions: + pas_exceptions.remove(attribute.attid) + for attribute in pas_exceptions: + self.fail("%d was not replicated even though the partial attribute set should be ignored." + % attribute) + + # Check that the user has been added to msDSRevealedUsers + (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) + + def test_msDSRevealedUsers_using_other_RODC(self): + """ + Ensure that the machine account is tied to the destination DSA. + """ + # Create a new identical RODC with just the first letter missing + other_rodc_name = self.rodc_name[1:] + other_rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(), + creds=self.get_credentials(), + lp=self.get_loadparm(), site=self.site, + netbios_name=other_rodc_name, + targetdir=None, domain=None, + machinepass=self.rodc_pass) + self._create_rodc(other_rodc_ctx) + + other_rodc_creds = Credentials() + other_rodc_creds.guess(other_rodc_ctx.lp) + other_rodc_creds.set_username(other_rodc_name + '$') + other_rodc_creds.set_password(self.rodc_pass) + + (other_rodc_drs, other_rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, other_rodc_creds) + + rand = random.randint(1, 10000000) + + user_name = "test_rodcF_%s" % rand + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) + self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", + [user_name], + add_members_operation=True) + + req10 = self._getnc_req10(dest_dsa=str(other_rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e3: + (enum, estr) = e3.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + + try: + (level, ctr) = other_rodc_drs.DsGetNCChanges(other_rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e4: + (enum, estr) = e4.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + def test_msDSRevealedUsers_local_deny_allow(self): + """ + Ensure that the deny trumps allow, and we can modify these + attributes directly instead of the global groups. + + This may fail on Windows due to tokenGroup calculation caching. + """ + rand = random.randint(1, 10000000) + expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, + drsuapi.DRSUAPI_ATTID_supplementalCredentials, + drsuapi.DRSUAPI_ATTID_ntPwdHistory, + drsuapi.DRSUAPI_ATTID_unicodePwd, + drsuapi.DRSUAPI_ATTID_dBCSPwd] + + # Add a user on DC1, add it to allowed password replication + # group, and replicate to RODC with EXOP_REPL_SECRETS + user_name = "test_rodcF_%s" % rand + password = "password12#" + user_dn = "CN=%s,%s" % (user_name, self.ou) + self.ldb_dc1.add({ + "dn": user_dn, + "objectclass": "user", + "sAMAccountName": user_name + }) + + # Store some secret on this user + self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) + + req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=user_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), + max_objects=133, + replica_flags=0) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) + + m["msDS-RevealOnDemandGroup"] = \ + ldb.MessageElement(user_dn, ldb.FLAG_MOD_ADD, + "msDS-RevealOnDemandGroup") + self.ldb_dc1.modify(m) + + # In local allow, should be success + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + except: + self.fail("Should have succeeded when in local allow group") + + self._assert_in_revealed_users(user_dn, expected_user_attributes) + + (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, self.rodc_creds) + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) + + m["msDS-NeverRevealGroup"] = \ + ldb.MessageElement(user_dn, ldb.FLAG_MOD_ADD, + "msDS-NeverRevealGroup") + self.ldb_dc1.modify(m) + + # In local allow and deny, should be failure + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e5: + (enum, estr) = e5.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) + + m["msDS-RevealOnDemandGroup"] = \ + ldb.MessageElement(user_dn, ldb.FLAG_MOD_DELETE, + "msDS-RevealOnDemandGroup") + self.ldb_dc1.modify(m) + + # In local deny, should be failure + (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, self.rodc_creds) + try: + (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) + self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") + except WERRORError as e6: + (enum, estr) = e6.args + self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED + + def _assert_in_revealed_users(self, user_dn, attrlist): + res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, + attrs=["msDS-RevealedUsers"]) + revealed_users = res[0]["msDS-RevealedUsers"] + actual_attrids = [] + packed_attrs = [] + unpacked_attrs = [] + for attribute in revealed_users: + attribute = attribute.decode('utf8') + dsdb_dn = dsdb_Dn(self.ldb_dc1, attribute) + metadata = ndr_unpack(drsblobs.replPropertyMetaData1, dsdb_dn.get_bytes()) + if user_dn in attribute: + unpacked_attrs.append(metadata) + packed_attrs.append(dsdb_dn.get_bytes()) + actual_attrids.append(metadata.attid) + + self.assertEqual(sorted(actual_attrids), sorted(attrlist)) + + return (packed_attrs, unpacked_attrs) + + def _assert_attrlist_equals(self, list_1, list_2): + return self._assert_attrlist_changed(list_1, list_2, [], num_changes=0, expected_new_usn=False) + + def _assert_attrlist_changed(self, list_1, list_2, changed_attributes, num_changes=1, expected_new_usn=True): + for i in range(len(list_2)): + self.assertEqual(list_1[i].attid, list_2[i].attid) + self.assertEqual(list_1[i].originating_invocation_id, list_2[i].originating_invocation_id) + self.assertEqual(list_1[i].version + num_changes, list_2[i].version) + + if expected_new_usn: + self.assertTrue(list_1[i].originating_usn < list_2[i].originating_usn) + self.assertTrue(list_1[i].local_usn < list_2[i].local_usn) + else: + self.assertEqual(list_1[i].originating_usn, list_2[i].originating_usn) + self.assertEqual(list_1[i].local_usn, list_2[i].local_usn) + + if list_1[i].attid in changed_attributes: + # We do the changes too quickly, so unless we put sleeps + # in between calls, these remain the same. Checking the USNs + # is enough. + pass + #self.assertTrue(list_1[i].originating_change_time < list_2[i].originating_change_time) + else: + self.assertEqual(list_1[i].originating_change_time, list_2[i].originating_change_time) + + def _create_rodc(self, ctx): + ctx.nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] + ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] + ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn) + + ctx.never_reveal_sid = ["" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY), + "" % security.SID_BUILTIN_ADMINISTRATORS, + "" % security.SID_BUILTIN_SERVER_OPERATORS, + "" % security.SID_BUILTIN_BACKUP_OPERATORS, + "" % security.SID_BUILTIN_ACCOUNT_OPERATORS] + ctx.reveal_sid = "" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW) + + mysid = ctx.get_mysid() + admin_dn = "" % mysid + ctx.managedby = admin_dn + + ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT | + samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | + samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT) + + ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn + ctx.secure_channel_type = misc.SEC_CHAN_RODC + ctx.RODC = True + ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | + drsuapi.DRSUAPI_DRS_PER_SYNC | + drsuapi.DRSUAPI_DRS_GET_ANC | + drsuapi.DRSUAPI_DRS_NEVER_SYNCED | + drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) + + ctx.join_add_objects() diff --git a/source4/torture/drs/python/repl_schema.py b/source4/torture/drs/python/repl_schema.py new file mode 100644 index 0000000..9c039a5 --- /dev/null +++ b/source4/torture/drs/python/repl_schema.py @@ -0,0 +1,444 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests various schema replication scenarios +# +# Copyright (C) Kamen Mazdrashki 2010 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN repl_schema -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import time +import random +import ldb +import drs_base + +from ldb import ( + ERR_NO_SUCH_OBJECT, + LdbError, + SCOPE_BASE, + Message, + FLAG_MOD_ADD, + FLAG_MOD_REPLACE +) +from samba.dcerpc import drsuapi, misc +from samba.drs_utils import drs_DsBind +from samba import dsdb + + +class DrsReplSchemaTestCase(drs_base.DrsBaseTestCase): + + # prefix for all objects created + obj_prefix = None + # current Class or Attribute object id + obj_id = 0 + + def _exop_req8(self, dest_dsa, invocation_id, nc_dn_str, exop, + replica_flags=0, max_objects=0): + req8 = drsuapi.DsGetNCChangesRequest8() + + req8.destination_dsa_guid = misc.GUID(dest_dsa) if dest_dsa else misc.GUID() + req8.source_dsa_invocation_id = misc.GUID(invocation_id) + req8.naming_context = drsuapi.DsReplicaObjectIdentifier() + req8.naming_context.dn = str(nc_dn_str) + req8.highwatermark = drsuapi.DsReplicaHighWaterMark() + req8.highwatermark.tmp_highest_usn = 0 + req8.highwatermark.reserved_usn = 0 + req8.highwatermark.highest_usn = 0 + req8.uptodateness_vector = None + req8.replica_flags = replica_flags + req8.max_object_count = max_objects + req8.max_ndr_size = 402116 + req8.extended_op = exop + req8.fsmo_info = 0 + req8.partial_attribute_set = None + req8.partial_attribute_set_ex = None + req8.mapping_ctr.num_mappings = 0 + req8.mapping_ctr.mappings = None + + return req8 + + def setUp(self): + super(DrsReplSchemaTestCase, self).setUp() + + # disable automatic replication temporary + self._disable_all_repl(self.dnsname_dc1) + self._disable_all_repl(self.dnsname_dc2) + + # make sure DCs are synchronized before the test + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + # initialize objects prefix if not done yet + if self.obj_prefix is None: + t = time.strftime("%s", time.gmtime()) + DrsReplSchemaTestCase.obj_prefix = "DrsReplSchema-%s" % t + + def tearDown(self): + self._enable_all_repl(self.dnsname_dc1) + self._enable_all_repl(self.dnsname_dc2) + super(DrsReplSchemaTestCase, self).tearDown() + + def _make_obj_names(self, base_name): + '''Try to create a unique name for an object + that is to be added to schema''' + self.obj_id += 1 + obj_name = "%s-%d-%s" % (self.obj_prefix, self.obj_id, base_name) + obj_ldn = obj_name.replace("-", "") + obj_dn = ldb.Dn(self.ldb_dc1, "CN=X") + obj_dn.add_base(ldb.Dn(self.ldb_dc1, self.schema_dn)) + obj_dn.set_component(0, "CN", obj_name) + return (obj_dn, obj_name, obj_ldn) + + def _schema_new_class(self, ldb_ctx, base_name, base_int, oc_cat=1, attrs=None): + (class_dn, class_name, class_ldn) = self._make_obj_names(base_name) + rec = {"dn": class_dn, + "objectClass": ["top", "classSchema"], + "cn": class_name, + "lDAPDisplayName": class_ldn, + "governsId": "1.3.6.1.4.1.7165.4.6.2.5." + + str((100000 * base_int) + random.randint(1, 100000)) + ".1.5.13", + "instanceType": "4", + "objectClassCategory": "%d" % oc_cat, + "subClassOf": "top", + "systemOnly": "FALSE"} + # allow overriding/adding attributes + if attrs is not None: + rec.update(attrs) + # add it to the Schema + try: + ldb_ctx.add(rec) + except LdbError as e: + (enum, estr) = e.args + self.fail("Adding record failed with %d/%s" % (enum, estr)) + + self._ldap_schemaUpdateNow(ldb_ctx) + return (rec["lDAPDisplayName"], rec["dn"]) + + def _schema_new_attr(self, ldb_ctx, base_name, base_int, attrs=None): + (attr_dn, attr_name, attr_ldn) = self._make_obj_names(base_name) + rec = {"dn": attr_dn, + "objectClass": ["top", "attributeSchema"], + "cn": attr_name, + "lDAPDisplayName": attr_ldn, + "attributeId": "1.3.6.1.4.1.7165.4.6.1.5." + + str((100000 * base_int) + random.randint(1, 100000)) + ".1.5.13", + "attributeSyntax": "2.5.5.12", + "omSyntax": "64", + "instanceType": "4", + "isSingleValued": "TRUE", + "systemOnly": "FALSE"} + # allow overriding/adding attributes + if attrs is not None: + rec.update(attrs) + # add it to the Schema + ldb_ctx.add(rec) + self._ldap_schemaUpdateNow(ldb_ctx) + return (rec["lDAPDisplayName"], rec["dn"]) + + def _check_object(self, obj_dn): + '''Check if object obj_dn exists on both DCs''' + res_dc1 = self.ldb_dc1.search(base=obj_dn, + scope=SCOPE_BASE, + attrs=["*"]) + self.assertEqual(len(res_dc1), 1, + "%s doesn't exists on %s" % (obj_dn, self.dnsname_dc1)) + try: + res_dc2 = self.ldb_dc2.search(base=obj_dn, + scope=SCOPE_BASE, + attrs=["*"]) + except LdbError as e1: + (enum, estr) = e1.args + if enum == ERR_NO_SUCH_OBJECT: + self.fail("%s doesn't exists on %s" % (obj_dn, self.dnsname_dc2)) + raise + self.assertEqual(len(res_dc2), 1, + "%s doesn't exists on %s" % (obj_dn, self.dnsname_dc2)) + + def test_class(self): + """Simple test for classSchema replication""" + # add new classSchema object + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-S", 0) + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + # check object is replicated + self._check_object(c_dn) + + def test_classInheritance(self): + """Test inheritance through subClassOf + I think 5 levels of inheritance is pretty decent for now.""" + # add 5 levels deep hierarchy + c_dn_list = [] + c_ldn_last = None + for i in range(1, 6): + base_name = "cls-I-%02d" % i + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, base_name, i) + c_dn_list.append(c_dn) + if c_ldn_last: + # inherit from last class added + m = Message.from_dict(self.ldb_dc1, + {"dn": c_dn, + "subClassOf": c_ldn_last}, + FLAG_MOD_REPLACE) + self.ldb_dc1.modify(m) + # store last class ldapDisplayName + c_ldn_last = c_ldn + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + # check objects are replicated + for c_dn in c_dn_list: + self._check_object(c_dn) + + def test_classWithCustomAttribute(self): + """Create new Attribute and a Class, + that has value for newly created attribute. + This should check code path that searches for + AttributeID_id in Schema cache""" + # add new attributeSchema object + (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-A", 7) + # add a base classSchema class so we can use our new + # attribute in class definition in a sibling class + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-A", 8, + 1, + {"systemMayContain": a_ldn, + "subClassOf": "classSchema"}) + # add new classSchema object with value for a_ldb attribute + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-B", 9, + 1, + {"objectClass": ["top", "classSchema", c_ldn], + a_ldn: "test_classWithCustomAttribute"}) + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + # check objects are replicated + self._check_object(c_dn) + self._check_object(a_dn) + + def test_classWithCustomLinkAttribute(self): + """Create new Attribute and a Class, + that has value for newly created attribute. + This should check code path that searches for + AttributeID_id in Schema cache""" + # add new attributeSchema object + (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-Link-X", 10, + attrs={'linkID': "1.2.840.113556.1.2.50", + "attributeSyntax": "2.5.5.1", + "omSyntax": "127"}) + # add a base classSchema class so we can use our new + # attribute in class definition in a sibling class + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-Link-Y", 11, + 1, + {"systemMayContain": a_ldn, + "subClassOf": "classSchema"}) + # add new classSchema object with value for a_ldb attribute + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-Link-Z", 12, + 1, + {"objectClass": ["top", "classSchema", c_ldn], + a_ldn: self.schema_dn}) + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + # check objects are replicated + self._check_object(c_dn) + self._check_object(a_dn) + + res = self.ldb_dc1.search(base="", + scope=SCOPE_BASE, + attrs=["domainFunctionality"]) + + if int(res[0]["domainFunctionality"][0]) > dsdb.DS_DOMAIN_FUNCTION_2000: + res = self.ldb_dc1.search(base=a_dn, + scope=SCOPE_BASE, + attrs=["msDS-IntId"]) + self.assertEqual(1, len(res)) + self.assertTrue("msDS-IntId" in res[0]) + int_id = int(res[0]["msDS-IntId"][0]) + if int_id < 0: + int_id += (1 << 32) + + dc_guid_1 = self.ldb_dc1.get_invocation_id() + + drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1) + + req8 = self._exop_req8(dest_dsa=None, + invocation_id=dc_guid_1, + nc_dn_str=c_dn, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_SYNC_FORCED) + + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + + for link in ctr.linked_attributes: + self.assertTrue(link.attid != int_id, + 'Got %d for both' % link.attid) + + def test_attribute(self): + """Simple test for attributeSchema replication""" + # add new attributeSchema object + (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-S", 13) + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + # check object is replicated + self._check_object(a_dn) + + def test_attribute_on_ou(self): + """Simple test having an OU with a custom attribute replicated correctly + + This ensures that the server + """ + + # add new attributeSchema object + (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-OU-S", 14) + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-OU-A", 15, + 3, + {"mayContain": a_ldn}) + ou_dn = ldb.Dn(self.ldb_dc1, "ou=X") + ou_dn.add_base(self.ldb_dc1.get_default_basedn()) + ou_dn.set_component(0, "OU", a_dn.get_component_value(0)) + rec = {"dn": ou_dn, + "objectClass": ["top", "organizationalUnit", c_ldn], + "ou": ou_dn.get_component_value(0), + a_ldn: "test OU"} + self.ldb_dc1.add(rec) + + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.domain_dn, forced=True) + # check objects are replicated + self._check_object(c_dn) + self._check_object(a_dn) + self._check_object(ou_dn) + self.ldb_dc1.delete(ou_dn) + + def test_all(self): + """Basic plan is to create bunch of classSchema + and attributeSchema objects, replicate Schema NC + and then check all objects are replicated correctly""" + + # add new classSchema object + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-A", 16) + # add new attributeSchema object + (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-A", 17) + + # add attribute to the class we have + m = Message.from_dict(self.ldb_dc1, + {"dn": c_dn, + "mayContain": a_ldn}, + FLAG_MOD_ADD) + self.ldb_dc1.modify(m) + + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True) + + # check objects are replicated + self._check_object(c_dn) + self._check_object(a_dn) + + def test_classWithCustomBinaryDNLinkAttribute(self): + # Add a new attribute to the schema, which has binary DN syntax (2.5.5.7) + (bin_ldn, bin_dn) = self._schema_new_attr(self.ldb_dc1, "attr-Link-Bin", 18, + attrs={"linkID": "1.2.840.113556.1.2.50", + "attributeSyntax": "2.5.5.7", + "omSyntax": "127"}) + + (bin_ldn_b, bin_dn_b) = self._schema_new_attr(self.ldb_dc1, "attr-Link-Bin-Back", 19, + attrs={"linkID": bin_ldn, + "attributeSyntax": "2.5.5.1", + "omSyntax": "127"}) + + # Add a new class to the schema which can have the binary DN attribute + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-Link-Bin", 20, + 3, + {"mayContain": bin_ldn}) + (c_ldn_b, c_dn_b) = self._schema_new_class(self.ldb_dc1, "cls-Link-Bin-Back", 21, + 3, + {"mayContain": bin_ldn_b}) + + link_end_dn = ldb.Dn(self.ldb_dc1, "ou=X") + link_end_dn.add_base(self.ldb_dc1.get_default_basedn()) + link_end_dn.set_component(0, "OU", bin_dn_b.get_component_value(0)) + + ou_dn = ldb.Dn(self.ldb_dc1, "ou=X") + ou_dn.add_base(self.ldb_dc1.get_default_basedn()) + ou_dn.set_component(0, "OU", bin_dn.get_component_value(0)) + + # Add an instance of the class to be pointed at + rec = {"dn": link_end_dn, + "objectClass": ["top", "organizationalUnit", c_ldn_b], + "ou": link_end_dn.get_component_value(0)} + self.ldb_dc1.add(rec) + + # .. and one that does, and points to the first one + rec = {"dn": ou_dn, + "objectClass": ["top", "organizationalUnit", c_ldn], + "ou": ou_dn.get_component_value(0)} + self.ldb_dc1.add(rec) + + m = Message.from_dict(self.ldb_dc1, + {"dn": ou_dn, + bin_ldn: "B:8:1234ABCD:%s" % str(link_end_dn)}, + FLAG_MOD_ADD) + self.ldb_dc1.modify(m) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.schema_dn, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.domain_dn, forced=True) + + self._check_object(c_dn) + self._check_object(bin_dn) + + # Make sure we can delete the backlink + self.ldb_dc1.delete(link_end_dn) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.schema_dn, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.domain_dn, forced=True) + + def test_rename(self): + """Basic plan is to create a classSchema + and attributeSchema objects, replicate Schema NC + and then check all objects are replicated correctly""" + + # add new classSchema object + (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-B", 20) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.schema_dn, forced=True) + + # check objects are replicated + self._check_object(c_dn) + + # rename the Class CN + c_dn_new = ldb.Dn(self.ldb_dc1, str(c_dn)) + c_dn_new.set_component(0, + "CN", + c_dn.get_component_value(0) + "-NEW") + try: + self.ldb_dc1.rename(c_dn, c_dn_new) + except LdbError as e2: + (num, _) = e2.args + self.fail("failed to change CN for %s: %s" % (c_dn, _)) + + # force replication from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn=self.schema_dn, forced=True) + + # check objects are replicated + self._check_object(c_dn_new) diff --git a/source4/torture/drs/python/repl_secdesc.py b/source4/torture/drs/python/repl_secdesc.py new file mode 100644 index 0000000..38ae25a --- /dev/null +++ b/source4/torture/drs/python/repl_secdesc.py @@ -0,0 +1,400 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Catalyst.Net Ltd. 2017 +# Copyright (C) Andrew Bartlett 2019 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +import drs_base +import ldb +import samba +from samba import sd_utils +from ldb import LdbError + +class ReplAclTestCase(drs_base.DrsBaseTestCase): + + def setUp(self): + super(ReplAclTestCase, self).setUp() + self.mod = "(A;CIOI;GA;;;SY)" + self.mod_becomes = "(A;OICIIO;GA;;;SY)" + self.mod_inherits_as = "(A;OICIIOID;GA;;;SY)" + + self.sd_utils_dc1 = sd_utils.SDUtils(self.ldb_dc1) + self.sd_utils_dc2 = sd_utils.SDUtils(self.ldb_dc2) + + self.ou = samba.tests.create_test_ou(self.ldb_dc1, + "test_acl_inherit") + + # disable replication for the tests so we can control at what point + # the DCs try to replicate + self._disable_all_repl(self.dnsname_dc1) + self._disable_all_repl(self.dnsname_dc2) + + # make sure DCs are synchronized before the test + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + def tearDown(self): + self.ldb_dc1.delete(self.ou, ["tree_delete:1"]) + + # re-enable replication + self._enable_all_repl(self.dnsname_dc1) + self._enable_all_repl(self.dnsname_dc2) + + super(ReplAclTestCase, self).tearDown() + + def test_acl_inheirt_new_object_1_pass(self): + # Set the inherited ACL on the parent OU + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set stuck as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Make a new object + dn = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + self.ldb_dc1.add({"dn": dn, "objectclass": "organizationalUnit"}) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Assert ACL replicated as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(self.ou)) + + # Confirm inherited ACLs are identical and were inherited + + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(dn), + self.sd_utils_dc2.get_sd_as_sddl(dn)) + + def test_acl_inheirt_new_object(self): + # Set the inherited ACL on the parent OU + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set stuck as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Make a new object + dn = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + self.ldb_dc1.add({"dn": dn, "objectclass": "organizationalUnit"}) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Assert ACL replicated as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(self.ou)) + + # Confirm inherited ACLs are identical and were inherited + + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(dn), + self.sd_utils_dc2.get_sd_as_sddl(dn)) + + def test_acl_inherit_existing_object(self): + # Make a new object + dn = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + self.ldb_dc1.add({"dn": dn, "objectclass": "organizationalUnit"}) + + try: + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=dn, + attrs=[]) + self.fail() + except LdbError as err: + enum = err.args[0] + self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm it is now replicated + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=dn, + attrs=[]) + + # Set the inherited ACL on the parent OU + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set stuck as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm inherited ACLs are identical and were inherited + + # Assert ACL replicated as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(self.ou)) + + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(dn), + self.sd_utils_dc2.get_sd_as_sddl(dn)) + + def test_acl_inheirt_existing_object_1_pass(self): + # Make a new object + dn = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + self.ldb_dc1.add({"dn": dn, "objectclass": "organizationalUnit"}) + + try: + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=dn, + attrs=[]) + self.fail() + except LdbError as err: + enum = err.args[0] + self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT) + + # Set the inherited ACL on the parent OU + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Assert ACL replicated as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(self.ou)) + + # Confirm inherited ACLs are identical and were inherited + + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(dn), + self.sd_utils_dc2.get_sd_as_sddl(dn)) + + def test_acl_inheirt_renamed_object(self): + # Make a new object + new_ou = samba.tests.create_test_ou(self.ldb_dc1, + "acl_test_l2") + + sub_ou_dn = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + + try: + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=new_ou, + attrs=[]) + self.fail() + except LdbError as err: + enum = err.args[0] + self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm it is now replicated + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=new_ou, + attrs=[]) + + # Set the inherited ACL on the parent OU on DC1 + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Assert ACL replicated as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(self.ou)) + + # Rename to under self.ou + + self.ldb_dc1.rename(new_ou, sub_ou_dn) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm inherited ACLs are identical and were inherited + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(sub_ou_dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(sub_ou_dn), + self.sd_utils_dc2.get_sd_as_sddl(sub_ou_dn)) + + + def test_acl_inheirt_renamed_child_object(self): + # Make a new OU + new_ou = samba.tests.create_test_ou(self.ldb_dc1, + "acl_test_l2") + + # Here is where the new OU will end up at the end. + sub2_ou_dn_final = ldb.Dn(self.ldb_dc1, "OU=l2,%s" % self.ou) + + sub3_ou_dn = ldb.Dn(self.ldb_dc1, "OU=l3,%s" % new_ou) + sub3_ou_dn_final = ldb.Dn(self.ldb_dc1, "OU=l3,%s" % sub2_ou_dn_final) + + self.ldb_dc1.add({"dn": sub3_ou_dn, + "objectclass": "organizationalUnit"}) + + sub4_ou_dn = ldb.Dn(self.ldb_dc1, "OU=l4,%s" % sub3_ou_dn) + sub4_ou_dn_final = ldb.Dn(self.ldb_dc1, "OU=l4,%s" % sub3_ou_dn_final) + + self.ldb_dc1.add({"dn": sub4_ou_dn, + "objectclass": "organizationalUnit"}) + + try: + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=new_ou, + attrs=[]) + self.fail() + except LdbError as err: + enum = err.args[0] + self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm it is now replicated + self.ldb_dc2.search(scope=ldb.SCOPE_BASE, + base=new_ou, + attrs=[]) + + # + # Given a tree new_ou -> l3 -> l4 + # + + # Set the inherited ACL on the grandchild OU (l3) on DC1 + self.sd_utils_dc1.dacl_add_ace(sub3_ou_dn, self.mod) + + # Assert ACL set stuck as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(sub3_ou_dn)) + + # Rename new_ou (l2) to under self.ou (this must happen second). If the + # inheritance between l3 and l4 is name-based, this could + # break. + + # The tree is now self.ou -> l2 -> l3 -> l4 + + self.ldb_dc1.rename(new_ou, sub2_ou_dn_final) + + # Assert ACL set remained as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(sub3_ou_dn_final)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Confirm set ACLs (on l3 ) are identical and were inherited + self.assertIn(self.mod_becomes, + self.sd_utils_dc2.get_sd_as_sddl(sub3_ou_dn_final)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(sub3_ou_dn_final), + self.sd_utils_dc2.get_sd_as_sddl(sub3_ou_dn_final)) + + # Confirm inherited ACLs (from l3 to l4) are identical + # and were inherited + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(sub4_ou_dn_final)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(sub4_ou_dn_final), + self.sd_utils_dc2.get_sd_as_sddl(sub4_ou_dn_final)) + + + def test_acl_inheirt_renamed_object_in_conflict(self): + # Make a new object to be renamed under self.ou + new_ou = samba.tests.create_test_ou(self.ldb_dc1, + "acl_test_l2") + + # Make a new OU under self.ou (on DC2) + sub_ou_dn = ldb.Dn(self.ldb_dc2, "OU=l2,%s" % self.ou) + self.ldb_dc2.add({"dn": sub_ou_dn, + "objectclass": "organizationalUnit"}) + + # Set the inherited ACL on the parent OU + self.sd_utils_dc1.dacl_add_ace(self.ou, self.mod) + + # Assert ACL set stuck as expected + self.assertIn(self.mod_becomes, + self.sd_utils_dc1.get_sd_as_sddl(self.ou)) + + # Replicate to DC2 + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + # Rename to under self.ou + self.ldb_dc1.rename(new_ou, sub_ou_dn) + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(sub_ou_dn)) + + # Replicate to DC2 (will cause a conflict, DC1 to win, version + # is higher since named twice) + + self._net_drs_replicate(DC=self.dnsname_dc2, + fromDC=self.dnsname_dc1, + forced=True) + + children = self.ldb_dc2.search(scope=ldb.SCOPE_ONELEVEL, + base=self.ou, + attrs=[]) + for child in children: + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc2.get_sd_as_sddl(child.dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(sub_ou_dn), + self.sd_utils_dc2.get_sd_as_sddl(child.dn)) + + # Replicate back + self._net_drs_replicate(DC=self.dnsname_dc1, + fromDC=self.dnsname_dc2, + forced=True) + + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(sub_ou_dn)) + + for child in children: + self.assertIn(self.mod_inherits_as, + self.sd_utils_dc1.get_sd_as_sddl(child.dn)) + self.assertEqual(self.sd_utils_dc1.get_sd_as_sddl(child.dn), + self.sd_utils_dc2.get_sd_as_sddl(child.dn)) diff --git a/source4/torture/drs/python/replica_sync.py b/source4/torture/drs/python/replica_sync.py new file mode 100644 index 0000000..f40b16d --- /dev/null +++ b/source4/torture/drs/python/replica_sync.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests various schema replication scenarios +# +# Copyright (C) Kamen Mazdrashki 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN replica_sync -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +import time +import ldb + +from ldb import ( + SCOPE_BASE, LdbError, ERR_NO_SUCH_OBJECT) + + +class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase): + """Intended as a black box test case for DsReplicaSync + implementation. It should test the behavior of this + case in cases when inbound replication is disabled""" + + def setUp(self): + super(DrsReplicaSyncTestCase, self).setUp() + + # This OU avoids this test conflicting with anything + # that may already be in the DB + self.top_ou = samba.tests.create_test_ou(self.ldb_dc1, + "replica_sync") + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + self.ou1 = None + self.ou2 = None + + def tearDown(self): + self._cleanup_object(self.ou1) + self._cleanup_object(self.ou2) + self._cleanup_dn(self.top_ou) + + # re-enable replication + self._enable_inbound_repl(self.dnsname_dc1) + self._enable_inbound_repl(self.dnsname_dc2) + + super(DrsReplicaSyncTestCase, self).tearDown() + + def _cleanup_dn(self, dn): + try: + self.ldb_dc2.delete(dn, ["tree_delete:1"]) + except LdbError as e: + (num, _) = e.args + self.assertEqual(num, ERR_NO_SUCH_OBJECT) + try: + self.ldb_dc1.delete(dn, ["tree_delete:1"]) + except LdbError as e1: + (num, _) = e1.args + self.assertEqual(num, ERR_NO_SUCH_OBJECT) + + def _cleanup_object(self, guid): + """Cleans up a test object, if it still exists""" + if guid is not None: + self._cleanup_dn('' % guid) + + def test_ReplEnabled(self): + """Tests we can replicate when replication is enabled""" + self._enable_inbound_repl(self.dnsname_dc1) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=False) + + def test_ReplDisabled(self): + """Tests we can't replicate when replication is disabled""" + self._disable_inbound_repl(self.dnsname_dc1) + + ccache_name = self.get_creds_ccache_name() + + # Tunnel the command line credentials down to the + # subcommand to avoid a new kinit + cmdline_auth = "--use-krb5-ccache=%s" % ccache_name + + # bin/samba-tool drs + cmd_list = ["drs", "replicate", cmdline_auth] + + nc_dn = self.domain_dn + # bin/samba-tool drs replicate + cmd_list += [self.dnsname_dc1, self.dnsname_dc2, nc_dn] + + (result, out, err) = self.runsubcmd(*cmd_list) + self.assertCmdFail(result) + self.assertTrue('WERR_DS_DRA_SINK_DISABLED' in err) + + def test_ReplDisabledForced(self): + """Tests we can force replicate when replication is disabled""" + self._disable_inbound_repl(self.dnsname_dc1) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True) + + def test_ReplLocal(self): + """Tests we can replicate direct to the local db""" + self._enable_inbound_repl(self.dnsname_dc1) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=False, local=True, full_sync=True) + + def _create_ou(self, samdb, name): + ldif = """ +dn: %s,%s +objectClass: organizationalUnit +""" % (name, self.top_ou) + samdb.add_ldif(ldif) + res = samdb.search(base="%s,%s" % (name, self.top_ou), + scope=SCOPE_BASE, attrs=["objectGUID"]) + return self._GUID_string(res[0]["objectGUID"][0]) + + def _check_deleted(self, sam_ldb, guid): + # search the user by guid as it may be deleted + res = sam_ldb.search(base='' % guid, + controls=["show_deleted:1"], + attrs=["isDeleted", "objectCategory", "ou"]) + self.assertEqual(len(res), 1) + ou_cur = res[0] + # Deleted Object base DN + dodn = self._deleted_objects_dn(sam_ldb) + # now check properties of the user + name_cur = ou_cur["ou"][0] + self.assertEqual(ou_cur["isDeleted"][0], b"TRUE") + self.assertTrue(not("objectCategory" in ou_cur)) + self.assertTrue(dodn in str(ou_cur["dn"]), + "OU %s is deleted but it is not located under %s!" % (name_cur, dodn)) + + def test_ReplConflictsFullSync(self): + """Tests that objects created in conflict become conflict DNs (honour full sync override)""" + + # First confirm local replication (so when we test against windows, this fails fast without creating objects) + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, local=True, forced=True, full_sync=True) + + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Full Sync") + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Full Sync") + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, local=True, forced=True, full_sync=True) + + # Check that DC2 got the DC1 object, and OU1 was make into conflict + res1 = self.ldb_dc2.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc2.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertFalse('CNF:%s' % self.ou2 in str(res2[0]["name"][0])) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc2, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc2, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC2 + + self.ldb_dc2.delete('' % self.ou1) + self.ldb_dc2.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=True) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsRemoteWin(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Remote Conflict") + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Remote Conflict") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and OU1 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsLocalWin(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC2 object created first + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Local Conflict") + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Local Conflict") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and OU2 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou2 in str(res2[0]["name"][0]), "Got %s for %s" % (str(res2[0]["name"][0]), self.ou2)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsRemoteWin_with_child(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Parent Remote Conflict") + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Parent Remote Conflict") + # Create children on DC2 + ou1_child = self._create_ou(self.ldb_dc1, "OU=Test Child,OU=Test Parent Remote Conflict") + ou2_child = self._create_ou(self.ldb_dc2, "OU=Test Child,OU=Test Parent Remote Conflict") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and SELF.OU1 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1, ["tree_delete:1"]) + self.ldb_dc1.delete('' % self.ou2, ["tree_delete:1"]) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + self._check_deleted(self.ldb_dc1, ou1_child) + self._check_deleted(self.ldb_dc1, ou2_child) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, ou1_child) + self._check_deleted(self.ldb_dc2, ou2_child) + + def test_ReplConflictsRenamedVsNewRemoteWin(self): + """Tests resolving a DN conflict between a renamed object and a new object""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create an OU and rename it on DC1 + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Remote Rename Conflict orig") + self.ldb_dc1.rename("" % self.ou1, "OU=Test Remote Rename Conflict,%s" % self.top_ou) + + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + + # create a conflicting object with the same DN on DC2 + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Remote Rename Conflict") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and SELF.OU1 was made into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsRenamedVsNewLocalWin(self): + """Tests resolving a DN conflict between a renamed object and a new object""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, where the DC2 object has been renamed + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Rename Local Conflict orig") + self.ldb_dc2.rename("" % self.ou2, "OU=Test Rename Local Conflict,%s" % self.top_ou) + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Rename Local Conflict") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and OU2 was made into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou2 in str(res2[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsRenameRemoteWin(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Remote Rename Conflict") + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Remote Rename Conflict 2") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + self.ldb_dc1.rename("" % self.ou1, "OU=Test Remote Rename Conflict 3,%s" % self.top_ou) + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ldb_dc2.rename("" % self.ou2, "OU=Test Remote Rename Conflict 3,%s" % self.top_ou) + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and SELF.OU1 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplConflictsRenameRemoteWin_with_child(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Parent Remote Rename Conflict") + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Parent Remote Rename Conflict 2") + # Create children on DC2 + ou1_child = self._create_ou(self.ldb_dc1, "OU=Test Child,OU=Test Parent Remote Rename Conflict") + ou2_child = self._create_ou(self.ldb_dc2, "OU=Test Child,OU=Test Parent Remote Rename Conflict 2") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + self.ldb_dc1.rename("" % self.ou1, "OU=Test Parent Remote Rename Conflict 3,%s" % self.top_ou) + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ldb_dc2.rename("" % self.ou2, "OU=Test Parent Remote Rename Conflict 3,%s" % self.top_ou) + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and SELF.OU1 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1, ["tree_delete:1"]) + self.ldb_dc1.delete('' % self.ou2, ["tree_delete:1"]) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + self._check_deleted(self.ldb_dc1, ou1_child) + self._check_deleted(self.ldb_dc1, ou2_child) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, ou1_child) + self._check_deleted(self.ldb_dc2, ou2_child) + + def test_ReplConflictsRenameLocalWin(self): + """Tests that objects created in conflict become conflict DNs""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Rename Local Conflict") + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Rename Local Conflict 2") + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + self.ldb_dc2.rename("" % self.ou2, "OU=Test Rename Local Conflict 3,%s" % self.top_ou) + # We have to sleep to ensure that the two objects have different timestamps + time.sleep(1) + self.ldb_dc1.rename("" % self.ou1, "OU=Test Rename Local Conflict 3,%s" % self.top_ou) + + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC2 got the DC1 object, and OU2 was make into conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou2 in str(res2[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + + def test_ReplLostAndFound(self): + """Tests that objects created under a OU deleted eleswhere end up in lostAndFound""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create two OUs on DC2 + self.ou1 = self._create_ou(self.ldb_dc2, "OU=Deleted parent") + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Deleted parent 2") + + # replicate them from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Delete both objects by GUID on DC1 + + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + # Create children on DC2 + ou1_child = self._create_ou(self.ldb_dc2, "OU=Test Child,OU=Deleted parent") + ou2_child = self._create_ou(self.ldb_dc2, "OU=Test Child,OU=Deleted parent 2") + + # Replicate from DC2 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check the sub-OUs are now in lostAndFound and the first one is a conflict DN + + # Check that DC2 got the DC1 object, and one or other object was make into conflict + res1 = self.ldb_dc1.search(base="" % ou1_child, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % ou2_child, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % ou1_child in str(res1[0]["name"][0]) or 'CNF:%s' % ou2_child in str(res2[0]["name"][0])) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) in str(res1[0].dn)) + self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) in str(res2[0].dn)) + self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value()) + self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value()) + + # Delete all objects by GUID on DC1 + + self.ldb_dc1.delete('' % ou1_child) + self.ldb_dc1.delete('' % ou2_child) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + # Check all deleted on DC1 + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + self._check_deleted(self.ldb_dc1, ou1_child) + self._check_deleted(self.ldb_dc1, ou2_child) + # Check all deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + self._check_deleted(self.ldb_dc2, ou1_child) + self._check_deleted(self.ldb_dc2, ou2_child) + + def test_ReplRenames(self): + """Tests that objects created under a OU deleted eleswhere end up in lostAndFound""" + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # Create two OUs on DC2 + self.ou1 = self._create_ou(self.ldb_dc2, "OU=Original parent") + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Original parent 2") + + # replicate them from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Create children on DC1 + ou1_child = self._create_ou(self.ldb_dc1, "OU=Test Child,OU=Original parent") + ou2_child = self._create_ou(self.ldb_dc1, "OU=Test Child 2,OU=Original parent") + ou3_child = self._create_ou(self.ldb_dc1, "OU=Test Case Child,OU=Original parent") + + # replicate them from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self.ldb_dc1.rename("" % ou2_child, "OU=Test Child 3,OU=Original parent 2,%s" % self.top_ou) + self.ldb_dc1.rename("" % ou1_child, "OU=Test Child 2,OU=Original parent 2,%s" % self.top_ou) + self.ldb_dc1.rename("" % ou2_child, "OU=Test Child,OU=Original parent 2,%s" % self.top_ou) + self.ldb_dc1.rename("" % ou3_child, "OU=Test CASE Child,OU=Original parent,%s" % self.top_ou) + self.ldb_dc2.rename("" % self.ou2, "OU=Original parent 3,%s" % self.top_ou) + self.ldb_dc2.rename("" % self.ou1, "OU=Original parent 2,%s" % self.top_ou) + + # replicate them from DC1 to DC2 + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + # Check the sub-OUs are now under Original Parent 3 (original + # parent 2 for Test CASE Child), and both have the right names + + # Check that DC2 got the DC1 object, and the renames are all correct + res1 = self.ldb_dc2.search(base="" % ou1_child, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc2.search(base="" % ou2_child, + scope=SCOPE_BASE, attrs=["name"]) + res3 = self.ldb_dc2.search(base="" % ou3_child, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0].dn) + print(res2[0].dn) + print(res3[0].dn) + self.assertEqual('Test Child 2', str(res1[0]["name"][0])) + self.assertEqual('Test Child', str(res2[0]["name"][0])) + self.assertEqual('Test CASE Child', str(res3[0]["name"][0])) + self.assertEqual(str(res1[0].dn), "OU=Test Child 2,OU=Original parent 3,%s" % self.top_ou) + self.assertEqual(str(res2[0].dn), "OU=Test Child,OU=Original parent 3,%s" % self.top_ou) + self.assertEqual(str(res3[0].dn), "OU=Test CASE Child,OU=Original parent 2,%s" % self.top_ou) + + # replicate them from DC2 to DC1 + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check that DC1 got the DC2 object, and the renames are all correct + res1 = self.ldb_dc1.search(base="" % ou1_child, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % ou2_child, + scope=SCOPE_BASE, attrs=["name"]) + res3 = self.ldb_dc1.search(base="" % ou3_child, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0].dn) + print(res2[0].dn) + print(res3[0].dn) + self.assertEqual('Test Child 2', str(res1[0]["name"][0])) + self.assertEqual('Test Child', str(res2[0]["name"][0])) + self.assertEqual('Test CASE Child', str(res3[0]["name"][0])) + self.assertEqual(str(res1[0].dn), "OU=Test Child 2,OU=Original parent 3,%s" % self.top_ou) + self.assertEqual(str(res2[0].dn), "OU=Test Child,OU=Original parent 3,%s" % self.top_ou) + self.assertEqual(str(res3[0].dn), "OU=Test CASE Child,OU=Original parent 2,%s" % self.top_ou) + + # Delete all objects by GUID on DC1 + + self.ldb_dc1.delete('' % ou1_child) + self.ldb_dc1.delete('' % ou2_child) + self.ldb_dc1.delete('' % ou3_child) + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + # Check all deleted on DC1 + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + self._check_deleted(self.ldb_dc1, ou1_child) + self._check_deleted(self.ldb_dc1, ou2_child) + self._check_deleted(self.ldb_dc1, ou3_child) + # Check all deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) + self._check_deleted(self.ldb_dc2, ou1_child) + self._check_deleted(self.ldb_dc2, ou2_child) + self._check_deleted(self.ldb_dc2, ou3_child) + + def reanimate_object(self, samdb, guid, new_dn): + """Re-animates a deleted object""" + res = samdb.search(base="" % guid, attrs=["isDeleted"], + controls=['show_deleted:1'], scope=SCOPE_BASE) + if len(res) != 1: + return + + msg = ldb.Message() + msg.dn = res[0].dn + msg["isDeleted"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = ldb.MessageElement([new_dn], ldb.FLAG_MOD_REPLACE, "distinguishedName") + samdb.modify(msg, ["show_deleted:1"]) + + def test_ReplReanimationConflict(self): + """ + Checks that if a reanimated object conflicts with a new object, then + the conflict is resolved correctly. + """ + + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + # create an object, "accidentally" delete it, and replicate the changes to both DCs + self.ou1 = self._create_ou(self.ldb_dc2, "OU=Conflict object") + self.ldb_dc2.delete('' % self.ou1) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Now pretend that the admin for one DC resolves the problem by + # re-animating the object... + self.reanimate_object(self.ldb_dc1, self.ou1, "OU=Conflict object,%s" % self.top_ou) + + # ...whereas another admin just creates a user with the same name + # again on a different DC + time.sleep(1) + self.ou2 = self._create_ou(self.ldb_dc2, "OU=Conflict object") + + # Now sync the DCs to resolve the conflict + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False) + + # Check the latest change won and SELF.OU1 was made into a conflict + res1 = self.ldb_dc1.search(base="" % self.ou1, + scope=SCOPE_BASE, attrs=["name"]) + res2 = self.ldb_dc1.search(base="" % self.ou2, + scope=SCOPE_BASE, attrs=["name"]) + print(res1[0]["name"][0]) + print(res2[0]["name"][0]) + self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0])) + self.assertFalse('CNF:%s' % self.ou2 in str(res2[0]["name"][0])) + + # Delete both objects by GUID on DC1 + self.ldb_dc1.delete('' % self.ou1) + self.ldb_dc1.delete('' % self.ou2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False) + + self._check_deleted(self.ldb_dc1, self.ou1) + self._check_deleted(self.ldb_dc1, self.ou2) + # Check deleted on DC2 + self._check_deleted(self.ldb_dc2, self.ou1) + self._check_deleted(self.ldb_dc2, self.ou2) diff --git a/source4/torture/drs/python/replica_sync_rodc.py b/source4/torture/drs/python/replica_sync_rodc.py new file mode 100644 index 0000000..cbdcc12 --- /dev/null +++ b/source4/torture/drs/python/replica_sync_rodc.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Test conflict scenarios on the RODC +# +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Catalyst.NET Ltd 2018 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name (RODC) +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN replica_sync_rodc -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests +import time +import ldb +from samba.common import get_string + +from ldb import ( + SCOPE_BASE, LdbError, ERR_NO_SUCH_OBJECT) + + +class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase): + """Intended as a black box test case for DsReplicaSync + implementation. It should test the behavior of this + case in cases when inbound replication is disabled""" + + def setUp(self): + super(DrsReplicaSyncTestCase, self).setUp() + self._disable_all_repl(self.dnsname_dc1) + self.ou1 = None + self.ou2 = None + + def tearDown(self): + # re-enable replication + self._enable_all_repl(self.dnsname_dc1) + + super(DrsReplicaSyncTestCase, self).tearDown() + + def _create_ou(self, samdb, name): + ldif = """ +dn: %s,%s +objectClass: organizationalUnit +""" % (name, self.domain_dn) + samdb.add_ldif(ldif) + res = samdb.search(base="%s,%s" % (name, self.domain_dn), + scope=SCOPE_BASE, attrs=["objectGUID"]) + return get_string(self._GUID_string(res[0]["objectGUID"][0])) + + def _check_deleted(self, sam_ldb, guid): + # search the user by guid as it may be deleted + res = sam_ldb.search(base='' % guid, + controls=["show_deleted:1"], + attrs=["isDeleted", "objectCategory", "ou"]) + self.assertEqual(len(res), 1) + ou_cur = res[0] + # Deleted Object base DN + dodn = self._deleted_objects_dn(sam_ldb) + # now check properties of the user + name_cur = ou_cur["ou"][0] + self.assertEqual(ou_cur["isDeleted"][0], "TRUE") + self.assertTrue(not("objectCategory" in ou_cur)) + self.assertTrue(dodn in str(ou_cur["dn"]), + "OU %s is deleted but it is not located under %s!" % (name_cur, dodn)) + + def test_ReplConflictsRODC(self): + """Tests that objects created in conflict become conflict DNs""" + # Replicate all objects to RODC beforehand + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + name = "OU=Test RODC Conflict" + self.ou1 = self._create_ou(self.ldb_dc1, name) + + # Replicate single object + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn="%s,%s" % (name, self.domain_dn), + local=True, single=True, forced=True) + + # Delete the object, so another can be added + self.ldb_dc1.delete('' % self.ou1) + + # Create a conflicting DN as it would appear to the RODC + self.ou2 = self._create_ou(self.ldb_dc1, name) + + try: + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn="%s,%s" % (name, self.domain_dn), + local=True, single=True, forced=True) + except: + # Cleanup the object + self.ldb_dc1.delete('' % self.ou2) + return + + # Replicate cannot succeed, HWM would be updated incorrectly. + self.fail("DRS replicate should have failed.") + + def test_ReplConflictsRODCRename(self): + """Tests that objects created in conflict become conflict DNs""" + # Replicate all objects to RODC beforehand + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True) + + # Create conflicting objects on DC1 and DC2, with DC1 object created first + name = "OU=Test RODC Rename Conflict" + self.ou1 = self._create_ou(self.ldb_dc1, name) + + # Replicate single object + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn="%s,%s" % (name, self.domain_dn), + local=True, single=True, forced=True) + + # Create a non-conflicting DN to rename as conflicting + free_name = "OU=Test RODC Rename No Conflict" + self.ou2 = self._create_ou(self.ldb_dc1, free_name) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn="%s,%s" % (free_name, self.domain_dn), + local=True, single=True, forced=True) + + # Delete the object, so we can rename freely + # DO NOT REPLICATE TO THE RODC + self.ldb_dc1.delete('' % self.ou1) + + # Collide the name from the RODC perspective + self.ldb_dc1.rename("" % self.ou2, "%s,%s" % (name, self.domain_dn)) + + try: + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, + nc_dn="%s,%s" % (name, self.domain_dn), + local=True, single=True, forced=True) + except: + # Cleanup the object + self.ldb_dc1.delete('' % self.ou2) + return + + # Replicate cannot succeed, HWM would be updated incorrectly. + self.fail("DRS replicate should have failed.") diff --git a/source4/torture/drs/python/ridalloc_exop.py b/source4/torture/drs/python/ridalloc_exop.py new file mode 100644 index 0000000..ecd5cec --- /dev/null +++ b/source4/torture/drs/python/ridalloc_exop.py @@ -0,0 +1,802 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Tests various RID allocation scenarios +# +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2016 +# Copyright (C) Catalyst IT Ltd. 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN ridalloc_exop -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base + +import ldb +from ldb import SCOPE_BASE + +from samba.dcerpc import drsuapi, misc +from samba.samdb import SamDB + +import shutil +import os +from samba.auth import system_session, admin_session +from samba.dbchecker import dbcheck +from samba.ndr import ndr_pack +from samba.dcerpc import security +from samba import drs_utils, dsdb + + +class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase): + """Intended as a semi-black box test case for DsGetNCChanges + implementation for extended operations. It should be testing + how DsGetNCChanges handles different input params (mostly invalid). + Final goal is to make DsGetNCChanges as binary compatible to + Windows implementation as possible""" + + def setUp(self): + super(DrsReplicaSyncTestCase, self).setUp() + + def tearDown(self): + super(DrsReplicaSyncTestCase, self).tearDown() + + def _determine_fSMORoleOwner(self, fsmo_obj_dn): + """Returns (owner, not_owner) pair where: + owner: dns name for FSMO owner + not_owner: dns name for DC not owning the FSMO""" + # collect info to return later + fsmo_info_1 = {"dns_name": self.dnsname_dc1, + "invocation_id": self.ldb_dc1.get_invocation_id(), + "ntds_guid": self.ldb_dc1.get_ntds_GUID(), + "server_dn": self.ldb_dc1.get_serverName()} + fsmo_info_2 = {"dns_name": self.dnsname_dc2, + "invocation_id": self.ldb_dc2.get_invocation_id(), + "ntds_guid": self.ldb_dc2.get_ntds_GUID(), + "server_dn": self.ldb_dc2.get_serverName()} + + msgs = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=fsmo_info_1["server_dn"], attrs=["serverReference"]) + fsmo_info_1["server_acct_dn"] = ldb.Dn(self.ldb_dc1, msgs[0]["serverReference"][0].decode('utf8')) + fsmo_info_1["rid_set_dn"] = ldb.Dn(self.ldb_dc1, "CN=RID Set") + fsmo_info_1["server_acct_dn"] + + msgs = self.ldb_dc2.search(scope=ldb.SCOPE_BASE, base=fsmo_info_2["server_dn"], attrs=["serverReference"]) + fsmo_info_2["server_acct_dn"] = ldb.Dn(self.ldb_dc2, msgs[0]["serverReference"][0].decode('utf8')) + fsmo_info_2["rid_set_dn"] = ldb.Dn(self.ldb_dc2, "CN=RID Set") + fsmo_info_2["server_acct_dn"] + + # determine the owner dc + res = self.ldb_dc1.search(fsmo_obj_dn, + scope=SCOPE_BASE, attrs=["fSMORoleOwner"]) + assert len(res) == 1, "Only one fSMORoleOwner value expected for %s!" % fsmo_obj_dn + fsmo_owner = res[0]["fSMORoleOwner"][0] + if fsmo_owner == self.info_dc1["dsServiceName"][0]: + return (fsmo_info_1, fsmo_info_2) + return (fsmo_info_2, fsmo_info_1) + + def _check_exop_failed(self, ctr6, expected_failure): + self.assertEqual(ctr6.extended_ret, expected_failure) + #self.assertEqual(ctr6.object_count, 0) + #self.assertEqual(ctr6.first_object, None) + self.assertEqual(ctr6.more_data, False) + self.assertEqual(ctr6.nc_object_count, 0) + self.assertEqual(ctr6.nc_linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes, []) + self.assertEqual(ctr6.drs_error[0], 0) + + def test_InvalidDestDSA_ridalloc(self): + """Test RID allocation with invalid destination DSA guid""" + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER) + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + + def test_do_ridalloc(self): + """Test doing a RID allocation with a valid destination DSA guid""" + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa=fsmo_not_owner["ntds_guid"], + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + ctr6 = ctr + self.assertEqual(ctr6.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS) + self.assertEqual(ctr6.object_count, 3) + self.assertNotEqual(ctr6.first_object, None) + self.assertEqual(ldb.Dn(self.ldb_dc1, ctr6.first_object.object.identifier.dn), fsmo_dn) + self.assertNotEqual(ctr6.first_object.next_object, None) + self.assertNotEqual(ctr6.first_object.next_object.next_object, None) + second_object = ctr6.first_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, second_object.identifier.dn), fsmo_not_owner["rid_set_dn"]) + third_object = ctr6.first_object.next_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, third_object.identifier.dn), fsmo_not_owner["server_acct_dn"]) + + self.assertEqual(ctr6.more_data, False) + self.assertEqual(ctr6.nc_object_count, 0) + self.assertEqual(ctr6.nc_linked_attributes_count, 0) + self.assertEqual(ctr6.drs_error[0], 0) + # We don't check the linked_attributes_count as if the domain + # has an RODC, it can gain links on the server account object + + def test_do_ridalloc_get_anc(self): + """Test doing a RID allocation with a valid destination DSA guid and GET_ANC flag""" + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa=fsmo_not_owner["ntds_guid"], + invocation_id=fsmo_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC, + replica_flags=drsuapi.DRSUAPI_DRS_GET_ANC) + + (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"]) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"])) + ctr6 = ctr + self.assertEqual(ctr6.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS) + self.assertEqual(ctr6.object_count, 3) + self.assertNotEqual(ctr6.first_object, None) + self.assertEqual(ldb.Dn(self.ldb_dc1, ctr6.first_object.object.identifier.dn), fsmo_dn) + self.assertNotEqual(ctr6.first_object.next_object, None) + self.assertNotEqual(ctr6.first_object.next_object.next_object, None) + second_object = ctr6.first_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, second_object.identifier.dn), fsmo_not_owner["rid_set_dn"]) + third_object = ctr6.first_object.next_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, third_object.identifier.dn), fsmo_not_owner["server_acct_dn"]) + self.assertEqual(ctr6.more_data, False) + self.assertEqual(ctr6.nc_object_count, 0) + self.assertEqual(ctr6.nc_linked_attributes_count, 0) + self.assertEqual(ctr6.drs_error[0], 0) + # We don't check the linked_attributes_count as if the domain + # has an RODC, it can gain links on the server account object + + def test_edit_rid_master(self): + """Test doing a RID allocation after changing the RID master from the original one. + This should set rIDNextRID to 0 on the new RID master.""" + # 1. a. Transfer role to non-RID master + # b. Check that it succeeds correctly + # + # 2. a. Call the RID alloc against the former master. + # b. Check that it succeeds. + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + # 1. Swap RID master role + m = ldb.Message() + m.dn = ldb.Dn(self.ldb_dc1, "") + m["becomeRidMaster"] = ldb.MessageElement("1", ldb.FLAG_MOD_REPLACE, + "becomeRidMaster") + + # Make sure that ldb_dc1 == RID Master + + server_dn = str(ldb.Dn(self.ldb_dc1, self.ldb_dc1.get_dsServiceName()).parent()) + + # self.ldb_dc1 == LOCALDC + if server_dn == fsmo_owner['server_dn']: + # ldb_dc1 == VAMPIREDC + ldb_dc1, ldb_dc2 = self.ldb_dc2, self.ldb_dc1 + else: + # Otherwise switch the two + ldb_dc1, ldb_dc2 = self.ldb_dc1, self.ldb_dc2 + + try: + # ldb_dc1 is now RID MASTER (as VAMPIREDC) + ldb_dc1.modify(m) + except ldb.LdbError as e1: + (num, msg) = e1.args + self.fail("Failed to reassign RID Master " + msg) + + try: + # 2. Perform a RID alloc + req8 = self._exop_req8(dest_dsa=fsmo_owner["ntds_guid"], + invocation_id=fsmo_not_owner["invocation_id"], + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC) + + (drs, drs_handle) = self._ds_bind(fsmo_not_owner["dns_name"]) + # 3. Make sure the allocation succeeds + try: + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + except RuntimeError as e: + self.fail("RID allocation failed: " + str(e)) + + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + + self.assertEqual(level, 6, "Expected level 6 response!") + self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_not_owner["ntds_guid"])) + self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_not_owner["invocation_id"])) + ctr6 = ctr + self.assertEqual(ctr6.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS) + self.assertEqual(ctr6.object_count, 3) + self.assertNotEqual(ctr6.first_object, None) + self.assertEqual(ldb.Dn(ldb_dc2, ctr6.first_object.object.identifier.dn), fsmo_dn) + self.assertNotEqual(ctr6.first_object.next_object, None) + self.assertNotEqual(ctr6.first_object.next_object.next_object, None) + second_object = ctr6.first_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, second_object.identifier.dn), fsmo_owner["rid_set_dn"]) + third_object = ctr6.first_object.next_object.next_object.object + self.assertEqual(ldb.Dn(self.ldb_dc1, third_object.identifier.dn), fsmo_owner["server_acct_dn"]) + finally: + # Swap the RID master back for other tests + m = ldb.Message() + m.dn = ldb.Dn(ldb_dc2, "") + m["becomeRidMaster"] = ldb.MessageElement("1", ldb.FLAG_MOD_REPLACE, "becomeRidMaster") + try: + ldb_dc2.modify(m) + except ldb.LdbError as e: + (num, msg) = e.args + self.fail("Failed to restore RID Master " + msg) + + def test_offline_samba_tool_seized_ridalloc(self): + """Perform a join against the non-RID manager and then seize the RID Manager role""" + + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_not_owner['dns_name'], "RIDALLOCTEST1") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + smbconf = os.path.join(targetdir, "etc/smb.conf") + + lp = self.get_loadparm() + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # Assert that no RID Set has been set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertFalse("rIDSetReferences" in res[0]) + + (result, out, err) = self.runsubcmd("fsmo", "seize", "--role", "rid", "-H", ldb_url, "--configfile=%s" % (smbconf), "--force") + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + finally: + shutil.rmtree(targetdir, ignore_errors=True) + self._test_force_demote(fsmo_not_owner['dns_name'], "RIDALLOCTEST1") + + def _test_join(self, server, netbios_name): + tmpdir = os.path.join(self.tempdir, "targetdir") + creds = self.get_credentials() + (result, out, err) = self.runsubcmd("domain", "join", + creds.get_realm(), + "dc", "-U%s%%%s" % (creds.get_username(), + creds.get_password()), + '--targetdir=%s' % tmpdir, + '--server=%s' % server, + "--option=netbios name = %s" % netbios_name) + self.assertCmdSuccess(result, out, err) + return tmpdir + + def _test_force_demote(self, server, netbios_name): + creds = self.get_credentials() + (result, out, err) = self.runsubcmd("domain", "demote", + "-U%s%%%s" % (creds.get_username(), + creds.get_password()), + '--server=%s' % server, + "--remove-other-dead-server=%s" % netbios_name) + self.assertCmdSuccess(result, out, err) + + def test_offline_manual_seized_ridalloc_with_dbcheck(self): + """Perform the same actions as test_offline_samba_tool_seized_ridalloc, + but do not create the RID set. Confirm that dbcheck correctly creates + the RID Set. + + Also check + """ + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_not_owner['dns_name'], "RIDALLOCTEST2") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + lp = self.get_loadparm() + + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + serviceName = new_ldb.get_dsServiceName() + m = ldb.Message() + m.dn = fsmo_dn + m["fSMORoleOwner"] = ldb.MessageElement(serviceName, + ldb.FLAG_MOD_REPLACE, + "fSMORoleOwner") + new_ldb.modify(m) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # Assert that no RID Set has been set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertFalse("rIDSetReferences" in res[0]) + + chk = dbcheck(new_ldb, verbose=False, fix=True, yes=True, quiet=True) + + self.assertEqual(chk.check_database(DN=server_ref_dn, scope=ldb.SCOPE_BASE), 1, "Should have fixed one error (missing RID Set)") + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + finally: + self._test_force_demote(fsmo_not_owner['dns_name'], "RIDALLOCTEST2") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_offline_manual_seized_ridalloc_add_user(self): + """Perform the same actions as test_offline_samba_tool_seized_ridalloc, + but do not create the RID set. Confirm that user-add correctly creates + the RID Set.""" + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_not_owner['dns_name'], "RIDALLOCTEST3") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + lp = self.get_loadparm() + + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + serviceName = new_ldb.get_dsServiceName() + m = ldb.Message() + m.dn = fsmo_dn + m["fSMORoleOwner"] = ldb.MessageElement(serviceName, + ldb.FLAG_MOD_REPLACE, + "fSMORoleOwner") + new_ldb.modify(m) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # Assert that no RID Set has been set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertFalse("rIDSetReferences" in res[0]) + + new_ldb.newuser("ridalloctestuser", "P@ssword!") + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + + finally: + self._test_force_demote(fsmo_not_owner['dns_name'], "RIDALLOCTEST3") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_offline_manual_seized_ridalloc_add_user_as_admin(self): + """Perform the same actions as test_offline_samba_tool_seized_ridalloc, + but do not create the RID set. Confirm that user-add correctly creates + the RID Set.""" + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_not_owner['dns_name'], "RIDALLOCTEST4") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + lp = self.get_loadparm() + + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=admin_session(lp, self.ldb_dc1.get_domain_sid()), lp=lp) + + serviceName = new_ldb.get_dsServiceName() + m = ldb.Message() + m.dn = fsmo_dn + m["fSMORoleOwner"] = ldb.MessageElement(serviceName, + ldb.FLAG_MOD_REPLACE, + "fSMORoleOwner") + new_ldb.modify(m) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # Assert that no RID Set has been set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertFalse("rIDSetReferences" in res[0]) + + # Create a user to allocate a RID Set for itself (the RID master) + new_ldb.newuser("ridalloctestuser", "P@ssword!") + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + + finally: + self._test_force_demote(fsmo_not_owner['dns_name'], "RIDALLOCTEST4") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_join_time_ridalloc(self): + """Perform a join against the RID manager and assert we have a RID Set""" + + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_owner['dns_name'], "RIDALLOCTEST5") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + + lp = self.get_loadparm() + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + finally: + self._test_force_demote(fsmo_owner['dns_name'], "RIDALLOCTEST5") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_rid_set_dbcheck(self): + """Perform a join against the RID manager and assert we have a RID Set. + Using dbcheck, we assert that we can detect out of range users.""" + + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_owner['dns_name'], "RIDALLOCTEST6") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + + lp = self.get_loadparm() + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + rid_set_dn = ldb.Dn(new_ldb, res[0]["rIDSetReferences"][0].decode('utf8')) + + # 4. Add a new user (triggers RID set work) + new_ldb.newuser("ridalloctestuser", "P@ssword!") + + # 5. Now fetch the RID SET + rid_set_res = new_ldb.search(base=rid_set_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDNextRid', + 'rIDAllocationPool']) + next_pool = int(rid_set_res[0]["rIDAllocationPool"][0]) + last_rid = (0xFFFFFFFF00000000 & next_pool) >> 32 + + # 6. Add user above the ridNextRid and at mid-range. + # + # We can do this with safety because this is an offline DB that will be + # destroyed. + m = ldb.Message() + m.dn = ldb.Dn(new_ldb, "CN=ridsettestuser1,CN=Users") + m.dn.add_base(new_ldb.get_default_basedn()) + m['objectClass'] = ldb.MessageElement('user', ldb.FLAG_MOD_ADD, 'objectClass') + m['objectSid'] = ldb.MessageElement(ndr_pack(security.dom_sid(str(new_ldb.get_domain_sid()) + "-%d" % (last_rid - 10))), + ldb.FLAG_MOD_ADD, + 'objectSid') + new_ldb.add(m, controls=["relax:0"]) + + # 7. Check the RID Set + chk = dbcheck(new_ldb, verbose=False, fix=True, yes=True, quiet=True) + + # Should have one error (wrong rIDNextRID) + self.assertEqual(chk.check_database(DN=rid_set_dn, scope=ldb.SCOPE_BASE), 1) + + # 8. Assert we get didn't show any other errors + chk = dbcheck(new_ldb, verbose=False, fix=False, quiet=True) + + rid_set_res = new_ldb.search(base=rid_set_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDNextRid', + 'rIDAllocationPool']) + last_allocated_rid = int(rid_set_res[0]["rIDNextRid"][0]) + self.assertEqual(last_allocated_rid, last_rid - 10) + + # 9. Assert that the range wasn't thrown away + + next_pool = int(rid_set_res[0]["rIDAllocationPool"][0]) + self.assertEqual(last_rid, (0xFFFFFFFF00000000 & next_pool) >> 32, "rid pool should have changed") + finally: + self._test_force_demote(fsmo_owner['dns_name'], "RIDALLOCTEST6") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_rid_set_dbcheck_after_seize(self): + """Perform a join against the RID manager and assert we have a RID Set. + We seize the RID master role, then using dbcheck, we assert that we can + detect out of range users (and then bump the RID set as required).""" + + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + targetdir = self._test_join(fsmo_owner['dns_name'], "RIDALLOCTEST7") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + smbconf = os.path.join(targetdir, "etc/smb.conf") + + lp = self.get_loadparm() + new_ldb = SamDB(ldb_url, credentials=self.get_credentials(), + session_info=system_session(lp), lp=lp) + + # 1. Get server name + res = new_ldb.search(base=ldb.Dn(new_ldb, new_ldb.get_serverName()), + scope=ldb.SCOPE_BASE, attrs=["serverReference"]) + # 2. Get server reference + server_ref_dn = ldb.Dn(new_ldb, res[0]['serverReference'][0].decode('utf8')) + + # 3. Assert we get the RID Set + res = new_ldb.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences']) + + self.assertTrue("rIDSetReferences" in res[0]) + rid_set_dn = ldb.Dn(new_ldb, res[0]["rIDSetReferences"][0].decode('utf8')) + # 4. Seize the RID Manager role + (result, out, err) = self.runsubcmd("fsmo", "seize", "--role", "rid", "-H", ldb_url, "--configfile=%s" % (smbconf), "--force") + self.assertCmdSuccess(result, out, err) + self.assertEqual(err, "", "Shouldn't be any error messages") + + # 5. Add a new user (triggers RID set work) + new_ldb.newuser("ridalloctestuser", "P@ssword!") + + # 6. Now fetch the RID SET + rid_set_res = new_ldb.search(base=rid_set_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDNextRid', + 'rIDAllocationPool']) + next_pool = int(rid_set_res[0]["rIDAllocationPool"][0]) + last_rid = (0xFFFFFFFF00000000 & next_pool) >> 32 + + # 7. Add user above the ridNextRid and at almost the end of the range. + # + m = ldb.Message() + m.dn = ldb.Dn(new_ldb, "CN=ridsettestuser2,CN=Users") + m.dn.add_base(new_ldb.get_default_basedn()) + m['objectClass'] = ldb.MessageElement('user', ldb.FLAG_MOD_ADD, 'objectClass') + m['objectSid'] = ldb.MessageElement(ndr_pack(security.dom_sid(str(new_ldb.get_domain_sid()) + "-%d" % (last_rid - 3))), + ldb.FLAG_MOD_ADD, + 'objectSid') + new_ldb.add(m, controls=["relax:0"]) + + # 8. Add user above the ridNextRid and at the end of the range + m = ldb.Message() + m.dn = ldb.Dn(new_ldb, "CN=ridsettestuser3,CN=Users") + m.dn.add_base(new_ldb.get_default_basedn()) + m['objectClass'] = ldb.MessageElement('user', ldb.FLAG_MOD_ADD, 'objectClass') + m['objectSid'] = ldb.MessageElement(ndr_pack(security.dom_sid(str(new_ldb.get_domain_sid()) + "-%d" % last_rid)), + ldb.FLAG_MOD_ADD, + 'objectSid') + new_ldb.add(m, controls=["relax:0"]) + + chk = dbcheck(new_ldb, verbose=False, fix=True, yes=True, quiet=True) + + # Should have fixed two errors (wrong ridNextRid) + self.assertEqual(chk.check_database(DN=rid_set_dn, scope=ldb.SCOPE_BASE), 2) + + # 9. Assert we get didn't show any other errors + chk = dbcheck(new_ldb, verbose=False, fix=False, quiet=True) + + # 10. Add another user (checks RID rollover) + # We have seized the role, so we can do that. + new_ldb.newuser("ridalloctestuser3", "P@ssword!") + + rid_set_res = new_ldb.search(base=rid_set_dn, + scope=ldb.SCOPE_BASE, attrs=['rIDNextRid', + 'rIDAllocationPool']) + next_pool = int(rid_set_res[0]["rIDAllocationPool"][0]) + self.assertNotEqual(last_rid, (0xFFFFFFFF00000000 & next_pool) >> 32, "rid pool should have changed") + finally: + self._test_force_demote(fsmo_owner['dns_name'], "RIDALLOCTEST7") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_replicate_against_deleted_objects_transaction(self): + """Not related to RID allocation, but uses the infrastructure here. + Do a join, create a link between two objects remotely, but + remove the target locally. Show that we need to set a magic + opaque if there is an outer transaction. + + """ + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + test_user4 = "ridalloctestuser4" + test_group = "ridalloctestgroup1" + + self.ldb_dc1.newuser(test_user4, "P@ssword!") + + self.addCleanup(self.ldb_dc1.deleteuser, test_user4) + + self.ldb_dc1.newgroup(test_group) + self.addCleanup(self.ldb_dc1.deletegroup, test_group) + + targetdir = self._test_join(self.dnsname_dc1, "RIDALLOCTEST8") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + lp = self.get_loadparm() + + new_ldb = SamDB(ldb_url, + session_info=system_session(lp), lp=lp) + + destination_dsa_guid = misc.GUID(new_ldb.get_ntds_GUID()) + + repl = drs_utils.drs_Replicate(f'ncacn_ip_tcp:{self.dnsname_dc1}[seal]', + lp, + self.get_credentials(), + new_ldb, + destination_dsa_guid) + + source_dsa_invocation_id = misc.GUID(self.ldb_dc1.invocation_id) + + # Add the link on the remote DC + self.ldb_dc1.add_remove_group_members(test_group, [test_user4]) + + # Starting a transaction overrides, currently the logic + # inside repl.replicatate to retry with GET_TGT which in + # turn tells the repl_meta_data module that the most up to + # date info is already available + new_ldb.transaction_start() + repl.replicate(self.ldb_dc1.domain_dn(), + source_dsa_invocation_id, + destination_dsa_guid) + + # Delete the user locally, before applying the links. + # This simulates getting the delete in the replciation + # stream. + new_ldb.deleteuser(test_user4) + + # This fails as the user has been deleted locally but a remote link is sent + self.assertRaises(ldb.LdbError, new_ldb.transaction_commit) + + new_ldb.transaction_start() + repl.replicate(self.ldb_dc1.domain_dn(), + source_dsa_invocation_id, + destination_dsa_guid) + + # Delete the user locally (the previous transaction + # doesn't apply), before applying the links. This + # simulates getting the delete in the replciation stream. + new_ldb.deleteuser(test_user4) + + new_ldb.set_opaque_integer(dsdb.DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME, + 1) + + # This should now work + try: + new_ldb.transaction_commit() + except ldb.LdbError as e: + self.fail(f"Failed to replicate despite setting opaque with {e.args[1]}") + + finally: + self._test_force_demote(self.dnsname_dc1, "RIDALLOCTEST8") + shutil.rmtree(targetdir, ignore_errors=True) + + def test_replicate_against_deleted_objects_normal(self): + """Not related to RID allocation, but uses the infrastructure here. + Do a join, create a link between two objects remotely, but + remove the target locally. . + + """ + fsmo_dn = ldb.Dn(self.ldb_dc1, "CN=RID Manager$,CN=System," + self.ldb_dc1.domain_dn()) + (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn) + + test_user5 = "ridalloctestuser5" + test_group2 = "ridalloctestgroup2" + + self.ldb_dc1.newuser(test_user5, "P@ssword!") + self.addCleanup(self.ldb_dc1.deleteuser, test_user5) + + self.ldb_dc1.newgroup(test_group2) + self.addCleanup(self.ldb_dc1.deletegroup, test_group2) + + targetdir = self._test_join(self.dnsname_dc1, "RIDALLOCTEST9") + try: + # Connect to the database + ldb_url = "tdb://%s" % os.path.join(targetdir, "private/sam.ldb") + lp = self.get_loadparm() + + new_ldb = SamDB(ldb_url, + session_info=system_session(lp), lp=lp) + + destination_dsa_guid = misc.GUID(new_ldb.get_ntds_GUID()) + + repl = drs_utils.drs_Replicate(f'ncacn_ip_tcp:{self.dnsname_dc1}[seal]', + lp, + self.get_credentials(), + new_ldb, + destination_dsa_guid) + + source_dsa_invocation_id = misc.GUID(self.ldb_dc1.invocation_id) + + # Add the link on the remote DC + self.ldb_dc1.add_remove_group_members(test_group2, [test_user5]) + + # Delete the user locally + new_ldb.deleteuser(test_user5) + + # Confirm replication copes with a link to a locally deleted user + repl.replicate(self.ldb_dc1.domain_dn(), + source_dsa_invocation_id, + destination_dsa_guid) + + finally: + self._test_force_demote(self.dnsname_dc1, "RIDALLOCTEST9") + shutil.rmtree(targetdir, ignore_errors=True) diff --git a/source4/torture/drs/python/samba_tool_drs.py b/source4/torture/drs/python/samba_tool_drs.py new file mode 100644 index 0000000..e622fe4 --- /dev/null +++ b/source4/torture/drs/python/samba_tool_drs.py @@ -0,0 +1,410 @@ +# Blackbox tests for "samba-tool drs" command +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +"""Blackbox tests for samba-tool drs.""" + +import samba.tests +import os +import ldb +import drs_base + + +class SambaToolDrsTests(drs_base.DrsBaseTestCase): + """Blackbox test case for samba-tool drs.""" + + def setUp(self): + super(SambaToolDrsTests, self).setUp() + + self.dc1 = samba.tests.env_get_var_value("DC1") + self.dc2 = samba.tests.env_get_var_value("DC2") + + creds = self.get_credentials() + self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), + creds.get_username(), creds.get_password()) + + def tearDown(self): + self._enable_inbound_repl(self.dnsname_dc1) + self._enable_inbound_repl(self.dnsname_dc2) + + self.rm_files('names.tdb', allow_missing=True) + self.rm_dirs('etc', 'msg.lock', 'private', 'state', 'bind-dns', + allow_missing=True) + + super(SambaToolDrsTests, self).tearDown() + + def _get_rootDSE(self, dc, ldap_only=True): + samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(), + credentials=self.get_credentials(), + ldap_only=ldap_only) + return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0] + + def test_samba_tool_bind(self): + """Tests 'samba-tool drs bind' command.""" + + # Output should be like: + # Extensions supported: + # + # Site GUID: + # Repl epoch: 0 + out = self.check_output("samba-tool drs bind %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue("Site GUID:" in out.decode('utf8')) + self.assertTrue("Repl epoch:" in out.decode('utf8')) + + def test_samba_tool_kcc(self): + """Tests 'samba-tool drs kcc' command.""" + + # Output should be like 'Consistency check on successful.' + out = self.check_output("samba-tool drs kcc %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue(b"Consistency check on" in out) + self.assertTrue(b"successful" in out) + + def test_samba_tool_options(self): + """Tests 'samba-tool drs options' command + """ + # Output should be like 'Current DSA options: IS_GC ' + out = self.check_output("samba-tool drs options %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue(b"Current DSA options:" in out) + + def test_samba_tool_replicate(self): + """Tests 'samba-tool drs replicate' command.""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate %s %s %s %s" % (self.dc1, + self.dc2, + nc_name, + self.cmdline_creds)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was successful" in out) + + def test_samba_tool_replicate_async(self): + """Tests 'samba-tool drs replicate --async-op' command.""" + + # Output should be like 'Replicate from to was started.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate --async-op %s %s %s %s" % (self.dc1, + self.dc2, + nc_name, + self.cmdline_creds)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was started" in out) + + def test_samba_tool_replicate_local_online(self): + """Tests 'samba-tool drs replicate --local-online' command.""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate --local-online %s %s %s" % (self.dc1, + self.dc2, + nc_name)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was successful" in out) + + def test_samba_tool_replicate_local_online_async(self): + """Tests 'samba-tool drs replicate --local-online --async-op' command.""" + + # Output should be like 'Replicate from to was started.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate --local-online --async-op %s %s %s" % (self.dc1, + self.dc2, + nc_name)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was started" in out) + + def test_samba_tool_replicate_local_machine_creds(self): + """Tests 'samba-tool drs replicate --local -P' command (uses machine creds).""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate -P --local %s %s %s" % (self.dc1, + self.dc2, + nc_name)) + self.assertTrue(b"Incremental" in out) + self.assertTrue(b"was successful" in out) + + def test_samba_tool_replicate_local(self): + """Tests 'samba-tool drs replicate --local' command (uses machine creds).""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + + def get_num_obj_links(output): + num_objs = None + num_links = None + for word in output.decode('utf8').split(" "): + try: + int(word) + if num_objs is None: + num_objs = int(word) + elif num_links is None: + num_links = int(word) + except ValueError: + pass + + return (num_objs, num_links) + + out = self.check_output("samba-tool drs replicate --local --full-sync %s %s %s %s" + % (self.dc1, self.dc2, nc_name, self.cmdline_creds)) + self.assertTrue(b"was successful" in out) + self.assertTrue(b"Full" in out) + + (first_obj, _) = get_num_obj_links(out) + + out = self.check_output("samba-tool drs replicate --local %s %s %s %s" + % (self.dc1, self.dc2, nc_name, self.cmdline_creds)) + self.assertTrue(b"was successful" in out) + self.assertTrue(b"Incremental" in out) + + (second_obj, _) = get_num_obj_links(out) + + self.assertTrue(first_obj > second_obj) + + server_rootdse = self._get_rootDSE(self.dc1) + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + + # We have to give it a different netbiosname every time + # it runs, otherwise the collision causes strange issues + # to happen. This should be different on different environments. + netbiosname = "test" + self.dc2 + if len(netbiosname) > 15: + netbiosname = netbiosname[:15] + + out = self.check_output("samba-tool domain join %s dc --server=%s %s --targetdir=%s --option=netbiosname=%s" + % (server_realm, self.dc1, self.cmdline_creds, self.tempdir, netbiosname)) + + new_dc_config_file = "%s/etc/smb.conf" % self.tempdir + + self.check_output("samba-tool drs replicate --local %s %s %s %s --configfile=%s" + % ("invalid", self.dc1, nc_name, + self.cmdline_creds, new_dc_config_file)) + + self._disable_inbound_repl(self.dnsname_dc1) + self._disable_inbound_repl(self.dnsname_dc2) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2) + + # add an object with link on dc1 + group_name = "group-repl-local-%s" % self.dc2 + user_name = "user-repl-local-%s" % self.dc2 + + self.check_output("samba-tool group add %s %s -H ldap://%s" + % (group_name, self.cmdline_creds, self.dc1)) + self.check_output("samba-tool user add %s %s --random-password -H ldap://%s" + % (user_name, self.cmdline_creds, self.dc1)) + self.check_output("samba-tool group addmembers %s %s %s -H ldap://%s" + % (group_name, user_name, self.cmdline_creds, self.dc1)) + + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1) + + # pull that change with --local into local db from dc1: should send link and some objects + out = self.check_output("samba-tool drs replicate --local %s %s %s %s --configfile=%s" + % ("invalid", self.dc1, nc_name, + self.cmdline_creds, new_dc_config_file)) + + (obj_1, link_1) = get_num_obj_links(out) + + self.assertGreaterEqual(obj_1, 2) + self.assertEqual(link_1, 1) + + # pull that change with --local into local db from dc2: shouldn't send link or object + # as we sent an up-to-dateness vector showing that we had already synced with DC1 + out = self.check_output("samba-tool drs replicate --local %s %s %s %s --configfile=%s" + % ("invalid", self.dc2, nc_name, + self.cmdline_creds, new_dc_config_file)) + + (obj_2, link_2) = get_num_obj_links(out) + + self.assertEqual(obj_2, 0) + self.assertEqual(link_2, 0) + + self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H ldap://%s %s --configfile=%s" + % (netbiosname, self.dc1, self.cmdline_creds, new_dc_config_file)) + + def test_samba_tool_replicate_machine_creds_P(self): + """Tests 'samba-tool drs replicate -P' command with machine creds.""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate -P %s %s %s" % (self.dc1, + self.dc2, + nc_name)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was successful" in out) + + def test_samba_tool_replicate_machine_creds(self): + """Tests 'samba-tool drs replicate' command with implicit machine creds.""" + + # Output should be like 'Replicate from to was successful.' + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate %s %s %s" % (self.dc1, + self.dc2, + nc_name)) + self.assertTrue(b"Replicate from" in out) + self.assertTrue(b"was successful" in out) + + def test_samba_tool_drs_clone_dc(self): + """Tests 'samba-tool drs clone-dc-database' command.""" + server_rootdse = self._get_rootDSE(self.dc1) + server_nc_name = server_rootdse["defaultNamingContext"] + server_ds_name = server_rootdse["dsServiceName"] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + self.check_output("samba-tool drs clone-dc-database %s --server=%s %s --targetdir=%s" + % (server_realm, + self.dc1, + self.cmdline_creds, + self.tempdir)) + ldb_rootdse = self._get_rootDSE("ldb://" + os.path.join(self.tempdir, "private", "sam.ldb"), ldap_only=False) + nc_name = ldb_rootdse["defaultNamingContext"] + ds_name = ldb_rootdse["dsServiceName"] + ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + self.assertEqual(nc_name, server_nc_name) + # The clone should pretend to be the source server + self.assertEqual(ds_name, server_ds_name) + self.assertEqual(ldap_service_name, server_ldap_service_name) + + samdb = samba.tests.connect_samdb("ldb://" + os.path.join(self.tempdir, "private", "sam.ldb"), + ldap_only=False, lp=self.get_loadparm()) + + def get_krbtgt_pw(): + samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) + self.assertRaises(KeyError, get_krbtgt_pw) + + server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)).decode('utf8') + ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn).decode('utf8') + + res = samdb.search(base=str(server_nc_name), + expression="(&(objectclass=user)(cn=dns-%s))" % (self.dc2), + attrs=[], scope=ldb.SCOPE_SUBTREE) + if len(res) == 1: + dns_obj = res[0] + else: + dns_obj = None + + # While we have this cloned, try demoting the other server on the clone, by GUID + self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (ntds_guid, + self.tempdir)) + + # Check some of the objects that should have been removed + def check_machine_obj(): + samdb.searchone("CN", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + self.assertRaises(ldb.LdbError, check_machine_obj) + + def check_server_obj(): + samdb.searchone("CN", server_dn) + self.assertRaises(ldb.LdbError, check_server_obj) + + def check_ntds_guid(): + samdb.searchone("CN", "" % ntds_guid) + self.assertRaises(ldb.LdbError, check_ntds_guid) + + if dns_obj is not None: + # Check some of the objects that should have been removed + def check_dns_account_obj(): + samdb.search(base=dns_obj.dn, scope=ldb.SCOPE_BASE, + attrs=[]) + self.assertRaises(ldb.LdbError, check_dns_account_obj) + + def test_samba_tool_drs_clone_dc_secrets(self): + """Tests 'samba-tool drs clone-dc-database --include-secrets' command .""" + server_rootdse = self._get_rootDSE(self.dc1) + server_nc_name = server_rootdse["defaultNamingContext"] + server_ds_name = server_rootdse["dsServiceName"] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + self.check_output("samba-tool drs clone-dc-database %s --server=%s %s --targetdir=%s --include-secrets" + % (server_realm, + self.dc1, + self.cmdline_creds, + self.tempdir)) + ldb_rootdse = self._get_rootDSE("ldb://" + os.path.join(self.tempdir, "private", "sam.ldb"), ldap_only=False) + nc_name = ldb_rootdse["defaultNamingContext"] + ds_name = ldb_rootdse["dsServiceName"] + ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + + samdb = samba.tests.connect_samdb("ldb://" + os.path.join(self.tempdir, "private", "sam.ldb"), + ldap_only=False, lp=self.get_loadparm()) + krbtgt_pw = samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) + self.assertIsNotNone(krbtgt_pw) + + self.assertEqual(nc_name, server_nc_name) + # The clone should pretend to be the source server + self.assertEqual(ds_name, server_ds_name) + self.assertEqual(ldap_service_name, server_ldap_service_name) + + server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)).decode('utf8') + ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn).decode('utf8') + + res = samdb.search(base=str(server_nc_name), + expression="(&(objectclass=user)(cn=dns-%s))" % (self.dc2), + attrs=[], scope=ldb.SCOPE_SUBTREE) + if len(res) == 1: + dns_obj = res[0] + else: + dns_obj = None + + def demote_self(): + # While we have this cloned, try demoting the other server on the clone + self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (self.dc1, + self.tempdir)) + self.assertRaises(samba.tests.BlackboxProcessError, demote_self) + + # While we have this cloned, try demoting the other server on the clone + self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H ldb://%s/private/sam.ldb" + % (self.dc2, + self.tempdir)) + + # Check some of the objects that should have been removed + def check_machine_obj(): + samdb.searchone("CN", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + self.assertRaises(ldb.LdbError, check_machine_obj) + + def check_server_obj(): + samdb.searchone("CN", server_dn) + self.assertRaises(ldb.LdbError, check_server_obj) + + def check_ntds_guid(): + samdb.searchone("CN", "" % ntds_guid) + self.assertRaises(ldb.LdbError, check_ntds_guid) + + if dns_obj is not None: + # Check some of the objects that should have been removed + def check_dns_account_obj(): + samdb.search(base=dns_obj.dn, scope=ldb.SCOPE_BASE, + attrs=[]) + self.assertRaises(ldb.LdbError, check_dns_account_obj) + + def test_samba_tool_drs_clone_dc_secrets_without_targetdir(self): + """Tests 'samba-tool drs clone-dc-database' command without --targetdir.""" + server_rootdse = self._get_rootDSE(self.dc1) + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + + def attempt_clone(): + self.check_output("samba-tool drs clone-dc-database %s --server=%s %s" + % (server_realm, + self.dc1, + self.cmdline_creds)) + self.assertRaises(samba.tests.BlackboxProcessError, attempt_clone) diff --git a/source4/torture/drs/python/samba_tool_drs_critical.py b/source4/torture/drs/python/samba_tool_drs_critical.py new file mode 100644 index 0000000..5260e15 --- /dev/null +++ b/source4/torture/drs/python/samba_tool_drs_critical.py @@ -0,0 +1,98 @@ +# Blackbox tests for "samba-tool drs" command +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +"""Blackbox tests for samba-tool drs.""" + +import samba.tests +import os +import ldb +import drs_base +import random + +class SambaToolDrsTests(drs_base.DrsBaseTestCase): + """Blackbox test case for samba-tool drs.""" + + def setUp(self): + super(SambaToolDrsTests, self).setUp() + + self.dc1 = samba.tests.env_get_var_value("DC1") + self.dc2 = samba.tests.env_get_var_value("DC2") + + creds = self.get_credentials() + self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), + creds.get_username(), creds.get_password()) + + def tearDown(self): + self._enable_inbound_repl(self.dnsname_dc1) + self._enable_inbound_repl(self.dnsname_dc2) + + self.rm_files('names.tdb', allow_missing=True) + self.rm_dirs('etc', 'msg.lock', 'private', 'state', 'bind-dns', + allow_missing=True) + + super(SambaToolDrsTests, self).tearDown() + + # This test is for the Samba 4.5 emulation servers (but runs + # against a normal server as well) that fail to correctly + # implement DRSUAPI_DRS_GET_ANC when DRSUAPI_DRS_CRITICAL_ONLY is + # set. + def test_samba_tool_drs_clone_dc_critical_object_chain(self): + """Tests 'samba-tool drs clone-dc-database' command with a Critical/non-critical/critical object chain.""" + + samdb = samba.tests.connect_samdb(self.dc1, lp=self.get_loadparm(), + credentials=self.get_credentials(), + ldap_only=True) + server_rootdse = samdb.search(base="", + scope=samba.tests.ldb.SCOPE_BASE)[0] + nc_name = server_rootdse["defaultNamingContext"][0] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + + not_critical_dn = f"OU=not-critical{random.randint(1, 10000000)},{nc_name}" + samdb.create_ou(not_critical_dn) + self.addCleanup(samdb.delete, + not_critical_dn) + domain_sid = samdb.get_domain_sid() + admin_sid = f"{domain_sid}-500" + samdb.rename(f"", + f"cn=administrator,{not_critical_dn}") + self.addCleanup(samdb.rename, + f"", + f"cn=administrator,cn=users,{nc_name}") + + try: + self.check_output("samba-tool drs clone-dc-database %s --server=%s %s --targetdir=%s" + % (server_realm, + self.dc1, + self.cmdline_creds, + self.tempdir)) + except samba.tests.BlackboxProcessError as e: + self.fail("Error calling samba-tool: %s" % e) + + local_samdb = samba.tests.connect_samdb("ldb://" + os.path.join(self.tempdir, "private", "sam.ldb"), + ldap_only=False, lp=self.get_loadparm()) + + # Check administrator was replicated and is in the right place + res = local_samdb.search(base=str(nc_name), + expression="(&(objectclass=user)(cn=administrator))", + attrs=[], scope=ldb.SCOPE_SUBTREE) + self.assertEqual(len(res), 1) + + admin_obj = res[0] + + self.assertEqual(admin_obj.dn, ldb.Dn(samdb, f"cn=administrator,{not_critical_dn}")) diff --git a/source4/torture/drs/python/samba_tool_drs_no_dns.py b/source4/torture/drs/python/samba_tool_drs_no_dns.py new file mode 100644 index 0000000..aad5966 --- /dev/null +++ b/source4/torture/drs/python/samba_tool_drs_no_dns.py @@ -0,0 +1,174 @@ +# Blackbox tests for "samba-tool drs" command +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2017 +# Copyright (C) Catalyst.Net Ltd 2019 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Blackbox tests for samba-tool drs with no DNS partitions + +Adapted from samba_tool_drs.py +""" + +import samba.tests +import os +import ldb +import drs_base + +from samba.tests import BlackboxProcessError +from samba.common import get_string + + +class SambaToolDrsNoDnsTests(drs_base.DrsBaseTestCase): + """Blackbox test case for samba-tool drs.""" + + def setUp(self): + super(SambaToolDrsNoDnsTests, self).setUp() + + self.dc1 = samba.tests.env_get_var_value("DC1") + + creds = self.get_credentials() + self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), + creds.get_username(), creds.get_password()) + + def tearDown(self): + self._enable_inbound_repl(self.dnsname_dc1) + self.rm_files('names.tdb', allow_missing=True) + self.rm_dirs('etc', 'msg.lock', 'private', 'state', 'bind-dns', + allow_missing=True) + + super(SambaToolDrsNoDnsTests, self).tearDown() + + def _get_rootDSE(self, dc, ldap_only=True): + samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(), + credentials=self.get_credentials(), + ldap_only=ldap_only) + return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0], samdb + + def test_samba_tool_replicate_local_no_dns_tdb(self): + self.backend = 'tdb' + self._test_samba_tool_replicate_local_no_dns() + + def test_samba_tool_replicate_local_no_dns_mdb(self): + self.backend = 'mdb' + self._test_samba_tool_replicate_local_no_dns() + + def _test_samba_tool_replicate_local_no_dns(self): + """Check we can provision a database without DNS partitions + (and then add them afterwards).""" + + server_rootdse, _ = self._get_rootDSE(self.dc1) + nc_name = server_rootdse["defaultNamingContext"] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + + # We have to give it a different netbiosname every time + # it runs, otherwise the collision causes strange issues + # to happen. This should be different on different environments. + netbiosname = "dns" + self.backend + self.dc1 + if len(netbiosname) > 15: + netbiosname = netbiosname[:15] + + self.check_output("samba-tool domain join %s dc --server=%s %s --targetdir=%s --option=netbiosname=%s %s --backend-store=%s" + % (server_realm, self.dc1, self.cmdline_creds, + self.tempdir, netbiosname, + "--dns-backend=NONE", + self.backend)) + + new_dc_config_file = os.path.join(self.tempdir, "etc", "smb.conf") + new_dc_sam = os.path.join(self.tempdir, "private", "sam.ldb") + + forestdns_dn = ldb.binary_encode('DC=ForestDNSZones,' + str(nc_name)) + domaindns_dn = ldb.binary_encode('DC=DomainDNSZones,' + str(nc_name)) + + self.check_output("samba-tool drs replicate --local %s %s %s %s --configfile=%s --full-sync" + % ("invalid", self.dc1, forestdns_dn, + self.cmdline_creds, new_dc_config_file)) + + self.check_output("samba-tool drs replicate --local %s %s %s %s --configfile=%s --full-sync" + % ("invalid", self.dc1, domaindns_dn, + self.cmdline_creds, new_dc_config_file)) + + server_rootdse, samdb = self._get_rootDSE("ldb://" + new_dc_sam, ldap_only=False) + server_ds_name = ldb.binary_encode(server_rootdse["dsServiceName"][0].decode('utf-8')) + + # Show that Has-Master-NCs is fixed by samba_upgradedns + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % forestdns_dn) + self.assertEqual(len(res), 0) + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % domaindns_dn) + self.assertEqual(len(res), 0) + + self.check_output("samba_upgradedns --configfile=%s" % (new_dc_config_file)) + + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % forestdns_dn) + self.assertEqual(len(res), 1) + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % domaindns_dn) + self.assertEqual(len(res), 1) + + # Show that replica locations is fixed by dbcheck + res = samdb.search(controls=["search_options:1:2"], + expression="(&(msds-nc-replica-locations=%s)(ncname=%s))" + % (server_ds_name, forestdns_dn)) + self.assertEqual(len(res), 0) + res = samdb.search(controls=["search_options:1:2"], + expression="(&(msds-nc-replica-locations=%s)(ncname=%s))" + % (server_ds_name, domaindns_dn)) + self.assertEqual(len(res), 0) + + try: + # This fixes any forward-link-backward-link issues with the tools + self.check_output("samba-tool dbcheck --configfile=%s --cross-ncs --fix --yes" % (new_dc_config_file)) + except BlackboxProcessError as e: + self.assertTrue("Checked " in get_string(e.stdout)) + + self.check_output("samba-tool dbcheck --configfile=%s --cross-ncs" % (new_dc_config_file)) + + # Compare the two directories + self.check_output("samba-tool ldapcmp ldap://%s ldb://%s %s --filter=%s" % + (self.dc1, new_dc_sam, self.cmdline_creds, + "msDs-masteredBy,msDS-NC-Replica-Locations,msDS-hasMasterNCs")) + + # Check all ForestDNS connections and backlinks + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % forestdns_dn) + self.assertEqual(len(res), 1) + res = samdb.search(base=forestdns_dn, + expression="(msds-masteredby=%s)" % server_ds_name) + self.assertEqual(len(res), 1) + res = samdb.search(controls=["search_options:1:2"], + expression="(&(msds-nc-replica-locations=%s)(ncname=%s))" + % (server_ds_name, forestdns_dn)) + self.assertEqual(len(res), 1) + + # Check all DomainDNS connections and backlinks + res = samdb.search(base=server_ds_name, + expression="(msds-hasmasterncs=%s)" % domaindns_dn) + self.assertEqual(len(res), 1) + res = samdb.search(base=domaindns_dn, + expression="(msds-masteredby=%s)" % server_ds_name) + self.assertEqual(len(res), 1) + res = samdb.search(controls=["search_options:1:2"], + expression="(&(msds-nc-replica-locations=%s)(ncname=%s))" + % (server_ds_name, domaindns_dn)) + self.assertEqual(len(res), 1) + + # Demote the DC we created in the test + self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H ldap://%s %s --configfile=%s" + % (netbiosname, self.dc1, self.cmdline_creds, new_dc_config_file)) diff --git a/source4/torture/drs/python/samba_tool_drs_showrepl.py b/source4/torture/drs/python/samba_tool_drs_showrepl.py new file mode 100644 index 0000000..0f0ed86 --- /dev/null +++ b/source4/torture/drs/python/samba_tool_drs_showrepl.py @@ -0,0 +1,377 @@ +# Blackbox tests for "samba-tool drs" command +# Copyright (C) Kamen Mazdrashki 2011 +# Copyright (C) Andrew Bartlett 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +"""Blackbox tests for samba-tool drs showrepl.""" +import samba.tests +import drs_base +from samba.dcerpc import drsuapi +from samba import drs_utils +import os +import json +import ldb +import random +from samba.common import get_string + +GUID_RE = r'[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}' +HEX8_RE = r'0x[\da-f]{8}' +DN_RE = r'(?:(?:CN|DC)=[\\:\w -]+,)+DC=com' + + +class SambaToolDrsShowReplTests(drs_base.DrsBaseTestCase): + """Blackbox test case for samba-tool drs.""" + + def setUp(self): + super(SambaToolDrsShowReplTests, self).setUp() + + self.dc1 = samba.tests.env_get_var_value("DC1") + self.dc2 = samba.tests.env_get_var_value("DC2") + + creds = self.get_credentials() + self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), + creds.get_username(), + creds.get_password()) + + def test_samba_tool_showrepl(self): + """Tests 'samba-tool drs showrepl' command. + """ + nc_list = [self.config_dn, self.domain_dn, self.schema_dn] + dns_name = self.ldb_dc1.domain_dns_name() + + # Manually run kcc to create a "Connection" object, so we can find + # this for the expected output below. + kcc_out = self.check_output("samba-tool drs kcc %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertIn(b"successful", kcc_out) + + # Run replicate to ensure there are incoming and outgoing partners + # exist, so we can expect these in the output below. + for nc in nc_list: + self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=nc, forced=True) + self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, nc_dn=nc, forced=True) + + # Output should be like: + # / + # DSA Options: + # DSA object GUID: + # DSA invocationId: + # + # + # + # ... + # TODO: Perhaps we should check at least for + # DSA's objectGUDI and invocationId + out = self.check_output("samba-tool drs showrepl " + "%s %s" % (self.dc1, self.cmdline_creds)) + + out = get_string(out) + # We want to assert that we are getting the same results, but + # dates and GUIDs change randomly. + # + # There are sections with headers like ==== THIS ====" + (header, + _inbound, inbound, + _outbound, outbound, + _conn, conn) = out.split("====") + + self.assertEqual(_inbound, ' INBOUND NEIGHBORS ') + self.assertEqual(_outbound, ' OUTBOUND NEIGHBORS ') + self.assertEqual(_conn, ' KCC CONNECTION OBJECTS ') + + self.assertRegex(header, + r'^Default-First-Site-Name\\%s\s+' + r"DSA Options: %s\s+" + r"DSA object GUID: %s\s+" + r"DSA invocationId: %s" % + (self.dc1.upper(), HEX8_RE, GUID_RE, GUID_RE)) + + # We don't assert the DomainDnsZones and ForestDnsZones are + # there because we don't know that they have been set up yet. + + for p in nc_list: + self.assertRegex( + inbound, + r'%s\n' + r'\tDefault-First-Site-Name\\[A-Z0-9]+ via RPC\n' + r'\t\tDSA object GUID: %s\n' + r'\t\tLast attempt @ [^\n]+\n' + r'\t\t\d+ consecutive failure\(s\).\n' + r'\t\tLast success @ [^\n]+\n' + r'\n' % (p, GUID_RE), + msg="%s inbound missing" % p) + + self.assertRegex( + outbound, + r'%s\n' + r'\tDefault-First-Site-Name\\[A-Z0-9]+ via RPC\n' + r'\t\tDSA object GUID: %s\n' + r'\t\tLast attempt @ [^\n]+\n' + r'\t\t\d+ consecutive failure\(s\).\n' + r'\t\tLast success @ [^\n]+\n' + r'\n' % (p, GUID_RE), + msg="%s outbound missing" % p) + + self.assertRegex(conn, + r'Connection --\n' + r'\tConnection name: %s\n' + r'\tEnabled : TRUE\n' + r'\tServer DNS name : \w+.%s\n' + r'\tServer DN name : %s' + r'\n' % (GUID_RE, dns_name, DN_RE)) + + def test_samba_tool_showrepl_json(self): + """Tests 'samba-tool drs showrepl --json' command. + """ + dns_name = self.ldb_dc1.domain_dns_name() + out = self.check_output("samba-tool drs showrepl %s %s --json" % + (self.dc1, self.cmdline_creds)) + d = json.loads(get_string(out)) + self.assertEqual(set(d), set(['repsFrom', + 'repsTo', + "NTDSConnections", + "dsa"])) + + # dsa + for k in ["objectGUID", "invocationId"]: + self.assertRegex(d['dsa'][k], '^%s$' % GUID_RE) + self.assertTrue(isinstance(d['dsa']["options"], int)) + + # repsfrom and repsto + for reps in (d['repsFrom'], d['repsTo']): + for r in reps: + for k in ('NC dn', "NTDS DN"): + self.assertRegex(r[k], '^%s$' % DN_RE) + for k in ("last attempt time", + "last attempt message", + "last success"): + self.assertTrue(isinstance(r[k], str)) + self.assertRegex(r["DSA objectGUID"], '^%s$' % GUID_RE) + self.assertTrue(isinstance(r["consecutive failures"], int)) + + # ntdsconnection + for n in d["NTDSConnections"]: + self.assertTrue(n["dns name"].endswith(dns_name)) + self.assertRegex(n["name"], "^%s$" % GUID_RE) + self.assertTrue(isinstance(n['enabled'], bool)) + self.assertTrue(isinstance(n['options'], int)) + self.assertTrue(isinstance(n['replicates NC'], list)) + self.assertRegex(n["remote DN"], "^%s$" % DN_RE) + + def _force_all_reps(self, samdb, dc, direction): + if direction == 'inbound': + info_type = drsuapi.DRSUAPI_DS_REPLICA_INFO_NEIGHBORS + elif direction == 'outbound': + info_type = drsuapi.DRSUAPI_DS_REPLICA_INFO_REPSTO + else: + raise ValueError("expected 'inbound' or 'outbound'") + + self._enable_all_repl(dc) + lp = self.get_loadparm() + creds = self.get_credentials() + drsuapi_conn, drsuapi_handle, _ = drs_utils.drsuapi_connect(dc, lp, creds) + req1 = drsuapi.DsReplicaGetInfoRequest1() + req1.info_type = info_type + _, info = drsuapi_conn.DsReplicaGetInfo(drsuapi_handle, 1, req1) + for x in info.array: + # you might think x.source_dsa_address was the thing, but no. + # and we need to filter out RODCs and deleted DCs + + res = [] + try: + res = samdb.search(base=x.source_dsa_obj_dn, + scope=ldb.SCOPE_BASE, + attrs=['msDS-isRODC', 'isDeleted'], + controls=['show_deleted:0']) + except ldb.LdbError as e: + if e.args[0] != ldb.ERR_NO_SUCH_OBJECT: + raise + + if (len(res) == 0 or + len(res[0].get('msDS-isRODC', '')) > 0 or + res[0]['isDeleted'] == 'TRUE'): + continue + + dsa_dn = str(ldb.Dn(samdb, x.source_dsa_obj_dn).parent()) + try: + res = samdb.search(base=dsa_dn, + scope=ldb.SCOPE_BASE, + attrs=['dNSHostName']) + except ldb.LdbError as e: + if e.args[0] != ldb.ERR_NO_SUCH_OBJECT: + raise + continue + + if len(res) == 0: + print("server %s has no dNSHostName" % dsa_dn) + continue + + remote = res[0].get('dNSHostName', [''])[0] + if remote: + self._enable_all_repl(remote) + + if direction == 'inbound': + src, dest = remote, dc + else: + src, dest = dc, remote + self._net_drs_replicate(dest, src, forced=True) + + def test_samba_tool_showrepl_pull_summary_all_good(self): + """Tests 'samba-tool drs showrepl --pull-summary' command.""" + # To be sure that all is good we need to force replication + # with everyone (because others might have it turned off), and + # turn replication on for them in case they suddenly decide to + # try again. + # + # We don't restore them to the non-auto-replication state. + samdb1 = self.getSamDB("-H", "ldap://%s" % self.dc1, + self.cmdline_creds) + self._enable_all_repl(self.dc1) + self._force_all_reps(samdb1, self.dc1, 'inbound') + self._force_all_reps(samdb1, self.dc1, 'outbound') + old_no_color = os.environ.get('NO_COLOR') + all_good_green = "\033[1;32m[ALL GOOD]\033[0m\n" + all_good = "[ALL GOOD]\n" + + try: + out = self.check_output( + "samba-tool drs showrepl --pull-summary %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good) + out = get_string(out) + + out = self.check_output("samba-tool drs showrepl --pull-summary " + "--color=yes %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good_green) + + # --verbose output is still quiet when all is good. + out = self.check_output( + "samba-tool drs showrepl --pull-summary -v %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good) + + out = self.check_output("samba-tool drs showrepl --pull-summary -v " + "--color=always %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good_green) + + out = self.check_output("samba-tool drs showrepl --pull-summary -v " + "--color=never %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good) + + os.environ['NO_COLOR'] = 'bean' + + out = self.check_output("samba-tool drs showrepl --pull-summary -v " + "--color=auto %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good) + + os.environ['NO_COLOR'] = '' + + out = self.check_output("samba-tool drs showrepl --pull-summary -v " + "--color=auto %s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual(out, all_good_green) + + except samba.tests.BlackboxProcessError as e: + self.fail(str(e)) + finally: + if old_no_color is None: + os.environ.pop('NO_COLOR', None) + else: + os.environ['NO_COLOR'] = old_no_color + + def test_samba_tool_showrepl_summary_forced_failure(self): + """Tests 'samba-tool drs showrepl --summary' command when we break the + network on purpose. + """ + self.addCleanup(self._enable_all_repl, self.dc1) + self._disable_all_repl(self.dc1) + + samdb1 = self.getSamDB("-H", "ldap://%s" % self.dc1, + self.cmdline_creds) + samdb2 = self.getSamDB("-H", "ldap://%s" % self.dc2, + self.cmdline_creds) + domain_dn = samdb1.domain_dn() + + # Add some things to NOT replicate + ou1 = "OU=dc1.%x,%s" % (random.randrange(1 << 64), domain_dn) + ou2 = "OU=dc2.%x,%s" % (random.randrange(1 << 64), domain_dn) + samdb1.add({ + "dn": ou1, + "objectclass": "organizationalUnit" + }) + self.addCleanup(samdb1.delete, ou1, ['tree_delete:1']) + samdb2.add({ + "dn": ou2, + "objectclass": "organizationalUnit" + }) + self.addCleanup(samdb2.delete, ou2, ['tree_delete:1']) + + dn1 = 'cn=u1.%%d,%s' % (ou1) + dn2 = 'cn=u2.%%d,%s' % (ou2) + + try: + for i in range(100): + samdb1.add({ + "dn": dn1 % i, + "objectclass": "user" + }) + samdb2.add({ + "dn": dn2 % i, + "objectclass": "user" + }) + out = self.check_output("samba-tool drs showrepl --summary -v " + "%s %s" % + (self.dc1, self.cmdline_creds)) + out = get_string(out) + self.assertStringsEqual('[ALL GOOD]', out, strip=True) + out = self.check_output("samba-tool drs showrepl --summary -v " + "--color=yes %s %s" % + (self.dc2, self.cmdline_creds)) + out = get_string(out) + self.assertIn('[ALL GOOD]', out) + + except samba.tests.BlackboxProcessError as e: + e_stdout = get_string(e.stdout) + e_stderr = get_string(e.stderr) + print("Good, failed as expected after %d rounds: %r" % (i, e.cmd)) + self.assertIn('There are failing connections', e_stdout, + msg=('stdout: %r\nstderr: %r\nretcode: %s' + '\nmessage: %r\ncmd: %r') % (e_stdout, + e_stderr, + e.returncode, + e.msg, + e.cmd)) + self.assertRegex( + e_stdout, + r'result 845[67] ' + r'\(WERR_DS_DRA_(SINK|SOURCE)_DISABLED\)', + msg=("The process should have failed " + "because replication was forced off, " + "but it failed for some other reason.")) + self.assertIn('consecutive failure(s).', e_stdout) + else: + self.fail("No DRS failure noticed after 100 rounds of trying") diff --git a/source4/torture/drs/rpc/dssync.c b/source4/torture/drs/rpc/dssync.c new file mode 100644 index 0000000..64d0498 --- /dev/null +++ b/source4/torture/drs/rpc/dssync.c @@ -0,0 +1,1072 @@ +/* + Unix SMB/CIFS implementation. + + DsGetNCChanges replication test + + Copyright (C) Stefan (metze) Metzmacher 2005 + Copyright (C) Brad Henry 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "libcli/cldap/cldap.h" +#include "torture/torture.h" +#include "../libcli/drsuapi/drsuapi.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "dsdb/samdb/samdb.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/drs/proto.h" +#include "lib/tsocket/tsocket.h" +#include "libcli/resolve/resolve.h" +#include "lib/util/util_paths.h" + +#undef strcasecmp + +struct DsSyncBindInfo { + struct dcerpc_pipe *drs_pipe; + struct dcerpc_binding_handle *drs_handle; + struct drsuapi_DsBind req; + struct GUID bind_guid; + struct drsuapi_DsBindInfoCtr our_bind_info_ctr; + struct drsuapi_DsBindInfo28 our_bind_info28; + struct drsuapi_DsBindInfo28 peer_bind_info28; + struct policy_handle bind_handle; +}; + +struct DsSyncLDAPInfo { + struct ldb_context *ldb; +}; + +struct DsSyncTest { + struct dcerpc_binding *drsuapi_binding; + + const char *ldap_url; + const char *dest_address; + const char *domain_dn; + const char *config_dn; + const char *schema_dn; + + /* what we need to do as 'Administrator' */ + struct { + struct cli_credentials *credentials; + struct DsSyncBindInfo drsuapi; + struct DsSyncLDAPInfo ldap; + } admin; + + /* what we need to do as the new dc machine account */ + struct { + struct cli_credentials *credentials; + struct DsSyncBindInfo drsuapi; + struct drsuapi_DsGetDCInfo2 dc_info2; + struct GUID invocation_id; + struct GUID object_guid; + } new_dc; + + /* info about the old dc */ + struct { + struct drsuapi_DsGetDomainControllerInfo dc_info; + } old_dc; +}; + +static struct DsSyncTest *test_create_context(struct torture_context *tctx) +{ + NTSTATUS status; + struct DsSyncTest *ctx; + struct drsuapi_DsBindInfo28 *our_bind_info28; + struct drsuapi_DsBindInfoCtr *our_bind_info_ctr; + const char *binding = torture_setting_string(tctx, "binding", NULL); + const char *host; + struct nbt_name name; + + ctx = talloc_zero(tctx, struct DsSyncTest); + if (!ctx) return NULL; + + status = dcerpc_parse_binding(ctx, binding, &ctx->drsuapi_binding); + if (!NT_STATUS_IS_OK(status)) { + printf("Bad binding string %s\n", binding); + return NULL; + } + status = dcerpc_binding_set_flags(ctx->drsuapi_binding, + DCERPC_SIGN | DCERPC_SEAL, 0); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_binding_set_flags - %s\n", nt_errstr(status)); + return NULL; + } + + host = dcerpc_binding_get_string_option(ctx->drsuapi_binding, "host"); + + ctx->ldap_url = talloc_asprintf(ctx, "ldap://%s", host); + + make_nbt_name_server(&name, host); + + /* do an initial name resolution to find its IP */ + status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, &name, tctx, + &ctx->dest_address, tctx->ev); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to resolve %s - %s\n", + name.name, nt_errstr(status)); + return NULL; + } + + /* ctx->admin ...*/ + ctx->admin.credentials = samba_cmdline_get_creds(); + + our_bind_info28 = &ctx->admin.drsuapi.our_bind_info28; + our_bind_info28->supported_extensions = 0xFFFFFFFF; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + our_bind_info28->site_guid = GUID_zero(); + our_bind_info28->pid = 0; + our_bind_info28->repl_epoch = 1; + + our_bind_info_ctr = &ctx->admin.drsuapi.our_bind_info_ctr; + our_bind_info_ctr->length = 28; + our_bind_info_ctr->info.info28 = *our_bind_info28; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &ctx->admin.drsuapi.bind_guid); + + ctx->admin.drsuapi.req.in.bind_guid = &ctx->admin.drsuapi.bind_guid; + ctx->admin.drsuapi.req.in.bind_info = our_bind_info_ctr; + ctx->admin.drsuapi.req.out.bind_handle = &ctx->admin.drsuapi.bind_handle; + + /* ctx->new_dc ...*/ + ctx->new_dc.credentials = samba_cmdline_get_creds(); + + our_bind_info28 = &ctx->new_dc.drsuapi.our_bind_info28; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "xpress", false)) { + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS; + } + our_bind_info28->site_guid = GUID_zero(); + our_bind_info28->pid = 0; + our_bind_info28->repl_epoch = 0; + + our_bind_info_ctr = &ctx->new_dc.drsuapi.our_bind_info_ctr; + our_bind_info_ctr->length = 28; + our_bind_info_ctr->info.info28 = *our_bind_info28; + + GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &ctx->new_dc.drsuapi.bind_guid); + + ctx->new_dc.drsuapi.req.in.bind_guid = &ctx->new_dc.drsuapi.bind_guid; + ctx->new_dc.drsuapi.req.in.bind_info = our_bind_info_ctr; + ctx->new_dc.drsuapi.req.out.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + + ctx->new_dc.invocation_id = ctx->new_dc.drsuapi.bind_guid; + + /* ctx->old_dc ...*/ + + return ctx; +} + +static bool _test_DsBind(struct torture_context *tctx, + struct DsSyncTest *ctx, struct cli_credentials *credentials, struct DsSyncBindInfo *b) +{ + NTSTATUS status; + bool ret = true; + + status = dcerpc_pipe_connect_b(ctx, + &b->drs_pipe, ctx->drsuapi_binding, + &ndr_table_drsuapi, + credentials, tctx->ev, tctx->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status)); + return false; + } + b->drs_handle = b->drs_pipe->binding_handle; + + status = dcerpc_drsuapi_DsBind_r(b->drs_handle, ctx, &b->req); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(b->req.out.result)) { + printf("DsBind failed - %s\n", win_errstr(b->req.out.result)); + ret = false; + } + + ZERO_STRUCT(b->peer_bind_info28); + if (b->req.out.bind_info) { + switch (b->req.out.bind_info->length) { + case 24: { + struct drsuapi_DsBindInfo24 *info24; + info24 = &b->req.out.bind_info->info.info24; + b->peer_bind_info28.supported_extensions= info24->supported_extensions; + b->peer_bind_info28.site_guid = info24->site_guid; + b->peer_bind_info28.pid = info24->pid; + b->peer_bind_info28.repl_epoch = 0; + break; + } + case 28: { + b->peer_bind_info28 = b->req.out.bind_info->info.info28; + break; + } + case 32: { + struct drsuapi_DsBindInfo32 *info32; + info32 = &b->req.out.bind_info->info.info32; + b->peer_bind_info28.supported_extensions= info32->supported_extensions; + b->peer_bind_info28.site_guid = info32->site_guid; + b->peer_bind_info28.pid = info32->pid; + b->peer_bind_info28.repl_epoch = info32->repl_epoch; + break; + } + case 48: { + struct drsuapi_DsBindInfo48 *info48; + info48 = &b->req.out.bind_info->info.info48; + b->peer_bind_info28.supported_extensions= info48->supported_extensions; + b->peer_bind_info28.site_guid = info48->site_guid; + b->peer_bind_info28.pid = info48->pid; + b->peer_bind_info28.repl_epoch = info48->repl_epoch; + break; + } + case 52: { + struct drsuapi_DsBindInfo52 *info52; + info52 = &b->req.out.bind_info->info.info52; + b->peer_bind_info28.supported_extensions= info52->supported_extensions; + b->peer_bind_info28.site_guid = info52->site_guid; + b->peer_bind_info28.pid = info52->pid; + b->peer_bind_info28.repl_epoch = info52->repl_epoch; + break; + } + default: + printf("DsBind - warning: unknown BindInfo length: %u\n", + b->req.out.bind_info->length); + } + } + + return ret; +} + +static bool test_LDAPBind(struct torture_context *tctx, struct DsSyncTest *ctx, + struct cli_credentials *credentials, struct DsSyncLDAPInfo *l) +{ + bool ret = true; + + struct ldb_context *ldb; + + const char *modules_option[] = { "modules:paged_searches", NULL }; + ctx->admin.ldap.ldb = ldb = ldb_init(ctx, tctx->ev); + if (ldb == NULL) { + return false; + } + + /* Despite us loading the schema from the AD server, we need + * the samba handlers to get the extended DN syntax stuff */ + ret = ldb_register_samba_handlers(ldb); + if (ret != LDB_SUCCESS) { + talloc_free(ldb); + return NULL; + } + + ldb_set_modules_dir(ldb, modules_path(ldb, "ldb")); + + if (ldb_set_opaque(ldb, "credentials", credentials)) { + talloc_free(ldb); + return NULL; + } + + if (ldb_set_opaque(ldb, "loadparm", tctx->lp_ctx)) { + talloc_free(ldb); + return NULL; + } + + ret = ldb_connect(ldb, ctx->ldap_url, 0, modules_option); + if (ret != LDB_SUCCESS) { + talloc_free(ldb); + torture_assert_int_equal(tctx, ret, LDB_SUCCESS, "Failed to make LDB connection to target"); + } + + printf("connected to LDAP: %s\n", ctx->ldap_url); + + return true; +} + +static bool test_GetInfo(struct torture_context *tctx, struct DsSyncTest *ctx) +{ + struct ldb_context *ldb = ctx->admin.ldap.ldb; + + /* We must have LDB connection ready by this time */ + SMB_ASSERT(ldb != NULL); + + ctx->domain_dn = ldb_dn_get_linearized(ldb_get_default_basedn(ldb)); + torture_assert(tctx, ctx->domain_dn != NULL, "Failed to get Domain DN"); + + ctx->config_dn = ldb_dn_get_linearized(ldb_get_config_basedn(ldb)); + torture_assert(tctx, ctx->config_dn != NULL, "Failed to get Domain DN"); + + ctx->schema_dn = ldb_dn_get_linearized(ldb_get_schema_basedn(ldb)); + torture_assert(tctx, ctx->schema_dn != NULL, "Failed to get Domain DN"); + + return true; +} + +static bool test_analyse_objects(struct torture_context *tctx, + struct DsSyncTest *ctx, + const char *partition, + const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr, + uint32_t object_count, + const struct drsuapi_DsReplicaObjectListItemEx *first_object, + const DATA_BLOB *gensec_skey) +{ + static uint32_t object_id; + const char *save_values_dir; + const struct drsuapi_DsReplicaObjectListItemEx *cur; + struct ldb_context *ldb = ctx->admin.ldap.ldb; + struct ldb_dn *deleted_dn; + WERROR status; + int i, j, ret; + struct dsdb_extended_replicated_objects *objs; + struct ldb_extended_dn_control *extended_dn_ctrl; + struct dsdb_schema *ldap_schema; + struct ldb_dn *partition_dn = ldb_dn_new(tctx, ldb, partition); + + torture_assert_not_null(tctx, partition_dn, "Failed to parse partition DN as as DN"); + + /* load dsdb_schema using remote prefixMap */ + torture_assert(tctx, + drs_util_dsdb_schema_load_ldb(tctx, ldb, mapping_ctr, false), + "drs_util_dsdb_schema_load_ldb() failed"); + ldap_schema = dsdb_get_schema(ldb, NULL); + + status = dsdb_replicated_objects_convert(ldb, + ldap_schema, + partition_dn, + mapping_ctr, + object_count, + first_object, + 0, NULL, + NULL, NULL, + gensec_skey, + 0, + ctx, &objs); + torture_assert_werr_ok(tctx, status, "dsdb_extended_replicated_objects_convert() failed!"); + + extended_dn_ctrl = talloc(objs, struct ldb_extended_dn_control); + extended_dn_ctrl->type = 1; + + deleted_dn = ldb_dn_new(objs, ldb, partition); + ldb_dn_add_child_fmt(deleted_dn, "CN=Deleted Objects"); + + for (i=0; i < objs->num_objects; i++) { + struct ldb_request *search_req; + struct ldb_result *res; + struct ldb_message *new_msg, *drs_msg, *ldap_msg; + size_t num_attrs = objs->objects[i].msg->num_elements+1; + const char **attrs = talloc_array(objs, const char *, num_attrs); + for (j=0; j < objs->objects[i].msg->num_elements; j++) { + attrs[j] = objs->objects[i].msg->elements[j].name; + } + attrs[j] = NULL; + res = talloc_zero(objs, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + ret = ldb_build_search_req(&search_req, ldb, objs, + objs->objects[i].msg->dn, + LDB_SCOPE_BASE, + NULL, + attrs, + NULL, + res, + ldb_search_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + return false; + } + talloc_steal(search_req, res); + ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL); + if (ret != LDB_SUCCESS) { + return false; + } + + ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, true, extended_dn_ctrl); + if (ret != LDB_SUCCESS) { + return false; + } + + ret = ldb_request(ldb, search_req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(search_req->handle, LDB_WAIT_ALL); + } + + torture_assert_int_equal(tctx, ret, LDB_SUCCESS, + talloc_asprintf(tctx, + "Could not re-fetch object just delivered over DRS: %s", + ldb_errstring(ldb))); + torture_assert_int_equal(tctx, res->count, 1, "Could not re-fetch object just delivered over DRS"); + ldap_msg = res->msgs[0]; + for (j=0; j < ldap_msg->num_elements; j++) { + ldap_msg->elements[j].flags = LDB_FLAG_MOD_ADD; + /* For unknown reasons, there is no nTSecurityDescriptor on cn=deleted objects over LDAP, but there is over DRS! Skip it on both transports for now here so */ + if ((ldb_attr_cmp(ldap_msg->elements[j].name, "nTSecurityDescriptor") == 0) && + (ldb_dn_compare(ldap_msg->dn, deleted_dn) == 0)) { + ldb_msg_remove_element(ldap_msg, &ldap_msg->elements[j]); + /* Don't skip one */ + j--; + } + } + + ret = ldb_msg_normalize(ldb, search_req, + objs->objects[i].msg, &drs_msg); + torture_assert(tctx, ret == LDB_SUCCESS, + "ldb_msg_normalize() has failed"); + + for (j=0; j < drs_msg->num_elements; j++) { + if (drs_msg->elements[j].num_values == 0) { + ldb_msg_remove_element(drs_msg, &drs_msg->elements[j]); + /* Don't skip one */ + j--; + + /* For unknown reasons, there is no nTSecurityDescriptor on cn=deleted objects over LDAP, but there is over DRS! */ + } else if ((ldb_attr_cmp(drs_msg->elements[j].name, "nTSecurityDescriptor") == 0) && + (ldb_dn_compare(drs_msg->dn, deleted_dn) == 0)) { + ldb_msg_remove_element(drs_msg, &drs_msg->elements[j]); + /* Don't skip one */ + j--; + } else if (ldb_attr_cmp(drs_msg->elements[j].name, "unicodePwd") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "dBCSPwd") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "userPassword") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "ntPwdHistory") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "lmPwdHistory") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "supplementalCredentials") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "priorValue") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "currentValue") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "trustAuthOutgoing") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "trustAuthIncoming") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "initialAuthOutgoing") == 0 || + ldb_attr_cmp(drs_msg->elements[j].name, "initialAuthIncoming") == 0) { + + /* These are not shown over LDAP, so we need to skip them for the comparison */ + ldb_msg_remove_element(drs_msg, &drs_msg->elements[j]); + /* Don't skip one */ + j--; + } else { + drs_msg->elements[j].flags = LDB_FLAG_MOD_ADD; + } + } + + + ret = ldb_msg_difference(ldb, search_req, + drs_msg, ldap_msg, &new_msg); + torture_assert(tctx, ret == LDB_SUCCESS, "ldb_msg_difference() has failed"); + if (new_msg->num_elements != 0) { + char *s; + bool is_warning = true; + unsigned int idx; + struct ldb_message_element *el; + const struct dsdb_attribute * a; + struct ldb_ldif ldif; + ldif.changetype = LDB_CHANGETYPE_MODIFY; + ldif.msg = new_msg; + s = ldb_ldif_write_string(ldb, new_msg, &ldif); + s = talloc_asprintf(tctx, "\n# Difference in between DRS and LDAP objects: \n%s", s); + + ret = ldb_msg_difference(ldb, search_req, + ldap_msg, drs_msg, &ldif.msg); + torture_assert(tctx, ret == LDB_SUCCESS, "ldb_msg_difference() has failed"); + s = talloc_asprintf_append(s, + "\n# Difference in between LDAP and DRS objects: \n%s", + ldb_ldif_write_string(ldb, new_msg, &ldif)); + + s = talloc_asprintf_append(s, + "# Should have no objects in 'difference' message. Diff elements: %d", + new_msg->num_elements); + + /* + * In case differences in messages are: + * 1. Attributes with different values, i.e. 'replace' + * 2. Those attributes are forward-link attributes + * then we just warn about those differences. + * It turns out windows doesn't send all of those values + * in replicated_object but in linked_attributes. + */ + for (idx = 0; idx < new_msg->num_elements && is_warning; idx++) { + el = &new_msg->elements[idx]; + a = dsdb_attribute_by_lDAPDisplayName(ldap_schema, + el->name); + if (LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_ADD && + LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_REPLACE) + { + /* DRS only value */ + is_warning = false; + } else if (a->linkID & 1) { + is_warning = false; + } + } + if (is_warning) { + torture_warning(tctx, "%s", s); + } else { + torture_fail(tctx, s); + } + } + + /* search_req is used as a tmp talloc context in the above */ + talloc_free(search_req); + } + + if (!lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) { + talloc_free(objs); + return true; + } + + save_values_dir = lpcfg_parm_string(tctx->lp_ctx, NULL, "dssync", "save_pwd_blobs_dir"); + + for (cur = first_object; cur; cur = cur->next_object) { + const char *dn; + bool dn_printed = false; + + if (!cur->object.identifier) continue; + + dn = cur->object.identifier->dn; + + for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) { + const char *name = NULL; + DATA_BLOB plain_data; + struct drsuapi_DsReplicaAttribute *attr; + ndr_pull_flags_fn_t pull_fn = NULL; + ndr_print_fn_t print_fn = NULL; + void *ptr = NULL; + attr = &cur->object.attribute_ctr.attributes[i]; + + switch (attr->attid) { + case DRSUAPI_ATTID_dBCSPwd: + name = "dBCSPwd"; + break; + case DRSUAPI_ATTID_unicodePwd: + name = "unicodePwd"; + break; + case DRSUAPI_ATTID_ntPwdHistory: + name = "ntPwdHistory"; + break; + case DRSUAPI_ATTID_lmPwdHistory: + name = "lmPwdHistory"; + break; + case DRSUAPI_ATTID_supplementalCredentials: + name = "supplementalCredentials"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob; + print_fn = (ndr_print_fn_t)ndr_print_supplementalCredentialsBlob; + ptr = talloc(ctx, struct supplementalCredentialsBlob); + break; + case DRSUAPI_ATTID_priorValue: + name = "priorValue"; + break; + case DRSUAPI_ATTID_currentValue: + name = "currentValue"; + break; + case DRSUAPI_ATTID_trustAuthOutgoing: + name = "trustAuthOutgoing"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob; + print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob; + ptr = talloc(ctx, struct trustAuthInOutBlob); + break; + case DRSUAPI_ATTID_trustAuthIncoming: + name = "trustAuthIncoming"; + pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob; + print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob; + ptr = talloc(ctx, struct trustAuthInOutBlob); + break; + case DRSUAPI_ATTID_initialAuthOutgoing: + name = "initialAuthOutgoing"; + break; + case DRSUAPI_ATTID_initialAuthIncoming: + name = "initialAuthIncoming"; + break; + default: + continue; + } + + if (attr->value_ctr.num_values != 1) continue; + + if (!attr->value_ctr.values[0].blob) continue; + + plain_data = *attr->value_ctr.values[0].blob; + + if (!dn_printed) { + object_id++; + DEBUG(0,("DN[%u] %s\n", object_id, dn)); + dn_printed = true; + } + DEBUGADD(0,("ATTR: %s plain.length=%lu\n", + name, (long)plain_data.length)); + if (plain_data.length) { + enum ndr_err_code ndr_err; + dump_data(0, plain_data.data, plain_data.length); + if (save_values_dir) { + char *fname; + fname = talloc_asprintf(ctx, "%s/%s%02d", + save_values_dir, + name, object_id); + if (fname) { + bool ok; + ok = file_save(fname, plain_data.data, plain_data.length); + if (!ok) { + DEBUGADD(0,("Failed to save '%s'\n", fname)); + } + } + talloc_free(fname); + } + + if (pull_fn) { + /* Can't use '_all' because of PIDL bugs with relative pointers */ + ndr_err = ndr_pull_struct_blob(&plain_data, ptr, + ptr, pull_fn); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + (void)ndr_print_debug(1, print_fn, name, ptr, __location__, __func__); + } else { + DEBUG(0, ("Failed to decode %s\n", name)); + } + } + } + talloc_free(ptr); + } + } + talloc_free(objs); + return true; +} + +static bool test_GetNCChanges(struct torture_context *tctx, + struct DsSyncTest *ctx, + const char *nc_dn_str) +{ + NTSTATUS status; + bool ret = true; + int i, y = 0; + uint64_t highest_usn = 0; + struct drsuapi_DsGetNCChanges r; + union drsuapi_DsGetNCChangesRequest req; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL; + struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; + uint32_t out_level = 0; + struct dom_sid null_sid; + DATA_BLOB gensec_skey; + struct { + uint32_t level; + } array[] = { +/* { + 5 + }, +*/ { + 8 + } + }; + + ZERO_STRUCT(null_sid); + + highest_usn = lpcfg_parm_int(tctx->lp_ctx, NULL, "dssync", "highest_usn", 0); + + array[0].level = lpcfg_parm_int(tctx->lp_ctx, NULL, "dssync", "get_nc_changes_level", array[0].level); + + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) { + const struct samr_Password *nthash; + nthash = cli_credentials_get_nt_hash(ctx->new_dc.credentials, ctx); + if (nthash) { + dump_data_pw("CREDENTIALS nthash:", nthash->hash, sizeof(nthash->hash)); + } + } + status = gensec_session_key(ctx->new_dc.drsuapi.drs_pipe->conn->security_state.generic_state, + ctx, &gensec_skey); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to get gensec session key: %s\n", nt_errstr(status)); + return false; + } + + for (i=0; i < ARRAY_SIZE(array); i++) { + printf("Testing DsGetNCChanges level %d\n", + array[i].level); + + r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + r.in.level = array[i].level; + + switch (r.in.level) { + case 5: + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = nc_dn_str; + + r.in.req = &req; + r.in.req->req5.destination_dsa_guid = ctx->new_dc.invocation_id; + r.in.req->req5.source_dsa_invocation_id = GUID_zero(); + r.in.req->req5.naming_context = &nc; + r.in.req->req5.highwatermark.tmp_highest_usn = highest_usn; + r.in.req->req5.highwatermark.reserved_usn = 0; + r.in.req->req5.highwatermark.highest_usn = highest_usn; + r.in.req->req5.uptodateness_vector = NULL; + r.in.req->req5.replica_flags = 0; + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) { + r.in.req->req5.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION; + } + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) { + r.in.req->req5.replica_flags |= DRSUAPI_DRS_WRIT_REP; + } + r.in.req->req5.replica_flags |= DRSUAPI_DRS_INIT_SYNC + | DRSUAPI_DRS_PER_SYNC + | DRSUAPI_DRS_GET_ANC + | DRSUAPI_DRS_NEVER_SYNCED + ; + r.in.req->req5.max_object_count = 133; + r.in.req->req5.max_ndr_size = 1336770; + r.in.req->req5.extended_op = DRSUAPI_EXOP_NONE; + r.in.req->req5.fsmo_info = 0; + + break; + case 8: + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = nc_dn_str; + /* nc.dn can be set to any other ad partition */ + + r.in.req = &req; + r.in.req->req8.destination_dsa_guid = ctx->new_dc.invocation_id; + r.in.req->req8.source_dsa_invocation_id = GUID_zero(); + r.in.req->req8.naming_context = &nc; + r.in.req->req8.highwatermark.tmp_highest_usn = highest_usn; + r.in.req->req8.highwatermark.reserved_usn = 0; + r.in.req->req8.highwatermark.highest_usn = highest_usn; + r.in.req->req8.uptodateness_vector = NULL; + r.in.req->req8.replica_flags = 0; + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) { + r.in.req->req8.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION; + } + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) { + r.in.req->req8.replica_flags |= DRSUAPI_DRS_WRIT_REP; + } + r.in.req->req8.replica_flags |= DRSUAPI_DRS_INIT_SYNC + | DRSUAPI_DRS_PER_SYNC + | DRSUAPI_DRS_GET_ANC + | DRSUAPI_DRS_NEVER_SYNCED + ; + r.in.req->req8.max_object_count = 402; + r.in.req->req8.max_ndr_size = 402116; + + r.in.req->req8.extended_op = DRSUAPI_EXOP_NONE; + r.in.req->req8.fsmo_info = 0; + r.in.req->req8.partial_attribute_set = NULL; + r.in.req->req8.partial_attribute_set_ex = NULL; + r.in.req->req8.mapping_ctr.num_mappings = 0; + r.in.req->req8.mapping_ctr.mappings = NULL; + + break; + } + + for (y=0; ;y++) { + uint32_t _level = 0; + union drsuapi_DsGetNCChangesCtr ctr; + + ZERO_STRUCT(r.out); + + r.out.level_out = &_level; + r.out.ctr = &ctr; + + if (r.in.level == 5) { + torture_comment(tctx, + "start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n", + y, + (unsigned long long) r.in.req->req5.highwatermark.tmp_highest_usn, + (unsigned long long) r.in.req->req5.highwatermark.highest_usn); + } + + if (r.in.level == 8) { + torture_comment(tctx, + "start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n", + y, + (unsigned long long) r.in.req->req8.highwatermark.tmp_highest_usn, + (unsigned long long) r.in.req->req8.highwatermark.highest_usn); + } + + status = dcerpc_drsuapi_DsGetNCChanges_r(ctx->new_dc.drsuapi.drs_handle, ctx, &r); + torture_drsuapi_assert_call(tctx, ctx->new_dc.drsuapi.drs_pipe, status, + &r, "dcerpc_drsuapi_DsGetNCChanges"); + + if (ret == true && *r.out.level_out == 1) { + out_level = 1; + ctr1 = &r.out.ctr->ctr1; + } else if (ret == true && *r.out.level_out == 2 && + r.out.ctr->ctr2.mszip1.ts) { + out_level = 1; + ctr1 = &r.out.ctr->ctr2.mszip1.ts->ctr1; + } + + if (out_level == 1) { + torture_comment(tctx, + "end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n", + y, + (unsigned long long) ctr1->new_highwatermark.tmp_highest_usn, + (unsigned long long) ctr1->new_highwatermark.highest_usn); + + if (!test_analyse_objects(tctx, ctx, nc_dn_str, &ctr1->mapping_ctr, ctr1->object_count, + ctr1->first_object, &gensec_skey)) { + return false; + } + + if (ctr1->more_data) { + r.in.req->req5.highwatermark = ctr1->new_highwatermark; + continue; + } + } + + if (ret == true && *r.out.level_out == 6) { + out_level = 6; + ctr6 = &r.out.ctr->ctr6; + } else if (ret == true && *r.out.level_out == 7 + && r.out.ctr->ctr7.level == 6 + && r.out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP + && r.out.ctr->ctr7.ctr.mszip6.ts) { + out_level = 6; + ctr6 = &r.out.ctr->ctr7.ctr.mszip6.ts->ctr6; + } else if (ret == true && *r.out.level_out == 7 + && r.out.ctr->ctr7.level == 6 + && r.out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_WIN2K3_LZ77_DIRECT2 + && r.out.ctr->ctr7.ctr.xpress6.ts) { + out_level = 6; + ctr6 = &r.out.ctr->ctr7.ctr.xpress6.ts->ctr6; + } + + if (out_level == 6) { + torture_comment(tctx, + "end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n", + y, + (unsigned long long) ctr6->new_highwatermark.tmp_highest_usn, + (unsigned long long) ctr6->new_highwatermark.highest_usn); + + if (!test_analyse_objects(tctx, ctx, nc_dn_str, &ctr6->mapping_ctr, ctr6->object_count, + ctr6->first_object, &gensec_skey)) { + return false; + } + + if (ctr6->more_data) { + r.in.req->req8.highwatermark = ctr6->new_highwatermark; + continue; + } + } + + break; + } + } + + return ret; +} + +/** + * Test DsGetNCChanges() DRSUAPI call against one + * or more Naming Contexts. + * Specific NC to test with may be supplied + * in lp_ctx configuration. If no NC is specified, + * it will test DsGetNCChanges() on all NCs on remote DC + */ +static bool test_FetchData(struct torture_context *tctx, struct DsSyncTest *ctx) +{ + bool ret = true; + size_t i, count; + const char *nc_dn_str; + const char **nc_list; + + nc_list = const_str_list(str_list_make_empty(ctx)); + torture_assert(tctx, nc_list, "Not enough memory!"); + + /* make a list of partitions to test with */ + nc_dn_str = lpcfg_parm_string(tctx->lp_ctx, NULL, "dssync", "partition"); + if (nc_dn_str == NULL) { + nc_list = str_list_add_const(nc_list, ctx->domain_dn); + nc_list = str_list_add_const(nc_list, ctx->config_dn); + nc_list = str_list_add_const(nc_list, ctx->schema_dn); + } else { + nc_list = str_list_add_const(nc_list, nc_dn_str); + } + + count = str_list_length(nc_list); + for (i = 0; i < count && ret; i++) { + torture_comment(tctx, "\nNaming Context: %s\n", nc_list[i]); + ret = test_GetNCChanges(tctx, ctx, nc_list[i]); + } + + talloc_free(nc_list); + return ret; +} + + +static bool test_FetchNT4Data(struct torture_context *tctx, + struct DsSyncTest *ctx) +{ + NTSTATUS status; + struct drsuapi_DsGetNT4ChangeLog r; + union drsuapi_DsGetNT4ChangeLogRequest req; + union drsuapi_DsGetNT4ChangeLogInfo info; + uint32_t level_out = 0; + DATA_BLOB cookie; + + ZERO_STRUCT(cookie); + + ZERO_STRUCT(r); + r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + r.in.level = 1; + r.out.info = &info; + r.out.level_out = &level_out; + + req.req1.flags = lpcfg_parm_int(tctx->lp_ctx, NULL, + "dssync", "nt4changelog_flags", + DRSUAPI_NT4_CHANGELOG_GET_CHANGELOG | + DRSUAPI_NT4_CHANGELOG_GET_SERIAL_NUMBERS); + req.req1.preferred_maximum_length = lpcfg_parm_int(tctx->lp_ctx, NULL, + "dssync", "nt4changelog_preferred_len", + 0x00004000); + + while (1) { + req.req1.restart_length = cookie.length; + req.req1.restart_data = cookie.data; + + r.in.req = &req; + + status = dcerpc_drsuapi_DsGetNT4ChangeLog_r(ctx->new_dc.drsuapi.drs_handle, ctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { + torture_skip(tctx, + "DsGetNT4ChangeLog not supported: NT_STATUS_NOT_IMPLEMENTED"); + } else if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + torture_skip(tctx, + "DsGetNT4ChangeLog not supported: NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE"); + } + torture_fail(tctx, + talloc_asprintf(tctx, "dcerpc_drsuapi_DsGetNT4ChangeLog failed - %s\n", + errstr)); + } else if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_DOMAIN_ROLE)) { + torture_skip(tctx, + "DsGetNT4ChangeLog not supported: WERR_INVALID_DOMAIN_ROLE"); + } else if (!W_ERROR_IS_OK(r.out.result)) { + torture_fail(tctx, + talloc_asprintf(tctx, "DsGetNT4ChangeLog failed - %s\n", + win_errstr(r.out.result))); + } else if (*r.out.level_out != 1) { + torture_fail(tctx, + talloc_asprintf(tctx, "DsGetNT4ChangeLog unknown level - %u\n", + *r.out.level_out)); + } else if (NT_STATUS_IS_OK(r.out.info->info1.status)) { + } else if (NT_STATUS_EQUAL(r.out.info->info1.status, STATUS_MORE_ENTRIES)) { + cookie.length = r.out.info->info1.restart_length; + cookie.data = r.out.info->info1.restart_data; + continue; + } else { + torture_fail(tctx, + talloc_asprintf(tctx, "DsGetNT4ChangeLog failed - %s\n", + nt_errstr(r.out.info->info1.status))); + } + + break; + } + + return true; +} + +/** + * DSSYNC test case setup + */ +static bool torture_dssync_tcase_setup(struct torture_context *tctx, void **data) +{ + bool bret; + struct DsSyncTest *ctx; + + *data = ctx = test_create_context(tctx); + torture_assert(tctx, ctx, "test_create_context() failed"); + + bret = _test_DsBind(tctx, ctx, ctx->admin.credentials, &ctx->admin.drsuapi); + torture_assert(tctx, bret, "_test_DsBind() failed"); + + bret = test_LDAPBind(tctx, ctx, ctx->admin.credentials, &ctx->admin.ldap); + torture_assert(tctx, bret, "test_LDAPBind() failed"); + + bret = test_GetInfo(tctx, ctx); + torture_assert(tctx, bret, "test_GetInfo() failed"); + + bret = _test_DsBind(tctx, ctx, ctx->new_dc.credentials, &ctx->new_dc.drsuapi); + torture_assert(tctx, bret, "_test_DsBind() failed"); + + return true; +} + +/** + * DSSYNC test case cleanup + */ +static bool torture_dssync_tcase_teardown(struct torture_context *tctx, void *data) +{ + struct DsSyncTest *ctx; + struct drsuapi_DsUnbind r; + struct policy_handle bind_handle; + + ctx = talloc_get_type(data, struct DsSyncTest); + + ZERO_STRUCT(r); + r.out.bind_handle = &bind_handle; + + /* Unbing admin handle */ + r.in.bind_handle = &ctx->admin.drsuapi.bind_handle; + dcerpc_drsuapi_DsUnbind_r(ctx->admin.drsuapi.drs_handle, ctx, &r); + + /* Unbing new_dc handle */ + r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle; + dcerpc_drsuapi_DsUnbind_r(ctx->new_dc.drsuapi.drs_handle, ctx, &r); + + talloc_free(ctx); + + return true; +} + +/** + * DSSYNC test case implementation + */ +void torture_drs_rpc_dssync_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "dssync"); + + torture_tcase_set_fixture(tcase, + torture_dssync_tcase_setup, + torture_dssync_tcase_teardown); + + torture_tcase_add_simple_test(tcase, "DC_FetchData", (run_func)test_FetchData); + torture_tcase_add_simple_test(tcase, "FetchNT4Data", (run_func)test_FetchNT4Data); +} + diff --git a/source4/torture/drs/rpc/msds_intid.c b/source4/torture/drs/rpc/msds_intid.c new file mode 100644 index 0000000..1bc5c32 --- /dev/null +++ b/source4/torture/drs/rpc/msds_intid.c @@ -0,0 +1,792 @@ +/* + Unix SMB/CIFS implementation. + + msDS-IntId attribute replication test. + + Copyright (C) Kamen Mazdrashki 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "libcli/cldap/cldap.h" +#include "torture/torture.h" +#include "../libcli/drsuapi/drsuapi.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "dsdb/samdb/samdb.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/drs/proto.h" +#include "lib/tsocket/tsocket.h" +#include "libcli/resolve/resolve.h" +#include "lib/util/util_paths.h" + +struct DsSyncBindInfo { + struct dcerpc_pipe *drs_pipe; + struct dcerpc_binding_handle *drs_handle; + struct drsuapi_DsBind req; + struct GUID bind_guid; + struct drsuapi_DsBindInfoCtr our_bind_info_ctr; + struct drsuapi_DsBindInfo28 our_bind_info28; + struct drsuapi_DsBindInfo28 peer_bind_info28; + struct policy_handle bind_handle; +}; + +struct DsaBindInfo { + struct dcerpc_binding *server_binding; + + struct dcerpc_pipe *drs_pipe; + struct dcerpc_binding_handle *drs_handle; + + DATA_BLOB gensec_skey; + struct drsuapi_DsBindInfo48 srv_info48; + struct policy_handle rpc_handle; +}; + +struct DsIntIdTestCtx { + const char *ldap_url; + const char *domain_dn; + const char *config_dn; + const char *schema_dn; + + /* what we need to do as 'Administrator' */ + struct cli_credentials *creds; + struct DsaBindInfo dsa_bind; + struct ldb_context *ldb; + +}; + +/* Format string to create provision LDIF with */ +#define PROVISION_LDIF_FMT \ + "###########################################################\n" \ + "# Format string with positional params:\n" \ + "# 1 - (int) Unique ID between 1 and 2^16\n" \ + "# 2 - (string) Domain DN\n" \ + "###########################################################\n" \ + "\n" \ + "###########################################################\n" \ + "# Update schema\n" \ + "###########################################################\n" \ + "dn: CN=msds-intid-%1$d,CN=Schema,CN=Configuration,%2$s\n" \ + "changetype: add\n" \ + "objectClass: top\n" \ + "objectClass: attributeSchema\n" \ + "cn: msds-intid-%1$d\n" \ + "attributeID: 1.3.6.1.4.1.7165.4.6.1.%1$d.1.5.9940\n" \ + "attributeSyntax: 2.5.5.10\n" \ + "omSyntax: 4\n" \ + "instanceType: 4\n" \ + "isSingleValued: TRUE\n" \ + "systemOnly: FALSE\n" \ + "\n" \ + "# schemaUpdateNow\n" \ + "DN:\n" \ + "changeType: modify\n" \ + "add: schemaUpdateNow\n" \ + "schemaUpdateNow: 1\n" \ + "-\n" \ + "\n" \ + "###########################################################\n" \ + "# Update schema (with linked attribute)\n" \ + "###########################################################\n" \ + "dn: CN=msds-intid-link-%1$d,CN=Schema,CN=Configuration,%2$s\n" \ + "changetype: add\n" \ + "objectClass: top\n" \ + "objectClass: attributeSchema\n" \ + "cn: msds-intid-link-%1$d\n" \ + "attributeID: 1.3.6.1.4.1.7165.4.6.1.%1$d.1.5.9941\n" \ + "attributeSyntax: 2.5.5.1\n" \ + "omSyntax: 127\n" \ + "instanceType: 4\n" \ + "isSingleValued: TRUE\n" \ + "systemOnly: FALSE\n" \ + "linkID: 1.2.840.113556.1.2.50\n" \ + "\n" \ + "# schemaUpdateNow\n" \ + "DN:\n" \ + "changeType: modify\n" \ + "add: schemaUpdateNow\n" \ + "schemaUpdateNow: 1\n" \ + "-\n" \ + "\n" \ + "###########################################################\n" \ + "# Update User class\n" \ + "###########################################################\n" \ + "dn: CN=User,CN=Schema,CN=Configuration,%2$s\n" \ + "changetype: modify\n" \ + "add: mayContain\n" \ + "mayContain: msdsIntid%1$d\n" \ + "mayContain: msdsIntidLink%1$d\n" \ + "-\n" \ + "\n" \ + "# schemaUpdateNow\n" \ + "DN:\n" \ + "changeType: modify\n" \ + "add: schemaUpdateNow\n" \ + "schemaUpdateNow: 1\n" \ + "-\n" \ + "\n" \ + "###########################################################\n" \ + "# create user to test with\n" \ + "###########################################################\n" \ + "dn: CN=dsIntId_usr_%1$d,CN=Users,%2$s\n" \ + "changetype: add\n" \ + "objectClass: user\n" \ + "cn: dsIntId_usr_%1$d\n" \ + "name: dsIntId_usr_%1$d\n" \ + "displayName: dsIntId_usr_%1$d\n" \ + "sAMAccountName: dsIntId_usr_%1$d\n" \ + "msdsIntid%1$d: msDS-IntId-%1$d attribute value\n" \ + "msdsIntidLink%1$d: %2$s\n" \ + "\n" + + +static struct DsIntIdTestCtx *_dsintid_create_context(struct torture_context *tctx) +{ + NTSTATUS status; + struct DsIntIdTestCtx *ctx; + struct dcerpc_binding *server_binding; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + /* Create test suite context */ + ctx = talloc_zero(tctx, struct DsIntIdTestCtx); + if (!ctx) { + torture_result(tctx, TORTURE_FAIL, "Not enough memory!"); + return NULL; + } + + /* parse binding object */ + status = dcerpc_parse_binding(ctx, binding, &server_binding); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "Bad binding string '%s': %s", binding, nt_errstr(status)); + return NULL; + } + + status = dcerpc_binding_set_flags(server_binding, + DCERPC_SIGN | DCERPC_SEAL, 0); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "dcerpc_binding_set_flags: %s", nt_errstr(status)); + return NULL; + } + + /* populate test suite context */ + ctx->creds = samba_cmdline_get_creds(); + ctx->dsa_bind.server_binding = server_binding; + + ctx->ldap_url = talloc_asprintf(ctx, "ldap://%s", + dcerpc_binding_get_string_option(server_binding, "host")); + + return ctx; +} + +static bool _test_DsaBind(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct cli_credentials *credentials, + uint32_t req_extensions, + struct DsaBindInfo *bi) +{ + NTSTATUS status; + struct GUID bind_guid; + struct drsuapi_DsBind r; + struct drsuapi_DsBindInfoCtr bind_info_ctr; + uint32_t supported_extensions; + + /* make DCE RPC connection */ + status = dcerpc_pipe_connect_b(mem_ctx, + &bi->drs_pipe, + bi->server_binding, + &ndr_table_drsuapi, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "Failed to connect to server"); + + bi->drs_handle = bi->drs_pipe->binding_handle; + + status = gensec_session_key(bi->drs_pipe->conn->security_state.generic_state, + mem_ctx, &bi->gensec_skey); + torture_assert_ntstatus_ok(tctx, status, "failed to get gensec session key"); + + /* Bind to DRSUAPI interface */ + GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &bind_guid); + + /* + * Add flags that should be 1, according to MS docs. + * It turns out DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3 + * is actually required in order for GetNCChanges() to + * return schemaInfo entry in the prefixMap returned. + * Use DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION so + * we are able to fetch sensitive data. + */ + supported_extensions = req_extensions + | DRSUAPI_SUPPORTED_EXTENSION_BASE + | DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION + | DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD + | DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3 + | DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; + + ZERO_STRUCT(bind_info_ctr); + bind_info_ctr.length = 28; + bind_info_ctr.info.info28.supported_extensions = supported_extensions; + + r.in.bind_guid = &bind_guid; + r.in.bind_info = &bind_info_ctr; + r.out.bind_handle = &bi->rpc_handle; + + status = dcerpc_drsuapi_DsBind_r(bi->drs_handle, mem_ctx, &r); + torture_drsuapi_assert_call(tctx, bi->drs_pipe, status, + &r, "dcerpc_drsuapi_DsBind_r"); + + + switch (r.out.bind_info->length) { + case 24: { + struct drsuapi_DsBindInfo24 *info24; + info24 = &r.out.bind_info->info.info24; + bi->srv_info48.supported_extensions = info24->supported_extensions; + bi->srv_info48.site_guid = info24->site_guid; + bi->srv_info48.pid = info24->pid; + break; + } + case 28: { + struct drsuapi_DsBindInfo28 *info28; + info28 = &r.out.bind_info->info.info28; + bi->srv_info48.supported_extensions = info28->supported_extensions; + bi->srv_info48.site_guid = info28->site_guid; + bi->srv_info48.pid = info28->pid; + bi->srv_info48.repl_epoch = info28->repl_epoch; + break; + } + case 32: { + struct drsuapi_DsBindInfo32 *info32; + info32 = &r.out.bind_info->info.info32; + bi->srv_info48.supported_extensions = info32->supported_extensions; + bi->srv_info48.site_guid = info32->site_guid; + bi->srv_info48.pid = info32->pid; + bi->srv_info48.repl_epoch = info32->repl_epoch; + break; + } + case 48: { + bi->srv_info48 = r.out.bind_info->info.info48; + break; + } + case 52: { + struct drsuapi_DsBindInfo52 *info52; + info52 = &r.out.bind_info->info.info52; + bi->srv_info48.supported_extensions = info52->supported_extensions; + bi->srv_info48.site_guid = info52->site_guid; + bi->srv_info48.pid = info52->pid; + bi->srv_info48.repl_epoch = info52->repl_epoch; + break; + } + default: + torture_result(tctx, TORTURE_FAIL, + "DsBind: unknown BindInfo length: %u", + r.out.bind_info->length); + return false; + } + + /* check if server supports extensions we've requested */ + if ((bi->srv_info48.supported_extensions & req_extensions) != req_extensions) { + torture_result(tctx, TORTURE_FAIL, + "Server does not support requested extensions. " + "Requested: 0x%08X, Supported: 0x%08X", + req_extensions, bi->srv_info48.supported_extensions); + return false; + } + + return true; +} + +static bool _test_LDAPBind(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct cli_credentials *credentials, + const char *ldap_url, + struct ldb_context **_ldb) +{ + bool ret = true; + + struct ldb_context *ldb; + + const char *modules_option[] = { "modules:paged_searches", NULL }; + ldb = ldb_init(mem_ctx, tctx->ev); + if (ldb == NULL) { + return false; + } + + /* Despite us loading the schema from the AD server, we need + * the samba handlers to get the extended DN syntax stuff */ + ret = ldb_register_samba_handlers(ldb); + if (ret != LDB_SUCCESS) { + talloc_free(ldb); + return NULL; + } + + ldb_set_modules_dir(ldb, modules_path(ldb, "ldb")); + + if (ldb_set_opaque(ldb, "credentials", credentials) != LDB_SUCCESS) { + talloc_free(ldb); + return NULL; + } + + if (ldb_set_opaque(ldb, "loadparm", tctx->lp_ctx) != LDB_SUCCESS) { + talloc_free(ldb); + return NULL; + } + + ret = ldb_connect(ldb, ldap_url, 0, modules_option); + if (ret != LDB_SUCCESS) { + talloc_free(ldb); + torture_assert_int_equal(tctx, ret, LDB_SUCCESS, "Failed to make LDB connection to target"); + } + + *_ldb = ldb; + + return true; +} + +static bool _test_provision(struct torture_context *tctx, struct DsIntIdTestCtx *ctx) +{ + int ret; + char *ldif_str; + const char *pstr; + struct ldb_ldif *ldif; + uint32_t attr_id; + struct ldb_context *ldb = ctx->ldb; + + /* We must have LDB connection ready by this time */ + SMB_ASSERT(ldb != NULL); + + ctx->domain_dn = ldb_dn_get_linearized(ldb_get_default_basedn(ldb)); + torture_assert(tctx, ctx->domain_dn != NULL, "Failed to get Domain DN"); + + ctx->config_dn = ldb_dn_get_linearized(ldb_get_config_basedn(ldb)); + torture_assert(tctx, ctx->config_dn != NULL, "Failed to get Domain DN"); + + ctx->schema_dn = ldb_dn_get_linearized(ldb_get_schema_basedn(ldb)); + torture_assert(tctx, ctx->schema_dn != NULL, "Failed to get Domain DN"); + + /* prepare LDIF to provision with */ + attr_id = generate_random() % 0xFFFF; + pstr = ldif_str = talloc_asprintf(ctx, PROVISION_LDIF_FMT, + attr_id, ctx->domain_dn); + + /* Provision test data */ + while ((ldif = ldb_ldif_read_string(ldb, &pstr)) != NULL) { + switch (ldif->changetype) { + case LDB_CHANGETYPE_DELETE: + ret = ldb_delete(ldb, ldif->msg->dn); + break; + case LDB_CHANGETYPE_MODIFY: + ret = ldb_modify(ldb, ldif->msg); + break; + case LDB_CHANGETYPE_ADD: + default: + ret = ldb_add(ldb, ldif->msg); + break; + } + if (ret != LDB_SUCCESS) { + char *msg = talloc_asprintf(ctx, + "Failed to apply ldif - %s (%s): \n%s", + ldb_errstring(ldb), + ldb_strerror(ret), + ldb_ldif_write_string(ldb, ctx, ldif)); + torture_fail(tctx, msg); + + } + ldb_ldif_read_free(ldb, ldif); + } + + return true; +} + + +static bool _test_GetNCChanges(struct torture_context *tctx, + struct DsaBindInfo *bi, + const char *nc_dn_str, + TALLOC_CTX *mem_ctx, + struct drsuapi_DsGetNCChangesCtr6 **_ctr6) +{ + NTSTATUS status; + struct drsuapi_DsGetNCChanges r; + union drsuapi_DsGetNCChangesRequest req; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct drsuapi_DsGetNCChangesCtr6 *ctr6_chunk = NULL; + struct drsuapi_DsGetNCChangesCtr6 ctr6; + uint32_t _level = 0; + union drsuapi_DsGetNCChangesCtr ctr; + + struct dom_sid null_sid; + + ZERO_STRUCT(null_sid); + + /* fill-in Naming Context */ + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = nc_dn_str; + + /* fill-in request fields */ + req.req8.destination_dsa_guid = GUID_random(); + req.req8.source_dsa_invocation_id = GUID_zero(); + req.req8.naming_context = &nc; + req.req8.highwatermark.tmp_highest_usn = 0; + req.req8.highwatermark.reserved_usn = 0; + req.req8.highwatermark.highest_usn = 0; + req.req8.uptodateness_vector = NULL; + req.req8.replica_flags = DRSUAPI_DRS_WRIT_REP + | DRSUAPI_DRS_INIT_SYNC + | DRSUAPI_DRS_PER_SYNC + | DRSUAPI_DRS_GET_ANC + | DRSUAPI_DRS_NEVER_SYNCED + ; + req.req8.max_object_count = 402; + req.req8.max_ndr_size = 402116; + + req.req8.extended_op = DRSUAPI_EXOP_NONE; + req.req8.fsmo_info = 0; + req.req8.partial_attribute_set = NULL; + req.req8.partial_attribute_set_ex = NULL; + req.req8.mapping_ctr.num_mappings = 0; + req.req8.mapping_ctr.mappings = NULL; + + r.in.bind_handle = &bi->rpc_handle; + r.in.level = 8; + r.in.req = &req; + + ZERO_STRUCT(r.out); + r.out.level_out = &_level; + r.out.ctr = &ctr; + + ZERO_STRUCT(ctr6); + do { + ZERO_STRUCT(ctr); + + status = dcerpc_drsuapi_DsGetNCChanges_r(bi->drs_handle, mem_ctx, &r); + torture_drsuapi_assert_call(tctx, bi->drs_pipe, status, + &r, "dcerpc_drsuapi_DsGetNCChanges_r"); + + /* we expect to get level 6 reply */ + torture_assert_int_equal(tctx, _level, 6, "Expected level 6 reply"); + + /* store this chunk for later use */ + ctr6_chunk = &r.out.ctr->ctr6; + + if (!ctr6.first_object) { + ctr6 = *ctr6_chunk; + } else { + struct drsuapi_DsReplicaObjectListItemEx *cur; + + ctr6.object_count += ctr6_chunk->object_count; + for (cur = ctr6.first_object; cur->next_object; cur = cur->next_object) {} + cur->next_object = ctr6_chunk->first_object; + + if (ctr6_chunk->linked_attributes_count != 0) { + uint32_t i; + ctr6.linked_attributes = talloc_realloc(mem_ctx, ctr6.linked_attributes, + struct drsuapi_DsReplicaLinkedAttribute, + ctr6.linked_attributes_count + ctr6_chunk->linked_attributes_count); + for (i = 0; i < ctr6_chunk->linked_attributes_count; i++) { + ctr6.linked_attributes[ctr6.linked_attributes_count++] = ctr6_chunk->linked_attributes[i]; + } + } + } + + /* prepare for next request */ + r.in.req->req8.highwatermark = ctr6_chunk->new_highwatermark; + + } while (ctr6_chunk->more_data); + + *_ctr6 = talloc(mem_ctx, struct drsuapi_DsGetNCChangesCtr6); + torture_assert(mem_ctx, *_ctr6, "Not enough memory"); + **_ctr6 = ctr6; + + return true; +} + +static char * _make_error_message(TALLOC_CTX *mem_ctx, + enum drsuapi_DsAttributeId drs_attid, + const struct dsdb_attribute *dsdb_attr, + const struct drsuapi_DsReplicaObjectIdentifier *identifier) +{ + return talloc_asprintf(mem_ctx, "\nInvalid ATTID for %1$s (%2$s)\n" + " drs_attid: %3$11d (0x%3$08X)\n" + " msDS_IntId: %4$11d (0x%4$08X)\n" + " attributeId_id: %5$11d (0x%5$08X)", + dsdb_attr->lDAPDisplayName, + identifier->dn, + drs_attid, + dsdb_attr->msDS_IntId, + dsdb_attr->attributeID_id); +} + +/** + * Fetch Schema NC and check ATTID values returned. + * When Schema partition is replicated, ATTID + * should always be made using prefixMap + */ +static bool test_dsintid_schema(struct torture_context *tctx, struct DsIntIdTestCtx *ctx) +{ + uint32_t i; + const struct dsdb_schema *ldap_schema; + struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; + const struct dsdb_attribute *dsdb_attr; + const struct drsuapi_DsReplicaAttribute *drs_attr; + const struct drsuapi_DsReplicaAttributeCtr *attr_ctr; + const struct drsuapi_DsReplicaObjectListItemEx *cur; + const struct drsuapi_DsReplicaLinkedAttribute *la; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(ctx); + torture_assert(tctx, mem_ctx, "Not enough memory"); + + /* fetch whole Schema partition */ + torture_comment(tctx, "Fetch partition: %s\n", ctx->schema_dn); + if (!_test_GetNCChanges(tctx, &ctx->dsa_bind, ctx->schema_dn, mem_ctx, &ctr6)) { + torture_fail(tctx, "_test_GetNCChanges() failed"); + } + + /* load schema if not loaded yet */ + torture_comment(tctx, "Loading schema...\n"); + if (!drs_util_dsdb_schema_load_ldb(tctx, ctx->ldb, &ctr6->mapping_ctr, false)) { + torture_fail(tctx, "drs_util_dsdb_schema_load_ldb() failed"); + } + ldap_schema = dsdb_get_schema(ctx->ldb, NULL); + + /* verify ATTIDs fetched */ + torture_comment(tctx, "Verify ATTIDs fetched\n"); + for (cur = ctr6->first_object; cur; cur = cur->next_object) { + attr_ctr = &cur->object.attribute_ctr; + for (i = 0; i < attr_ctr->num_attributes; i++) { + drs_attr = &attr_ctr->attributes[i]; + dsdb_attr = dsdb_attribute_by_attributeID_id(ldap_schema, + drs_attr->attid); + + torture_assert(tctx, + drs_attr->attid == dsdb_attr->attributeID_id, + _make_error_message(ctx, drs_attr->attid, + dsdb_attr, + cur->object.identifier)); + if (dsdb_attr->msDS_IntId) { + torture_assert(tctx, + drs_attr->attid != dsdb_attr->msDS_IntId, + _make_error_message(ctx, drs_attr->attid, + dsdb_attr, + cur->object.identifier)); + } + } + } + + /* verify ATTIDs for Linked Attributes */ + torture_comment(tctx, "Verify ATTIDs for Linked Attributes (%u)\n", + ctr6->linked_attributes_count); + for (i = 0; i < ctr6->linked_attributes_count; i++) { + la = &ctr6->linked_attributes[i]; + dsdb_attr = dsdb_attribute_by_attributeID_id(ldap_schema, la->attid); + + torture_assert(tctx, + la->attid == dsdb_attr->attributeID_id, + _make_error_message(ctx, la->attid, + dsdb_attr, + la->identifier)); + if (dsdb_attr->msDS_IntId) { + torture_assert(tctx, + la->attid != dsdb_attr->msDS_IntId, + _make_error_message(ctx, la->attid, + dsdb_attr, + la->identifier)); + } + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Fetch non-Schema NC and check ATTID values returned. + * When non-Schema partition is replicated, ATTID + * should be msDS-IntId value for the attribute + * if this value exists + */ +static bool _test_dsintid(struct torture_context *tctx, + struct DsIntIdTestCtx *ctx, + const char *nc_dn_str) +{ + uint32_t i; + const struct dsdb_schema *ldap_schema; + struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; + const struct dsdb_attribute *dsdb_attr; + const struct drsuapi_DsReplicaAttribute *drs_attr; + const struct drsuapi_DsReplicaAttributeCtr *attr_ctr; + const struct drsuapi_DsReplicaObjectListItemEx *cur; + const struct drsuapi_DsReplicaLinkedAttribute *la; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(ctx); + torture_assert(tctx, mem_ctx, "Not enough memory"); + + /* fetch whole Schema partition */ + torture_comment(tctx, "Fetch partition: %s\n", nc_dn_str); + if (!_test_GetNCChanges(tctx, &ctx->dsa_bind, nc_dn_str, mem_ctx, &ctr6)) { + torture_fail(tctx, "_test_GetNCChanges() failed"); + } + + /* load schema if not loaded yet */ + torture_comment(tctx, "Loading schema...\n"); + if (!drs_util_dsdb_schema_load_ldb(tctx, ctx->ldb, &ctr6->mapping_ctr, false)) { + torture_fail(tctx, "drs_util_dsdb_schema_load_ldb() failed"); + } + ldap_schema = dsdb_get_schema(ctx->ldb, NULL); + + /* verify ATTIDs fetched */ + torture_comment(tctx, "Verify ATTIDs fetched\n"); + for (cur = ctr6->first_object; cur; cur = cur->next_object) { + attr_ctr = &cur->object.attribute_ctr; + for (i = 0; i < attr_ctr->num_attributes; i++) { + drs_attr = &attr_ctr->attributes[i]; + dsdb_attr = dsdb_attribute_by_attributeID_id(ldap_schema, + drs_attr->attid); + if (dsdb_attr->msDS_IntId) { + torture_assert(tctx, + drs_attr->attid == dsdb_attr->msDS_IntId, + _make_error_message(ctx, drs_attr->attid, + dsdb_attr, + cur->object.identifier)); + } else { + torture_assert(tctx, + drs_attr->attid == dsdb_attr->attributeID_id, + _make_error_message(ctx, drs_attr->attid, + dsdb_attr, + cur->object.identifier)); + } + } + } + + /* verify ATTIDs for Linked Attributes */ + torture_comment(tctx, "Verify ATTIDs for Linked Attributes (%u)\n", + ctr6->linked_attributes_count); + for (i = 0; i < ctr6->linked_attributes_count; i++) { + la = &ctr6->linked_attributes[i]; + dsdb_attr = dsdb_attribute_by_attributeID_id(ldap_schema, la->attid); + + if (dsdb_attr->msDS_IntId) { + torture_assert(tctx, + la->attid == dsdb_attr->msDS_IntId, + _make_error_message(ctx, la->attid, + dsdb_attr, + la->identifier)); + } else { + torture_assert(tctx, + la->attid == dsdb_attr->attributeID_id, + _make_error_message(ctx, la->attid, + dsdb_attr, + la->identifier)); + } + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Fetch Domain NC and check ATTID values returned. + * When Domain partition is replicated, ATTID + * should be msDS-IntId value for the attribute + * if this value exists + */ +static bool test_dsintid_configuration(struct torture_context *tctx, struct DsIntIdTestCtx *ctx) +{ + return _test_dsintid(tctx, ctx, ctx->config_dn); +} + +/** + * Fetch Configuration NC and check ATTID values returned. + * When Configuration partition is replicated, ATTID + * should be msDS-IntId value for the attribute + * if this value exists + */ +static bool test_dsintid_domain(struct torture_context *tctx, struct DsIntIdTestCtx *ctx) +{ + return _test_dsintid(tctx, ctx, ctx->domain_dn); +} + + +/** + * DSSYNC test case setup + */ +static bool torture_dsintid_tcase_setup(struct torture_context *tctx, void **data) +{ + bool bret; + struct DsIntIdTestCtx *ctx; + + *data = ctx = _dsintid_create_context(tctx); + torture_assert(tctx, ctx, "test_create_context() failed"); + + bret = _test_DsaBind(tctx, ctx, ctx->creds, + DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8 | + DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6, + &ctx->dsa_bind); + torture_assert(tctx, bret, "_test_DsaBind() failed"); + + bret = _test_LDAPBind(tctx, ctx, ctx->creds, ctx->ldap_url, &ctx->ldb); + torture_assert(tctx, bret, "_test_LDAPBind() failed"); + + bret = _test_provision(tctx, ctx); + torture_assert(tctx, bret, "_test_provision() failed"); + + return true; +} + +/** + * DSSYNC test case cleanup + */ +static bool torture_dsintid_tcase_teardown(struct torture_context *tctx, void *data) +{ + struct DsIntIdTestCtx *ctx; + struct drsuapi_DsUnbind r; + struct policy_handle bind_handle; + + ctx = talloc_get_type(data, struct DsIntIdTestCtx); + + ZERO_STRUCT(r); + r.out.bind_handle = &bind_handle; + + /* Release DRSUAPI handle */ + r.in.bind_handle = &ctx->dsa_bind.rpc_handle; + dcerpc_drsuapi_DsUnbind_r(ctx->dsa_bind.drs_handle, ctx, &r); + + talloc_free(ctx); + + return true; +} + +/** + * DSSYNC test case implementation + */ +void torture_drs_rpc_dsintid_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "msDSIntId"); + + torture_tcase_set_fixture(tcase, + torture_dsintid_tcase_setup, + torture_dsintid_tcase_teardown); + + torture_tcase_add_simple_test(tcase, "Schema", (run_func)test_dsintid_schema); + torture_tcase_add_simple_test(tcase, "Configuration", (run_func)test_dsintid_configuration); + torture_tcase_add_simple_test(tcase, "Domain", (run_func)test_dsintid_domain); +} diff --git a/source4/torture/drs/unit/prefixmap_tests.c b/source4/torture/drs/unit/prefixmap_tests.c new file mode 100644 index 0000000..35764cd --- /dev/null +++ b/source4/torture/drs/unit/prefixmap_tests.c @@ -0,0 +1,900 @@ +/* + Unix SMB/CIFS implementation. + + DRSUAPI prefixMap unit tests + + Copyright (C) Kamen Mazdrashki 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "torture/smbtorture.h" +#include "dsdb/samdb/samdb.h" +#include "torture/rpc/drsuapi.h" +#include "torture/drs/proto.h" +#include "param/param.h" +#include "librpc/ndr/libndr.h" + +/** + * Private data to be shared among all test in Test case + */ +struct drsut_prefixmap_data { + struct dsdb_schema_prefixmap *pfm_new; + struct dsdb_schema_prefixmap *pfm_full; + + /* default schemaInfo value to test with */ + struct dsdb_schema_info *schi_default; + + struct ldb_context *ldb_ctx; +}; + +/** + * Test-oid data structure + */ +struct drsut_pfm_oid_data { + uint32_t id; + const char *bin_oid; + const char *oid_prefix; +}; + +/** + * Default prefixMap initialization data. + * This prefixMap is what dsdb_schema_pfm_new() should return. + * Based on: MS-DRSR, 5.16.4 ATTRTYP-to-OID Conversion + * procedure NewPrefixTable( ) + */ +static const struct drsut_pfm_oid_data _prefixmap_test_new_data[] = { + {.id=0x00000000, .bin_oid="5504", .oid_prefix="2.5.4"}, + {.id=0x00000001, .bin_oid="5506", .oid_prefix="2.5.6"}, + {.id=0x00000002, .bin_oid="2A864886F7140102", .oid_prefix="1.2.840.113556.1.2"}, + {.id=0x00000003, .bin_oid="2A864886F7140103", .oid_prefix="1.2.840.113556.1.3"}, + {.id=0x00000004, .bin_oid="6086480165020201", .oid_prefix="2.16.840.1.101.2.2.1"}, + {.id=0x00000005, .bin_oid="6086480165020203", .oid_prefix="2.16.840.1.101.2.2.3"}, + {.id=0x00000006, .bin_oid="6086480165020105", .oid_prefix="2.16.840.1.101.2.1.5"}, + {.id=0x00000007, .bin_oid="6086480165020104", .oid_prefix="2.16.840.1.101.2.1.4"}, + {.id=0x00000008, .bin_oid="5505", .oid_prefix="2.5.5"}, + {.id=0x00000009, .bin_oid="2A864886F7140104", .oid_prefix="1.2.840.113556.1.4"}, + {.id=0x0000000A, .bin_oid="2A864886F7140105", .oid_prefix="1.2.840.113556.1.5"}, + {.id=0x00000013, .bin_oid="0992268993F22C64", .oid_prefix="0.9.2342.19200300.100"}, + {.id=0x00000014, .bin_oid="6086480186F84203", .oid_prefix="2.16.840.1.113730.3"}, + {.id=0x00000015, .bin_oid="0992268993F22C6401", .oid_prefix="0.9.2342.19200300.100.1"}, + {.id=0x00000016, .bin_oid="6086480186F8420301", .oid_prefix="2.16.840.1.113730.3.1"}, + {.id=0x00000017, .bin_oid="2A864886F7140105B658", .oid_prefix="1.2.840.113556.1.5.7000"}, + {.id=0x00000018, .bin_oid="5515", .oid_prefix="2.5.21"}, + {.id=0x00000019, .bin_oid="5512", .oid_prefix="2.5.18"}, + {.id=0x0000001A, .bin_oid="5514", .oid_prefix="2.5.20"}, +}; + +/** + * Data to be used for creating full prefix map for testing. + * 'full-prefixMap' is based on what w2k8 returns as a prefixMap + * on clean installation - i.e. prefixMap for clean Schema + */ +static const struct drsut_pfm_oid_data _prefixmap_full_map_data[] = { + {.id=0x00000000, .bin_oid="0x5504", .oid_prefix="2.5.4"}, + {.id=0x00000001, .bin_oid="0x5506", .oid_prefix="2.5.6"}, + {.id=0x00000002, .bin_oid="0x2A864886F7140102", .oid_prefix="1.2.840.113556.1.2"}, + {.id=0x00000003, .bin_oid="0x2A864886F7140103", .oid_prefix="1.2.840.113556.1.3"}, + {.id=0x00000004, .bin_oid="0x6086480165020201", .oid_prefix="2.16.840.1.101.2.2.1"}, + {.id=0x00000005, .bin_oid="0x6086480165020203", .oid_prefix="2.16.840.1.101.2.2.3"}, + {.id=0x00000006, .bin_oid="0x6086480165020105", .oid_prefix="2.16.840.1.101.2.1.5"}, + {.id=0x00000007, .bin_oid="0x6086480165020104", .oid_prefix="2.16.840.1.101.2.1.4"}, + {.id=0x00000008, .bin_oid="0x5505", .oid_prefix="2.5.5"}, + {.id=0x00000009, .bin_oid="0x2A864886F7140104", .oid_prefix="1.2.840.113556.1.4"}, + {.id=0x0000000a, .bin_oid="0x2A864886F7140105", .oid_prefix="1.2.840.113556.1.5"}, + {.id=0x00000013, .bin_oid="0x0992268993F22C64", .oid_prefix="0.9.2342.19200300.100"}, + {.id=0x00000014, .bin_oid="0x6086480186F84203", .oid_prefix="2.16.840.1.113730.3"}, + {.id=0x00000015, .bin_oid="0x0992268993F22C6401", .oid_prefix="0.9.2342.19200300.100.1"}, + {.id=0x00000016, .bin_oid="0x6086480186F8420301", .oid_prefix="2.16.840.1.113730.3.1"}, + {.id=0x00000017, .bin_oid="0x2A864886F7140105B658", .oid_prefix="1.2.840.113556.1.5.7000"}, + {.id=0x00000018, .bin_oid="0x5515", .oid_prefix="2.5.21"}, + {.id=0x00000019, .bin_oid="0x5512", .oid_prefix="2.5.18"}, + {.id=0x0000001a, .bin_oid="0x5514", .oid_prefix="2.5.20"}, + {.id=0x0000000b, .bin_oid="0x2A864886F71401048204", .oid_prefix="1.2.840.113556.1.4.260"}, + {.id=0x0000000c, .bin_oid="0x2A864886F714010538", .oid_prefix="1.2.840.113556.1.5.56"}, + {.id=0x0000000d, .bin_oid="0x2A864886F71401048206", .oid_prefix="1.2.840.113556.1.4.262"}, + {.id=0x0000000e, .bin_oid="0x2A864886F714010539", .oid_prefix="1.2.840.113556.1.5.57"}, + {.id=0x0000000f, .bin_oid="0x2A864886F71401048207", .oid_prefix="1.2.840.113556.1.4.263"}, + {.id=0x00000010, .bin_oid="0x2A864886F71401053A", .oid_prefix="1.2.840.113556.1.5.58"}, + {.id=0x00000011, .bin_oid="0x2A864886F714010549", .oid_prefix="1.2.840.113556.1.5.73"}, + {.id=0x00000012, .bin_oid="0x2A864886F71401048231", .oid_prefix="1.2.840.113556.1.4.305"}, + {.id=0x0000001b, .bin_oid="0x2B060104018B3A6577", .oid_prefix="1.3.6.1.4.1.1466.101.119"}, + {.id=0x0000001c, .bin_oid="0x6086480186F8420302", .oid_prefix="2.16.840.1.113730.3.2"}, + {.id=0x0000001d, .bin_oid="0x2B06010401817A01", .oid_prefix="1.3.6.1.4.1.250.1"}, + {.id=0x0000001e, .bin_oid="0x2A864886F70D0109", .oid_prefix="1.2.840.113549.1.9"}, + {.id=0x0000001f, .bin_oid="0x0992268993F22C6404", .oid_prefix="0.9.2342.19200300.100.4"}, + {.id=0x00000020, .bin_oid="0x2A864886F714010617", .oid_prefix="1.2.840.113556.1.6.23"}, + {.id=0x00000021, .bin_oid="0x2A864886F71401061201", .oid_prefix="1.2.840.113556.1.6.18.1"}, + {.id=0x00000022, .bin_oid="0x2A864886F71401061202", .oid_prefix="1.2.840.113556.1.6.18.2"}, + {.id=0x00000023, .bin_oid="0x2A864886F71401060D03", .oid_prefix="1.2.840.113556.1.6.13.3"}, + {.id=0x00000024, .bin_oid="0x2A864886F71401060D04", .oid_prefix="1.2.840.113556.1.6.13.4"}, + {.id=0x00000025, .bin_oid="0x2B0601010101", .oid_prefix="1.3.6.1.1.1.1"}, + {.id=0x00000026, .bin_oid="0x2B0601010102", .oid_prefix="1.3.6.1.1.1.2"}, + {.id=0x000003ed, .bin_oid="0x2A864886F7140104B65866", .oid_prefix="1.2.840.113556.1.4.7000.102"}, + {.id=0x00000428, .bin_oid="0x2A864886F7140105B6583E", .oid_prefix="1.2.840.113556.1.5.7000.62"}, + {.id=0x0000044c, .bin_oid="0x2A864886F7140104B6586683", .oid_prefix="1.2.840.113556.1.4.7000.102:0x83"}, + {.id=0x0000044f, .bin_oid="0x2A864886F7140104B6586681", .oid_prefix="1.2.840.113556.1.4.7000.102:0x81"}, + {.id=0x0000047d, .bin_oid="0x2A864886F7140105B6583E81", .oid_prefix="1.2.840.113556.1.5.7000.62:0x81"}, + {.id=0x00000561, .bin_oid="0x2A864886F7140105B6583E83", .oid_prefix="1.2.840.113556.1.5.7000.62:0x83"}, + {.id=0x000007d1, .bin_oid="0x2A864886F71401061401", .oid_prefix="1.2.840.113556.1.6.20.1"}, + {.id=0x000007e1, .bin_oid="0x2A864886F71401061402", .oid_prefix="1.2.840.113556.1.6.20.2"}, + {.id=0x00001b86, .bin_oid="0x2A817A", .oid_prefix="1.2.250"}, + {.id=0x00001c78, .bin_oid="0x2A817A81", .oid_prefix="1.2.250:0x81"}, + {.id=0x00001c7b, .bin_oid="0x2A817A8180", .oid_prefix="1.2.250:0x8180"}, +}; + + +/** + * OID-to-ATTID mappings to be used for testing. + * An entry is marked as 'exists=true' if it exists in + * base prefixMap (_prefixmap_test_new_data) + */ +static const struct { + const char *oid; + uint32_t id; + uint32_t attid; + bool exists; +} _prefixmap_test_data[] = { + {.oid="2.5.4.0", .id=0x00000000, .attid=0x000000, .exists=true}, + {.oid="2.5.4.42", .id=0x00000000, .attid=0x00002a, .exists=true}, + {.oid="1.2.840.113556.1.2.1", .id=0x00000002, .attid=0x020001, .exists=true}, + {.oid="1.2.840.113556.1.2.13", .id=0x00000002, .attid=0x02000d, .exists=true}, + {.oid="1.2.840.113556.1.2.281", .id=0x00000002, .attid=0x020119, .exists=true}, + {.oid="1.2.840.113556.1.4.125", .id=0x00000009, .attid=0x09007d, .exists=true}, + {.oid="1.2.840.113556.1.4.146", .id=0x00000009, .attid=0x090092, .exists=true}, + {.oid="1.2.250.1", .id=0x00001b86, .attid=0x1b860001, .exists=false}, + {.oid="1.2.250.16386", .id=0x00001c78, .attid=0x1c788002, .exists=false}, + {.oid="1.2.250.2097154", .id=0x00001c7b, .attid=0x1c7b8002, .exists=false}, +}; + + +/** + * Creates dsdb_schema_prefixmap based on predefined data + */ +static WERROR _drsut_prefixmap_new(const struct drsut_pfm_oid_data *_pfm_init_data, uint32_t count, + TALLOC_CTX *mem_ctx, struct dsdb_schema_prefixmap **_pfm) +{ + uint32_t i; + struct dsdb_schema_prefixmap *pfm; + + pfm = talloc(mem_ctx, struct dsdb_schema_prefixmap); + W_ERROR_HAVE_NO_MEMORY(pfm); + + pfm->length = count; + pfm->prefixes = talloc_array(pfm, struct dsdb_schema_prefixmap_oid, pfm->length); + if (!pfm->prefixes) { + talloc_free(pfm); + return WERR_NOT_ENOUGH_MEMORY; + } + + for (i = 0; i < pfm->length; i++) { + pfm->prefixes[i].id = _pfm_init_data[i].id; + pfm->prefixes[i].bin_oid = strhex_to_data_blob(pfm, _pfm_init_data[i].bin_oid); + if (!pfm->prefixes[i].bin_oid.data) { + talloc_free(pfm); + return WERR_NOT_ENOUGH_MEMORY; + } + } + + *_pfm = pfm; + + return WERR_OK; +} + +/** + * Compares two prefixMaps for being equal - same items on same indexes + */ +static bool _torture_drs_pfm_compare_same(struct torture_context *tctx, + const struct dsdb_schema_prefixmap *pfm_left, + const struct dsdb_schema_prefixmap *pfm_right, + bool quiet) +{ + uint32_t i; + char *err_msg = NULL; + + if (pfm_left->length != pfm_right->length) { + err_msg = talloc_asprintf(tctx, "prefixMaps differ in size; left = %d, right = %d", + pfm_left->length, pfm_right->length); + goto failed; + } + + for (i = 0; i < pfm_left->length; i++) { + struct dsdb_schema_prefixmap_oid *entry_left = &pfm_left->prefixes[i]; + struct dsdb_schema_prefixmap_oid *entry_right = &pfm_right->prefixes[i]; + + if (entry_left->id != entry_right->id) { + err_msg = talloc_asprintf(tctx, "Different IDs for index=%d", i); + goto failed; + } + if (data_blob_cmp(&entry_left->bin_oid, &entry_right->bin_oid)) { + err_msg = talloc_asprintf(tctx, "Different bin_oid for index=%d", i); + goto failed; + } + } + + return true; + +failed: + if (!quiet) { + torture_comment(tctx, "_torture_drs_pfm_compare_same: %s", err_msg); + } + talloc_free(err_msg); + + return false; +} + +/* + * Tests dsdb_schema_pfm_new() + */ +static bool torture_drs_unit_pfm_new(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + bool bret; + TALLOC_CTX *mem_ctx; + struct dsdb_schema_prefixmap *pfm = NULL; + + mem_ctx = talloc_new(priv); + + /* create new prefix map */ + werr = dsdb_schema_pfm_new(mem_ctx, &pfm); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_new() failed!"); + torture_assert(tctx, pfm != NULL, "NULL prefixMap created!"); + torture_assert(tctx, pfm->length > 0, "Empty prefixMap created!"); + torture_assert(tctx, pfm->prefixes != NULL, "No prefixes for newly created prefixMap!"); + + /* compare newly created prefixMap with template one */ + bret = _torture_drs_pfm_compare_same(tctx, priv->pfm_new, pfm, false); + + talloc_free(mem_ctx); + + return bret; +} + +/** + * Tests dsdb_schema_pfm_make_attid() using full prefixMap. + * In this test we know exactly which ATTID and prefixMap->ID + * should be returned, i.e. no prefixMap entries should be added. + */ +static bool torture_drs_unit_pfm_make_attid_full_map(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i, count; + uint32_t attid; + char *err_msg; + + count = ARRAY_SIZE(_prefixmap_test_data); + for (i = 0; i < count; i++) { + werr = dsdb_schema_pfm_make_attid(priv->pfm_full, _prefixmap_test_data[i].oid, &attid); + /* prepare error message */ + err_msg = talloc_asprintf(priv, "dsdb_schema_pfm_make_attid() failed with %s", + _prefixmap_test_data[i].oid); + torture_assert(tctx, err_msg, "Unexpected: Have no memory!"); + /* verify result and returned ATTID */ + torture_assert_werr_ok(tctx, werr, err_msg); + torture_assert_int_equal(tctx, attid, _prefixmap_test_data[i].attid, err_msg); + /* reclaim memory for prepared error message */ + talloc_free(err_msg); + } + + return true; +} + +/** + * Tests dsdb_schema_pfm_make_attid() using initially small prefixMap. + * In this test we don't know exactly which ATTID and prefixMap->ID + * should be returned, but we can verify lo-word of ATTID. + * This test verifies implementation branch when a new + * prefix should be added into prefixMap. + */ +static bool torture_drs_unit_pfm_make_attid_small_map(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i, j; + uint32_t idx; + uint32_t attid, attid_2; + char *err_msg; + struct dsdb_schema_prefixmap *pfm = NULL; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + + /* create new prefix map */ + werr = dsdb_schema_pfm_new(mem_ctx, &pfm); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_new() failed!"); + + /* make some ATTIDs and check result */ + for (i = 0; i < ARRAY_SIZE(_prefixmap_test_data); i++) { + werr = dsdb_schema_pfm_make_attid(pfm, _prefixmap_test_data[i].oid, &attid); + + /* prepare error message */ + err_msg = talloc_asprintf(mem_ctx, "dsdb_schema_pfm_make_attid() failed with %s", + _prefixmap_test_data[i].oid); + torture_assert(tctx, err_msg, "Unexpected: Have no memory!"); + + /* verify result and returned ATTID */ + torture_assert_werr_ok(tctx, werr, err_msg); + /* verify ATTID lo-word */ + torture_assert_int_equal(tctx, attid & 0xFFFF, _prefixmap_test_data[i].attid & 0xFFFF, err_msg); + + /* try again, this time verify for whole ATTID */ + werr = dsdb_schema_pfm_make_attid(pfm, _prefixmap_test_data[i].oid, &attid_2); + torture_assert_werr_ok(tctx, werr, err_msg); + torture_assert_int_equal(tctx, attid_2, attid, err_msg); + + /* reclaim memory for prepared error message */ + talloc_free(err_msg); + + /* check there is such an index in modified prefixMap */ + idx = (attid >> 16); + for (j = 0; j < pfm->length; j++) { + if (pfm->prefixes[j].id == idx) + break; + } + if (j >= pfm->length) { + torture_result(tctx, TORTURE_FAIL, __location__": No prefix for ATTID=0x%08X", attid); + return false; + } + + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Tests dsdb_schema_pfm_attid_from_oid() using full prefixMap. + * In this test we know exactly which ATTID and prefixMap->ID + * should be returned- dsdb_schema_pfm_attid_from_oid() should succeed. + */ +static bool torture_drs_unit_pfm_attid_from_oid_full_map(struct torture_context *tctx, + struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i, count; + uint32_t attid; + char *err_msg; + + count = ARRAY_SIZE(_prefixmap_test_data); + for (i = 0; i < count; i++) { + werr = dsdb_schema_pfm_attid_from_oid(priv->pfm_full, + _prefixmap_test_data[i].oid, + &attid); + /* prepare error message */ + err_msg = talloc_asprintf(priv, "dsdb_schema_pfm_attid_from_oid() failed with %s", + _prefixmap_test_data[i].oid); + torture_assert(tctx, err_msg, "Unexpected: Have no memory!"); + /* verify result and returned ATTID */ + torture_assert_werr_ok(tctx, werr, err_msg); + torture_assert_int_equal(tctx, attid, _prefixmap_test_data[i].attid, err_msg); + /* reclaim memory for prepared error message */ + talloc_free(err_msg); + } + + return true; +} + +/** + * Tests dsdb_schema_pfm_attid_from_oid() using base (initial) prefixMap. + * dsdb_schema_pfm_attid_from_oid() should fail when testing with OID + * that are not already in the prefixMap. + */ +static bool torture_drs_unit_pfm_attid_from_oid_base_map(struct torture_context *tctx, + struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i; + uint32_t attid; + char *err_msg; + struct dsdb_schema_prefixmap *pfm = NULL; + struct dsdb_schema_prefixmap pfm_prev; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Unexpected: Have no memory!"); + + /* create new prefix map */ + werr = dsdb_schema_pfm_new(mem_ctx, &pfm); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_new() failed!"); + + /* keep initial pfm around for testing */ + pfm_prev = *pfm; + pfm_prev.prefixes = talloc_reference(mem_ctx, pfm->prefixes); + + /* get some ATTIDs and check result */ + for (i = 0; i < ARRAY_SIZE(_prefixmap_test_data); i++) { + werr = dsdb_schema_pfm_attid_from_oid(pfm, _prefixmap_test_data[i].oid, &attid); + + /* prepare error message */ + err_msg = talloc_asprintf(mem_ctx, + "dsdb_schema_pfm_attid_from_oid() failed for %s", + _prefixmap_test_data[i].oid); + torture_assert(tctx, err_msg, "Unexpected: Have no memory!"); + + + /* verify pfm hasn't been altered */ + if (_prefixmap_test_data[i].exists) { + /* should succeed and return valid ATTID */ + torture_assert_werr_ok(tctx, werr, err_msg); + /* verify ATTID */ + torture_assert_int_equal(tctx, + attid, _prefixmap_test_data[i].attid, + err_msg); + } else { + /* should fail */ + torture_assert_werr_equal(tctx, werr, WERR_NOT_FOUND, err_msg); + } + + /* prefixMap should never be changed */ + if (!_torture_drs_pfm_compare_same(tctx, &pfm_prev, pfm, true)) { + torture_fail(tctx, "schema->prefixmap has changed"); + } + + /* reclaim memory for prepared error message */ + talloc_free(err_msg); + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Tests dsdb_schema_pfm_oid_from_attid() using full prefixMap. + */ +static bool torture_drs_unit_pfm_oid_from_attid(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i, count; + char *err_msg; + const char *oid; + + count = ARRAY_SIZE(_prefixmap_test_data); + for (i = 0; i < count; i++) { + oid = NULL; + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, _prefixmap_test_data[i].attid, + priv, &oid); + /* prepare error message */ + err_msg = talloc_asprintf(priv, "dsdb_schema_pfm_oid_from_attid() failed with 0x%08X", + _prefixmap_test_data[i].attid); + torture_assert(tctx, err_msg, "Unexpected: Have no memory!"); + /* verify result and returned ATTID */ + torture_assert_werr_ok(tctx, werr, err_msg); + torture_assert(tctx, oid, "dsdb_schema_pfm_oid_from_attid() returned NULL OID!!!"); + torture_assert_str_equal(tctx, oid, _prefixmap_test_data[i].oid, err_msg); + /* reclaim memory for prepared error message */ + talloc_free(err_msg); + /* free memory for OID */ + talloc_free(discard_const(oid)); + } + + return true; +} + +/** + * Tests dsdb_schema_pfm_oid_from_attid() for handling + * correctly different type of attid values. + * See: MS-ADTS, 3.1.1.2.6 ATTRTYP + */ +static bool torture_drs_unit_pfm_oid_from_attid_check_attid(struct torture_context *tctx, + struct drsut_prefixmap_data *priv) +{ + WERROR werr; + const char *oid; + + /* Test with valid prefixMap attid */ + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0x00010001, tctx, &oid); + torture_assert_werr_ok(tctx, werr, "Testing prefixMap type attid = 0x00010001"); + + /* Test with valid attid but invalid index */ + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0x01110001, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_DS_NO_ATTRIBUTE_OR_VALUE, + "Testing invalid-index attid = 0x01110001"); + + /* Test with attid in msDS-IntId range */ + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0x80000000, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing msDS-IntId type attid = 0x80000000"); + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xBFFFFFFF, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing msDS-IntId type attid = 0xBFFFFFFF"); + + /* Test with attid in RESERVED range */ + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xC0000000, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing RESERVED type attid = 0xC0000000"); + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFEFFFF, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing RESERVED type attid = 0xFFFEFFFF"); + + /* Test with attid in INTERNAL range */ + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFF0000, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing INTERNAL type attid = 0xFFFF0000"); + werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFFFFFF, tctx, &oid); + torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER, + "Testing INTERNAL type attid = 0xFFFFFFFF"); + + return true; +} + +/** + * Test Schema prefixMap conversions to/from drsuapi prefixMap + * representation. + */ +static bool torture_drs_unit_pfm_to_from_drsuapi(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + struct dsdb_schema_info *schema_info; + DATA_BLOB schema_info_blob; + struct dsdb_schema_prefixmap *pfm; + struct drsuapi_DsReplicaOIDMapping_Ctr *ctr; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(tctx); + torture_assert(tctx, mem_ctx, "Unexpected: Have no memory!"); + + /* convert Schema_prefixMap to drsuapi_prefixMap */ + werr = dsdb_drsuapi_pfm_from_schema_pfm(priv->pfm_full, priv->schi_default, mem_ctx, &ctr); + torture_assert_werr_ok(tctx, werr, "dsdb_drsuapi_pfm_from_schema_pfm() failed"); + torture_assert(tctx, ctr && ctr->mappings, "drsuapi_prefixMap not constructed correctly"); + torture_assert_int_equal(tctx, ctr->num_mappings, priv->pfm_full->length + 1, + "drs_mappings count does not match"); + /* look for schema_info entry - it should be the last one */ + schema_info_blob = data_blob_const(ctr->mappings[ctr->num_mappings - 1].oid.binary_oid, + ctr->mappings[ctr->num_mappings - 1].oid.length); + werr = dsdb_schema_info_from_blob(&schema_info_blob, tctx, &schema_info); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_from_blob failed"); + torture_assert_int_equal(tctx, schema_info->revision, priv->schi_default->revision, + "schema_info (revision) not stored correctly or not last entry"); + torture_assert(tctx, GUID_equal(&schema_info->invocation_id, &priv->schi_default->invocation_id), + "schema_info (invocation_id) not stored correctly or not last entry"); + + /* compare schema_prefixMap and drsuapi_prefixMap */ + werr = dsdb_schema_pfm_contains_drsuapi_pfm(priv->pfm_full, ctr); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_contains_drsuapi_pfm() failed"); + + /* convert back drsuapi_prefixMap to schema_prefixMap */ + werr = dsdb_schema_pfm_from_drsuapi_pfm(ctr, true, mem_ctx, &pfm, &schema_info); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_from_drsuapi_pfm() failed"); + torture_assert_int_equal(tctx, schema_info->revision, priv->schi_default->revision, + "Fetched schema_info is different (revision)"); + torture_assert(tctx, GUID_equal(&schema_info->invocation_id, &priv->schi_default->invocation_id), + "Fetched schema_info is different (invocation_id)"); + + /* compare against the original */ + if (!_torture_drs_pfm_compare_same(tctx, priv->pfm_full, pfm, true)) { + talloc_free(mem_ctx); + return false; + } + + /* test conversion with partial drsuapi_prefixMap */ + ctr->num_mappings--; + werr = dsdb_schema_pfm_from_drsuapi_pfm(ctr, false, mem_ctx, &pfm, NULL); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_from_drsuapi_pfm() failed"); + /* compare against the original */ + if (!_torture_drs_pfm_compare_same(tctx, priv->pfm_full, pfm, false)) { + talloc_free(mem_ctx); + return false; + } + + talloc_free(mem_ctx); + return true; +} + + +/** + * Test Schema prefixMap conversions to/from ldb_val + * blob representation. + */ +static bool torture_drs_unit_pfm_to_from_ldb_val(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + struct dsdb_schema *schema; + struct ldb_val pfm_ldb_val; + struct ldb_val schema_info_ldb_val; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(tctx); + torture_assert(tctx, mem_ctx, "Unexpected: Have no memory!"); + + schema = dsdb_new_schema(mem_ctx); + torture_assert(tctx, schema, "Unexpected: failed to allocate schema object"); + + /* set priv->pfm_full as prefixMap for new schema object */ + schema->prefixmap = priv->pfm_full; + schema->schema_info = priv->schi_default; + + /* convert schema_prefixMap to ldb_val blob */ + werr = dsdb_get_oid_mappings_ldb(schema, mem_ctx, &pfm_ldb_val, &schema_info_ldb_val); + torture_assert_werr_ok(tctx, werr, "dsdb_get_oid_mappings_ldb() failed"); + torture_assert(tctx, pfm_ldb_val.data && pfm_ldb_val.length, + "pfm_ldb_val not constructed correctly"); + torture_assert(tctx, schema_info_ldb_val.data && schema_info_ldb_val.length, + "schema_info_ldb_val not constructed correctly"); + + /* convert pfm_ldb_val back to schema_prefixMap */ + schema->prefixmap = NULL; + schema->schema_info = NULL; + werr = dsdb_load_oid_mappings_ldb(schema, &pfm_ldb_val, &schema_info_ldb_val); + torture_assert_werr_ok(tctx, werr, "dsdb_load_oid_mappings_ldb() failed"); + /* compare against the original */ + if (!_torture_drs_pfm_compare_same(tctx, schema->prefixmap, priv->pfm_full, false)) { + talloc_free(mem_ctx); + return false; + } + torture_assert_int_equal(tctx, schema->schema_info->revision, priv->schi_default->revision, + "Fetched schema_info is different (revision)"); + torture_assert(tctx, GUID_equal(&schema->schema_info->invocation_id, &priv->schi_default->invocation_id), + "Fetched schema_info is different (invocation_id)"); + + talloc_free(mem_ctx); + return true; +} + +/** + * Test read/write in ldb implementation + */ +static bool torture_drs_unit_pfm_read_write_ldb(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + struct dsdb_schema *schema; + struct dsdb_schema_prefixmap *pfm; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(tctx); + torture_assert(tctx, mem_ctx, "Unexpected: Have no memory!"); + + /* makeup a dsdb_schema to test with */ + schema = dsdb_new_schema(mem_ctx); + torture_assert(tctx, schema, "Unexpected: failed to allocate schema object"); + /* set priv->pfm_full as prefixMap for new schema object */ + schema->prefixmap = priv->pfm_full; + schema->schema_info = priv->schi_default; + + /* write prfixMap to ldb */ + werr = dsdb_write_prefixes_from_schema_to_ldb(mem_ctx, priv->ldb_ctx, schema); + torture_assert_werr_ok(tctx, werr, "dsdb_write_prefixes_from_schema_to_ldb() failed"); + + /* read from ldb what we have written */ + werr = dsdb_read_prefixes_from_ldb(priv->ldb_ctx, mem_ctx, &pfm); + torture_assert_werr_ok(tctx, werr, "dsdb_read_prefixes_from_ldb() failed"); + + /* compare data written/read */ + if (!_torture_drs_pfm_compare_same(tctx, schema->prefixmap, priv->pfm_full, false)) { + torture_fail(tctx, "prefixMap read/write in LDB is not consistent"); + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Test dsdb_create_prefix_mapping + */ +static bool torture_drs_unit_dsdb_create_prefix_mapping(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + WERROR werr; + uint32_t i; + struct dsdb_schema *schema; + TALLOC_CTX *mem_ctx; + struct dsdb_schema_prefixmap *pfm_ldb = NULL; + + mem_ctx = talloc_new(tctx); + torture_assert(tctx, mem_ctx, "Unexpected: Have no memory!"); + + /* makeup a dsdb_schema to test with */ + schema = dsdb_new_schema(mem_ctx); + torture_assert(tctx, schema, "Unexpected: failed to allocate schema object"); + /* set priv->pfm_full as prefixMap for new schema object */ + schema->schema_info = priv->schi_default; + werr = _drsut_prefixmap_new(_prefixmap_test_new_data, ARRAY_SIZE(_prefixmap_test_new_data), + schema, &schema->prefixmap); + torture_assert_werr_ok(tctx, werr, "_drsut_prefixmap_new() failed"); + /* write prfixMap to ldb */ + werr = dsdb_write_prefixes_from_schema_to_ldb(mem_ctx, priv->ldb_ctx, schema); + torture_assert_werr_ok(tctx, werr, "dsdb_write_prefixes_from_schema_to_ldb() failed"); + + /* read from ldb what we have written */ + werr = dsdb_read_prefixes_from_ldb(priv->ldb_ctx, mem_ctx, &pfm_ldb); + torture_assert_werr_ok(tctx, werr, "dsdb_read_prefixes_from_ldb() failed"); + /* compare data written/read */ + if (!_torture_drs_pfm_compare_same(tctx, schema->prefixmap, pfm_ldb, true)) { + torture_fail(tctx, "pfm in LDB is different"); + } + TALLOC_FREE(pfm_ldb); + + for (i = 0; i < ARRAY_SIZE(_prefixmap_test_data); i++) { + struct dsdb_schema_prefixmap *pfm_prev; + struct dsdb_schema_prefixmap *pfm_new; + + pfm_prev = schema->prefixmap; + + pfm_new = dsdb_schema_pfm_copy_shallow(schema, pfm_prev); + torture_assert(tctx, pfm_new != NULL, "dsdb_schema_pfm_copy_shallow() failed"); + + if (!_prefixmap_test_data[i].exists) { + uint32_t attid; + + werr = dsdb_schema_pfm_make_attid(pfm_new, + _prefixmap_test_data[i].oid, + &attid); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_pfm_make_attid() failed"); + } + + /* call dsdb_create_prefix_mapping() and check result accordingly */ + werr = dsdb_create_prefix_mapping(priv->ldb_ctx, schema, _prefixmap_test_data[i].oid); + torture_assert_werr_ok(tctx, werr, "dsdb_create_prefix_mapping() failed"); + + /* + * The prefix should not change, only on reload + */ + torture_assert(tctx, pfm_prev == schema->prefixmap, + "schema->prefixmap has been reallocated!"); + if (!_torture_drs_pfm_compare_same(tctx, pfm_prev, schema->prefixmap, true)) { + torture_fail(tctx, "schema->prefixmap has changed"); + } + + /* read from ldb what we have written */ + werr = dsdb_read_prefixes_from_ldb(priv->ldb_ctx, mem_ctx, &pfm_ldb); + torture_assert_werr_ok(tctx, werr, "dsdb_read_prefixes_from_ldb() failed"); + /* compare data written/read */ + if (!_torture_drs_pfm_compare_same(tctx, pfm_new, pfm_ldb, true)) { + torture_fail(tctx, talloc_asprintf(tctx, "%u: pfm in LDB is different", i)); + } + /* free mem for pfm read from LDB */ + TALLOC_FREE(pfm_ldb); + + /* prepare for the next round */ + schema->prefixmap = pfm_new; + } + + talloc_free(mem_ctx); + + return true; +} + +/** + * Prepare temporary LDB and opens it + */ +static bool torture_drs_unit_ldb_setup(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + int ldb_err; + char *ldb_url; + bool bret = true; + TALLOC_CTX* mem_ctx; + char *tempdir; + NTSTATUS status; + + mem_ctx = talloc_new(priv); + + status = torture_temp_dir(tctx, "drs_", &tempdir); + torture_assert_ntstatus_ok(tctx, status, "creating temp dir"); + + ldb_url = talloc_asprintf(priv, "%s/drs_test.ldb", tempdir); + + /* create LDB */ + priv->ldb_ctx = ldb_init(priv, tctx->ev); + ldb_err = ldb_connect(priv->ldb_ctx, ldb_url, 0, NULL); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, "ldb_connect() failed"); + + /* set some schemaNamingContext */ + ldb_err = ldb_set_opaque(priv->ldb_ctx, + "schemaNamingContext", + ldb_dn_new(priv->ldb_ctx, priv->ldb_ctx, "CN=Schema,CN=Config")); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, "ldb_set_opaque() failed"); + + /* add prefixMap attribute so tested layer could work properly */ + { + struct ldb_message *msg = ldb_msg_new(mem_ctx); + msg->dn = ldb_get_schema_basedn(priv->ldb_ctx); + ldb_err = ldb_msg_add_string(msg, "prefixMap", "prefixMap"); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "ldb_msg_add_string() failed"); + + ldb_err = ldb_add(priv->ldb_ctx, msg); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, "ldb_add() failed"); + } + +DONE: + talloc_free(mem_ctx); + return bret; +} + +/* + * Setup/Teardown for test case + */ +static bool torture_drs_unit_prefixmap_setup(struct torture_context *tctx, struct drsut_prefixmap_data **_priv) +{ + WERROR werr; + DATA_BLOB blob; + struct drsut_prefixmap_data *priv; + + priv = *_priv = talloc_zero(tctx, struct drsut_prefixmap_data); + torture_assert(tctx, priv != NULL, "Not enough memory"); + + werr = _drsut_prefixmap_new(_prefixmap_test_new_data, ARRAY_SIZE(_prefixmap_test_new_data), + tctx, &priv->pfm_new); + torture_assert_werr_ok(tctx, werr, "failed to create pfm_new"); + + werr = _drsut_prefixmap_new(_prefixmap_full_map_data, ARRAY_SIZE(_prefixmap_full_map_data), + tctx, &priv->pfm_full); + torture_assert_werr_ok(tctx, werr, "failed to create pfm_test"); + + torture_assert(tctx, drsut_schemainfo_new(tctx, &priv->schi_default), + "drsut_schemainfo_new() failed"); + + werr = dsdb_blob_from_schema_info(priv->schi_default, priv, &blob); + torture_assert_werr_ok(tctx, werr, "dsdb_blob_from_schema_info() failed"); + + /* create temporary LDB and populate with data */ + if (!torture_drs_unit_ldb_setup(tctx, priv)) { + return false; + } + + return true; +} + +static bool torture_drs_unit_prefixmap_teardown(struct torture_context *tctx, struct drsut_prefixmap_data *priv) +{ + talloc_free(priv); + + return true; +} + +/** + * Test case initialization for + * drs.unit.prefixMap + */ +struct torture_tcase * torture_drs_unit_prefixmap(struct torture_suite *suite) +{ + typedef bool (*pfn_setup)(struct torture_context *, void **); + typedef bool (*pfn_teardown)(struct torture_context *, void *); + typedef bool (*pfn_run)(struct torture_context *, void *); + + struct torture_tcase * tc = torture_suite_add_tcase(suite, "prefixMap"); + + torture_tcase_set_fixture(tc, + (pfn_setup)torture_drs_unit_prefixmap_setup, + (pfn_teardown)torture_drs_unit_prefixmap_teardown); + + tc->description = talloc_strdup(tc, "Unit tests for DRSUAPI::prefixMap implementation"); + + torture_tcase_add_simple_test(tc, "new", (pfn_run)torture_drs_unit_pfm_new); + + torture_tcase_add_simple_test(tc, "make_attid_full_map", (pfn_run)torture_drs_unit_pfm_make_attid_full_map); + torture_tcase_add_simple_test(tc, "make_attid_small_map", (pfn_run)torture_drs_unit_pfm_make_attid_small_map); + + torture_tcase_add_simple_test(tc, "attid_from_oid_full_map", + (pfn_run)torture_drs_unit_pfm_attid_from_oid_full_map); + torture_tcase_add_simple_test(tc, "attid_from_oid_empty_map", + (pfn_run)torture_drs_unit_pfm_attid_from_oid_base_map); + + torture_tcase_add_simple_test(tc, "oid_from_attid_full_map", (pfn_run)torture_drs_unit_pfm_oid_from_attid); + torture_tcase_add_simple_test(tc, "oid_from_attid_check_attid", + (pfn_run)torture_drs_unit_pfm_oid_from_attid_check_attid); + + torture_tcase_add_simple_test(tc, "pfm_to_from_drsuapi", (pfn_run)torture_drs_unit_pfm_to_from_drsuapi); + + torture_tcase_add_simple_test(tc, "pfm_to_from_ldb_val", (pfn_run)torture_drs_unit_pfm_to_from_ldb_val); + + torture_tcase_add_simple_test(tc, "pfm_read_write_ldb", (pfn_run)torture_drs_unit_pfm_read_write_ldb); + + torture_tcase_add_simple_test(tc, "dsdb_create_prefix_mapping", (pfn_run)torture_drs_unit_dsdb_create_prefix_mapping); + + return tc; +} diff --git a/source4/torture/drs/unit/schemainfo_tests.c b/source4/torture/drs/unit/schemainfo_tests.c new file mode 100644 index 0000000..4b4cca6 --- /dev/null +++ b/source4/torture/drs/unit/schemainfo_tests.c @@ -0,0 +1,740 @@ +/* + Unix SMB/CIFS implementation. + + DRSUAPI schemaInfo unit tests + + Copyright (C) Kamen Mazdrashki 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "torture/smbtorture.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/samdb/ldb_modules/util.h" +#include "ldb_wrap.h" +#include +#include "torture/rpc/drsuapi.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" +#include "torture/drs/proto.h" +#include "torture/drs/proto.h" + + +/** + * schemaInfo to init ldb context with + * Rev: 0 + * GUID: 00000000-0000-0000-0000-000000000000 + */ +#define SCHEMA_INFO_INIT_STR "FF0000000000000000000000000000000000000000" + +/** + * Default schema_info string to be used for testing + * Rev: 01 + * GUID: 071c82fd-45c7-4351-a3db-51f75a630a7f + */ +#define SCHEMA_INFO_DEFAULT_STR "FF00000001FD821C07C7455143A3DB51F75A630A7F" + +/** + * Schema info data to test with + */ +struct schemainfo_data { + DATA_BLOB ndr_blob; + struct dsdb_schema_info schi; + WERROR werr_expected; + bool test_both_ways; +}; + +/** + * Schema info test data in human-readable format (... kind of) + */ +static const struct { + const char *schema_info_str; + uint32_t revision; + const char *guid_str; + WERROR werr_expected; + bool test_both_ways; +} _schemainfo_test_data[] = { + { + .schema_info_str = "FF0000000000000000000000000000000000000000", + .revision = 0, + .guid_str = "00000000-0000-0000-0000-000000000000", + .werr_expected = WERR_OK, + .test_both_ways = true + }, + { + .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_OK, + .test_both_ways = true + }, + { + .schema_info_str = "FFFFFFFFFFFD821C07C7455143A3DB51F75A630A7F", + .revision = 0xFFFFFFFF, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_OK, + .test_both_ways = true + }, + { /* len == 21 */ + .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F00", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_INVALID_PARAMETER, + .test_both_ways = false + }, + { /* marker == FF */ + .schema_info_str = "AA00000001FD821C07C7455143A3DB51F75A630A7F", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_INVALID_PARAMETER, + .test_both_ways = false + } +}; + +/** + * Private data to be shared among all test in Test case + */ +struct drsut_schemainfo_data { + struct ldb_context *ldb; + struct ldb_module *ldb_module; + struct dsdb_schema *schema; + + /* Initial schemaInfo set in ldb to test with */ + struct dsdb_schema_info *schema_info; + + uint32_t test_data_count; + struct schemainfo_data *test_data; +}; + +/** + * torture macro to assert for equal dsdb_schema_info's + */ +#define torture_assert_schema_info_equal(torture_ctx,got,expected,cmt)\ + do { const struct dsdb_schema_info *__got = (got), *__expected = (expected); \ + if (__got->revision != __expected->revision) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got".revision %d did not match "#expected".revision %d: %s", \ + (int)__got->revision, (int)__expected->revision, cmt); \ + return false; \ + } \ + if (!GUID_equal(&__got->invocation_id, &__expected->invocation_id)) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got".invocation_id did not match "#expected".invocation_id: %s", cmt); \ + return false; \ + } \ + } while(0) + +/* + * forward declaration for internal functions + */ +static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx, + struct ldb_context *ldb, + const char *schema_info_str, + bool in_setup); + + +/** + * Creates dsdb_schema_info object based on NDR data + * passed as hex string + */ +static bool _drsut_schemainfo_new(struct torture_context *tctx, + const char *schema_info_str, struct dsdb_schema_info **_si) +{ + WERROR werr; + DATA_BLOB blob; + + blob = strhex_to_data_blob(tctx, schema_info_str); + if (!blob.data) { + torture_comment(tctx, "Not enough memory!\n"); + return false; + } + + werr = dsdb_schema_info_from_blob(&blob, tctx, _si); + if (!W_ERROR_IS_OK(werr)) { + torture_comment(tctx, + "Failed to create dsdb_schema_info object for %s: %s", + schema_info_str, + win_errstr(werr)); + return false; + } + + data_blob_free(&blob); + + return true; +} + +/** + * Creates dsdb_schema_info object based on predefined data + * Function is public as it is intended to be used by other + * tests (e.g. prefixMap tests) + */ +bool drsut_schemainfo_new(struct torture_context *tctx, struct dsdb_schema_info **_si) +{ + return _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, _si); +} + + +/* + * Tests dsdb_schema_info_new() and dsdb_schema_info_blob_new() + */ +static bool test_dsdb_schema_info_new(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + WERROR werr; + DATA_BLOB ndr_blob; + DATA_BLOB ndr_blob_expected; + struct dsdb_schema_info *schi; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + ndr_blob_expected = strhex_to_data_blob(mem_ctx, SCHEMA_INFO_INIT_STR); + torture_assert(tctx, ndr_blob_expected.data, "Not enough memory!"); + + werr = dsdb_schema_info_new(mem_ctx, &schi); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_new() failed"); + torture_assert_int_equal(tctx, schi->revision, 0, + "dsdb_schema_info_new() creates schemaInfo with invalid revision"); + torture_assert(tctx, GUID_all_zero(&schi->invocation_id), + "dsdb_schema_info_new() creates schemaInfo with not ZERO GUID"); + + werr = dsdb_schema_info_blob_new(mem_ctx, &ndr_blob); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_blob_new() failed"); + torture_assert_data_blob_equal(tctx, ndr_blob, ndr_blob_expected, + "dsdb_schema_info_blob_new() returned invalid blob"); + + talloc_free(mem_ctx); + return true; +} + +/* + * Tests dsdb_schema_info_from_blob() + */ +static bool test_dsdb_schema_info_from_blob(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + uint32_t i; + WERROR werr; + char *msg; + struct dsdb_schema_info *schema_info; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + for (i = 0; i < priv->test_data_count; i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + msg = talloc_asprintf(tctx, "dsdb_schema_info_from_blob() [%d]-[%s]", + i, _schemainfo_test_data[i].schema_info_str); + + werr = dsdb_schema_info_from_blob(&data->ndr_blob, mem_ctx, &schema_info); + torture_assert_werr_equal(tctx, werr, data->werr_expected, msg); + + /* test returned data */ + if (W_ERROR_IS_OK(werr)) { + torture_assert_schema_info_equal(tctx, + schema_info, &data->schi, + "after dsdb_schema_info_from_blob() call"); + } + } + + talloc_free(mem_ctx); + + return true; +} + +/* + * Tests dsdb_blob_from_schema_info() + */ +static bool test_dsdb_blob_from_schema_info(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + uint32_t i; + WERROR werr; + char *msg; + DATA_BLOB ndr_blob; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + for (i = 0; i < priv->test_data_count; i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + /* not all test are valid reverse type of conversion */ + if (!data->test_both_ways) { + continue; + } + + msg = talloc_asprintf(tctx, "dsdb_blob_from_schema_info() [%d]-[%s]", + i, _schemainfo_test_data[i].schema_info_str); + + werr = dsdb_blob_from_schema_info(&data->schi, mem_ctx, &ndr_blob); + torture_assert_werr_equal(tctx, werr, data->werr_expected, msg); + + /* test returned data */ + if (W_ERROR_IS_OK(werr)) { + torture_assert_data_blob_equal(tctx, + ndr_blob, data->ndr_blob, + "dsdb_blob_from_schema_info()"); + } + } + + talloc_free(mem_ctx); + + return true; +} + +static bool test_dsdb_schema_info_cmp(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + DATA_BLOB blob; + struct drsuapi_DsReplicaOIDMapping_Ctr *ctr; + struct dsdb_schema_info schema_info; + + ctr = talloc_zero(priv, struct drsuapi_DsReplicaOIDMapping_Ctr); + torture_assert(tctx, ctr, "Not enough memory!"); + + /* not enough elements */ + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_INVALID_PARAMETER, + "dsdb_schema_info_cmp(): unexpected result"); + + /* an empty element for schemaInfo */ + ctr->num_mappings = 1; + ctr->mappings = talloc_zero_array(ctr, struct drsuapi_DsReplicaOIDMapping, 1); + torture_assert(tctx, ctr->mappings, "Not enough memory!"); + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_INVALID_PARAMETER, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with invalid schemaInfo - length != 21 */ + blob = strhex_to_data_blob(ctr, "FF00000001FD821C07C7455143A3DB51F75A630A7F00"); + torture_assert(tctx, blob.data, "Not enough memory!"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_INVALID_PARAMETER, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with invalid schemaInfo - marker != 0xFF */ + blob = strhex_to_data_blob(ctr, "AA00000001FD821C07C7455143A3DB51F75A630A7F"); + torture_assert(tctx, blob.data, "Not enough memory!"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_INVALID_PARAMETER, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but older one should be ok */ + blob = strhex_to_data_blob(ctr, "FF0000000000000000000000000000000000000000"); + torture_assert(tctx, blob.data, "Not enough memory!"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_OK, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with correct schemaInfo, but invalid ATTID */ + schema_info = *priv->schema->schema_info; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].id_prefix = 1; + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_INVALID_PARAMETER, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo */ + ctr->mappings[0].id_prefix = 0; + torture_assert_werr_ok(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but older revision */ + schema_info = *priv->schema->schema_info; + schema_info.revision -= 1; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_OK, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but newer revision */ + schema_info = *priv->schema->schema_info; + schema_info.revision += 1; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_DS_DRA_SCHEMA_MISMATCH, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but newer revision and other invocationId */ + schema_info = *priv->schema->schema_info; + schema_info.revision += 1; + schema_info.invocation_id.time_mid += 1; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_DS_DRA_SCHEMA_MISMATCH, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but older revision and other invocationId */ + schema_info = *priv->schema->schema_info; + schema_info.revision -= 1; + schema_info.invocation_id.time_mid += 1; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_OK, + "dsdb_schema_info_cmp(): unexpected result"); + + /* test with valid schemaInfo, but same revision and other invocationId */ + schema_info = *priv->schema->schema_info; + schema_info.invocation_id.time_mid += 1; + torture_assert_werr_ok(tctx, + dsdb_blob_from_schema_info(&schema_info, tctx, &blob), + "dsdb_blob_from_schema_info() failed"); + ctr->mappings[0].oid.length = blob.length; + ctr->mappings[0].oid.binary_oid = blob.data; + torture_assert_werr_equal(tctx, + dsdb_schema_info_cmp(priv->schema, ctr), + WERR_DS_DRA_SCHEMA_CONFLICT, + "dsdb_schema_info_cmp(): unexpected result"); + + talloc_free(ctr); + return true; +} + +/* + * Tests dsdb_module_schema_info_blob_read() + * and dsdb_module_schema_info_blob_write() + */ +static bool test_dsdb_module_schema_info_blob_rw(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int ldb_err; + DATA_BLOB blob_write; + DATA_BLOB blob_read; + + /* reset schmeInfo to know value */ + torture_assert(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false), + "_drsut_ldb_schema_info_reset() failed"); + + /* write tests' default schemaInfo */ + blob_write = strhex_to_data_blob(priv, SCHEMA_INFO_DEFAULT_STR); + torture_assert(tctx, blob_write.data, "Not enough memory!"); + + ldb_err = dsdb_module_schema_info_blob_write(priv->ldb_module, + DSDB_FLAG_TOP_MODULE, + &blob_write, NULL); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "dsdb_module_schema_info_blob_write() failed"); + + ldb_err = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE, + priv, &blob_read, NULL); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "dsdb_module_schema_info_blob_read() failed"); + + /* check if we get what we wrote */ + torture_assert_data_blob_equal(tctx, blob_read, blob_write, + "Write/Read of schemeInfo blob failed"); + + return true; +} + +/* + * Tests dsdb_schema_update_schema_info() + */ +static bool test_dsdb_module_schema_info_update(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int ldb_err; + WERROR werr; + DATA_BLOB blob; + struct dsdb_schema_info *schema_info; + + /* reset schmeInfo to know value */ + torture_assert(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false), + "_drsut_ldb_schema_info_reset() failed"); + + ldb_err = dsdb_module_schema_info_update(priv->ldb_module, + priv->schema, + DSDB_FLAG_TOP_MODULE | DSDB_FLAG_AS_SYSTEM, NULL); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "dsdb_module_schema_info_update() failed"); + + /* get updated schemaInfo */ + ldb_err = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE, + priv, &blob, NULL); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "dsdb_module_schema_info_blob_read() failed"); + + werr = dsdb_schema_info_from_blob(&blob, priv, &schema_info); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_from_blob() failed"); + + /* check against default schema_info */ + torture_assert_schema_info_equal(tctx, schema_info, priv->schema_info, + "schemaInfo attribute no updated correctly"); + + return true; +} + + +/** + * Reset schemaInfo record to know value + */ +static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx, + struct ldb_context *ldb, + const char *schema_info_str, + bool in_setup) +{ + bool bret = true; + int ldb_err; + DATA_BLOB blob; + struct ldb_message *msg; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + blob = strhex_to_data_blob(mem_ctx, schema_info_str); + torture_assert_goto(tctx, blob.data, bret, DONE, "Not enough memory!"); + + msg = ldb_msg_new(mem_ctx); + torture_assert_goto(tctx, msg, bret, DONE, "Not enough memory!"); + + msg->dn = ldb_get_schema_basedn(ldb); + ldb_err = ldb_msg_add_value(msg, "schemaInfo", &blob, NULL); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "ldb_msg_add_value() failed"); + + if (in_setup) { + ldb_err = ldb_add(ldb, msg); + } else { + ldb_err = dsdb_replace(ldb, msg, DSDB_MODIFY_PERMISSIVE); + } + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "dsdb_replace() failed"); + +DONE: + talloc_free(mem_ctx); + return bret; +} + +/** + * Prepare temporary LDB and opens it + */ +static bool _drsut_ldb_setup(struct torture_context *tctx, struct drsut_schemainfo_data *priv) +{ + int ldb_err; + char *ldb_url; + bool bret = true; + char *tempdir = NULL; + NTSTATUS status; + TALLOC_CTX* mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + status = torture_temp_dir(tctx, "drs_", &tempdir); + torture_assert_ntstatus_ok_goto(tctx, status, bret, DONE, "creating temp dir"); + + ldb_url = talloc_asprintf(priv, "%s/drs_schemainfo.ldb", tempdir); + torture_assert_goto(tctx, ldb_url, bret, DONE, "Not enough memory!"); + + /* create LDB */ + priv->ldb = ldb_wrap_connect(priv, tctx->ev, tctx->lp_ctx, + ldb_url, NULL, NULL, 0); + torture_assert_goto(tctx, priv->ldb, bret, DONE, "ldb_wrap_connect() failed"); + + /* set some schemaNamingContext */ + ldb_err = ldb_set_opaque(priv->ldb, + "schemaNamingContext", + ldb_dn_new(priv->ldb, priv->ldb, "CN=Schema,CN=Config")); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "ldb_set_opaque() failed"); + + /* add schemaInfo attribute so tested layer could work properly */ + torture_assert_goto(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, true), + bret, DONE, + "_drsut_ldb_schema_info_reset() failed"); + +DONE: + talloc_free(tempdir); + talloc_free(mem_ctx); + return bret; +} + +/* + * Setup/Teardown for test case + */ +static bool torture_drs_unit_schemainfo_setup(struct torture_context *tctx, + struct drsut_schemainfo_data **_priv) +{ + size_t i; + int ldb_err; + NTSTATUS status; + DATA_BLOB ndr_blob; + struct GUID guid; + struct drsut_schemainfo_data *priv; + + priv = talloc_zero(tctx, struct drsut_schemainfo_data); + torture_assert(tctx, priv, "Not enough memory!"); + + /* returned allocated pointer here + * teardown() will be called even in case of failure, + * so we'll get a changes to clean up */ + *_priv = priv; + + /* create initial schemaInfo */ + torture_assert(tctx, + _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, &priv->schema_info), + "Failed to create schema_info test object"); + + /* create data to test with */ + priv->test_data_count = ARRAY_SIZE(_schemainfo_test_data); + priv->test_data = talloc_array(tctx, struct schemainfo_data, priv->test_data_count); + + for (i = 0; i < ARRAY_SIZE(_schemainfo_test_data); i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + ndr_blob = strhex_to_data_blob(priv, + _schemainfo_test_data[i].schema_info_str); + torture_assert(tctx, ndr_blob.data, "Not enough memory!"); + + status = GUID_from_string(_schemainfo_test_data[i].guid_str, &guid); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, + "GUID_from_string() failed for %s", + _schemainfo_test_data[i].guid_str)); + + data->ndr_blob = ndr_blob; + data->schi.invocation_id = guid; + data->schi.revision = _schemainfo_test_data[i].revision; + data->werr_expected = _schemainfo_test_data[i].werr_expected; + data->test_both_ways = _schemainfo_test_data[i].test_both_ways; + + } + + /* create temporary LDB and populate with data */ + if (!_drsut_ldb_setup(tctx, priv)) { + return false; + } + + /* create ldb_module mockup object */ + priv->ldb_module = ldb_module_new(priv, priv->ldb, "schemaInfo_test_module", NULL); + torture_assert(tctx, priv->ldb_module, "Not enough memory!"); + + /* create schema mockup object */ + priv->schema = dsdb_new_schema(priv); + + /* set schema_info in dsdb_schema for testing */ + torture_assert(tctx, + _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, &priv->schema->schema_info), + "Failed to create schema_info test object"); + + /* pre-cache invocationId for samdb_ntds_invocation_id() + * to work with our mock ldb */ + ldb_err = ldb_set_opaque(priv->ldb, "cache.invocation_id", + &priv->schema_info->invocation_id); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "ldb_set_opaque() failed"); + + /* Perform all tests in transactions so that + * underlying modify calls not to fail */ + ldb_err = ldb_transaction_start(priv->ldb); + torture_assert_int_equal(tctx, + ldb_err, + LDB_SUCCESS, + "ldb_transaction_start() failed"); + + return true; +} + +static bool torture_drs_unit_schemainfo_teardown(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int ldb_err; + + /* commit pending transaction so we will + * be able to check what LDB state is */ + ldb_err = ldb_transaction_commit(priv->ldb); + if (ldb_err != LDB_SUCCESS) { + torture_comment(tctx, "ldb_transaction_commit() - %s (%s)", + ldb_strerror(ldb_err), + ldb_errstring(priv->ldb)); + } + + talloc_free(priv); + + return true; +} + +/** + * Test case initialization for + * drs.unit.schemaInfo + */ +struct torture_tcase * torture_drs_unit_schemainfo(struct torture_suite *suite) +{ + typedef bool (*pfn_setup)(struct torture_context *, void **); + typedef bool (*pfn_teardown)(struct torture_context *, void *); + typedef bool (*pfn_run)(struct torture_context *, void *); + + struct torture_tcase * tc = torture_suite_add_tcase(suite, "schemaInfo"); + + torture_tcase_set_fixture(tc, + (pfn_setup)torture_drs_unit_schemainfo_setup, + (pfn_teardown)torture_drs_unit_schemainfo_teardown); + + tc->description = talloc_strdup(tc, "Unit tests for DRSUAPI::schemaInfo implementation"); + + torture_tcase_add_simple_test(tc, "dsdb_schema_info_new", + (pfn_run)test_dsdb_schema_info_new); + torture_tcase_add_simple_test(tc, "dsdb_schema_info_from_blob", + (pfn_run)test_dsdb_schema_info_from_blob); + torture_tcase_add_simple_test(tc, "dsdb_blob_from_schema_info", + (pfn_run)test_dsdb_blob_from_schema_info); + torture_tcase_add_simple_test(tc, "dsdb_schema_info_cmp", + (pfn_run)test_dsdb_schema_info_cmp); + torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_blob read|write", + (pfn_run)test_dsdb_module_schema_info_blob_rw); + torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_update", + (pfn_run)test_dsdb_module_schema_info_update); + + + return tc; +} diff --git a/source4/torture/drs/wscript_build b/source4/torture/drs/wscript_build new file mode 100644 index 0000000..0dc26d6 --- /dev/null +++ b/source4/torture/drs/wscript_build @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +bld.SAMBA_MODULE('TORTURE_DRS', + source='drs_init.c drs_util.c unit/prefixmap_tests.c unit/schemainfo_tests.c rpc/dssync.c rpc/msds_intid.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_drs_init', + deps='samba-util ldb samba-errors torture ldbsamba talloc dcerpc ndr NDR_DRSUAPI gensec samba-hostconfig RPC_NDR_DRSUAPI DSDB_MODULE_HELPERS asn1util samdb NDR_DRSBLOBS samba-credentials samdb-common LIBCLI_RESOLVE LP_RESOLVE torturemain', + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c new file mode 100644 index 0000000..a832c59 --- /dev/null +++ b/source4/torture/gentest.c @@ -0,0 +1,3463 @@ +/* + Unix SMB/CIFS implementation. + + generic testing tool - version with both SMB and SMB2 support + + Copyright (C) Andrew Tridgell 2003-2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "lib/events/events.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/raw/request.h" +#include "libcli/libcli.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "librpc/gen_ndr/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "auth/credentials/credentials.h" +#include "libcli/resolve/resolve.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "dynconfig/dynconfig.h" +#include "libcli/security/security.h" +#include "libcli/raw/raw_proto.h" +#include "../libcli/smb/smbXcli_base.h" + +#define NSERVERS 2 +#define NINSTANCES 2 + +/* global options */ +static struct gentest_options { + int showall; + int analyze; + int analyze_always; + int analyze_continuous; + unsigned int max_open_handles; + unsigned int seed; + unsigned int numops; + int use_oplocks; + char **ignore_patterns; + const char *seeds_file; + int use_preset_seeds; + int fast_reconnect; + int mask_indexing; + int no_eas; + int no_acls; + int skip_cleanup; + int valid; + int smb2; +} options; + +/* mapping between open handles on the server and local handles */ +static struct { + bool active; + unsigned int instance; + struct smb2_handle smb2_handle[NSERVERS]; /* SMB2 */ + uint16_t smb_handle[NSERVERS]; /* SMB */ + const char *name; +} *open_handles; +static unsigned int num_open_handles; + +/* state information for the servers. We open NINSTANCES connections to + each server */ +static struct { + struct smb2_tree *smb2_tree[NINSTANCES]; + struct smbcli_tree *smb_tree[NINSTANCES]; + char *server_name; + char *share_name; + struct cli_credentials *credentials; +} servers[NSERVERS]; + +/* the seeds and flags for each operation */ +static struct { + unsigned int seed; + bool disabled; +} *op_parms; + + +/* oplock break info */ +static struct { + bool got_break; + struct smb2_handle smb2_handle; + uint16_t smb_handle; + uint16_t handle; + uint8_t level; + bool do_close; +} oplocks[NSERVERS][NINSTANCES]; + +/* change notify reply info */ +static struct { + int notify_count; + NTSTATUS status; + union smb_notify notify; +} notifies[NSERVERS][NINSTANCES]; + +/* info relevant to the current operation */ +static struct { + const char *name; + unsigned int seed; + NTSTATUS status; + unsigned int opnum; + TALLOC_CTX *mem_ctx; + const char *mismatch; +} current_op; + +static struct smb2_handle bad_smb2_handle; + + +#define BAD_HANDLE 0xFFFE + +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data); +static void idle_func_smb2(struct smb2_transport *transport, void *private_data); +static bool oplock_handler_smb(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private_data); +static void idle_func_smb(struct smbcli_transport *transport, void *private_data); + +/* + check if a string should be ignored. This is used as the basis + for all error ignore settings +*/ +static bool ignore_pattern(const char *str) +{ + int i; + if (!options.ignore_patterns) return false; + + for (i=0;options.ignore_patterns[i];i++) { + if (strcmp(options.ignore_patterns[i], str) == 0 || + gen_fnmatch(options.ignore_patterns[i], str) == 0) { + DEBUG(2,("Ignoring '%s'\n", str)); + return true; + } + } + return false; +} + +/***************************************************** +connect to the servers +*******************************************************/ +static bool connect_servers_fast(void) +{ + int h, i; + + /* close all open files */ + for (h=0;hsession->transport->oplock.handler = oplock_handler_smb2; + servers[i].smb2_tree[j]->session->transport->oplock.private_data = (void *)(uintptr_t)((i<<8)|j); + smb2_transport_idle_handler(servers[i].smb2_tree[j]->session->transport, + idle_func_smb2, 50000, NULL); + } else { + smbcli_oplock_handler(servers[i].smb_tree[j]->session->transport, oplock_handler_smb, + (void *)(uintptr_t)((i<<8)|j)); + smbcli_transport_idle_handler(servers[i].smb_tree[j]->session->transport, idle_func_smb, + 50000, (void *)(uintptr_t)((i<<8)|j)); + } + } + } + + return true; +} + +/* + work out the time skew between the servers - be conservative +*/ +static unsigned int time_skew(void) +{ + unsigned int ret; + NTTIME nt0, nt1; + + if (options.smb2) { + struct smbXcli_conn *c0, *c1; + + c0 = servers[0].smb2_tree[0]->session->transport->conn; + c1 = servers[1].smb2_tree[0]->session->transport->conn; + + nt0 = smbXcli_conn_server_system_time(c0); + nt1 = smbXcli_conn_server_system_time(c1); + } else { + nt0 = servers[0].smb_tree[0]->session->transport->negotiate.server_time; + nt1 = servers[1].smb_tree[0]->session->transport->negotiate.server_time; + } + /* Samba's NTTIME is unsigned, abs() won't work! */ + if (nt0 > nt1){ + ret = nt0 - nt1; + } else { + ret = nt1 - nt0; + } + return ret + 300; +} + + +static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) +{ + return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; +} + +/* + turn a server handle into a local handle +*/ +static unsigned int fnum_to_handle_smb2(int server, int instance, struct smb2_handle server_handle) +{ + unsigned int i; + for (i=0;i 0 && count++ < 10*options.max_open_handles) { + h = random() % options.max_open_handles; + if (open_handles[h].active && + open_handles[h].instance == instance) { + return h; + } + } + return BAD_HANDLE; +} + +/* + return a file handle, but skewed so we don't close the last + couple of handles too readily +*/ +static uint16_t gen_fnum_close(int instance) +{ + if (num_open_handles < 5) { + if (gen_chance(90)) return BAD_HANDLE; + } + + return gen_fnum(instance); +} + +/* + generate an integer in a specified range +*/ +static int gen_int_range(uint64_t min, uint64_t max) +{ + unsigned int r = random(); + return min + (r % (1+max-min)); +} + +/* + return a fnum for use as a root fid + be careful to call GEN_SET_FNUM() when you use this! +*/ +static uint16_t gen_root_fid(int instance) +{ + if (gen_chance(5)) return gen_fnum(instance); + return 0; +} + +/* + generate a file offset +*/ +static int gen_offset(void) +{ + if (gen_chance(20)) return 0; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); + return gen_int_range(0, 1024*1024); +} + +/* + generate a io count +*/ +static int gen_io_count(void) +{ + if (gen_chance(20)) return 0; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); + return gen_int_range(0, 4096); +} + +/* + generate a filename +*/ +static const char *gen_fname(void) +{ + const char *names[] = {"gentest\\gentest.dat", + "gentest\\foo", + "gentest\\foo2.sym", + "gentest\\foo3.dll", + "gentest\\foo4", + "gentest\\foo4:teststream1", + "gentest\\foo4:teststream2", + "gentest\\foo5.exe", + "gentest\\foo5.exe:teststream3", + "gentest\\foo5.exe:teststream4", + "gentest\\foo6.com", + "gentest\\blah", + "gentest\\blah\\blergh.txt", + "gentest\\blah\\blergh2", + "gentest\\blah\\blergh3.txt", + "gentest\\blah\\blergh4", + "gentest\\blah\\blergh5.txt", + "gentest\\blah\\blergh5", + "gentest\\blah\\.", + "gentest\\blah\\..", + "gentest\\a_very_long_name.bin", + "gentest\\x.y", + "gentest\\blah"}; + int i; + + do { + i = gen_int_range(0, ARRAY_SIZE(names)-1); + } while (ignore_pattern(names[i])); + + return names[i]; +} + +/* + generate a filename with a higher chance of choosing an already + open file +*/ +static const char *gen_fname_open(int instance) +{ + uint16_t h; + h = gen_fnum(instance); + if (h == BAD_HANDLE) { + return gen_fname(); + } + return open_handles[h].name; +} + +/* + generate a wildcard pattern +*/ +static const char *gen_pattern(void) +{ + int i; + const char *names[] = {"gentest\\*.dat", + "gentest\\*", + "gentest\\*.*", + "gentest\\blah\\*.*", + "gentest\\blah\\*", + "gentest\\?"}; + + if (gen_chance(50)) return gen_fname(); + + do { + i = gen_int_range(0, ARRAY_SIZE(names)-1); + } while (ignore_pattern(names[i])); + + return names[i]; +} + +static uint32_t gen_bits_levels(int nlevels, ...) +{ + va_list ap; + uint32_t pct; + uint32_t mask; + int i; + va_start(ap, nlevels); + for (i=0;isession->transport && + tid == servers[i].smb_tree[j]->tid) { + oplocks[i][j].got_break = true; + oplocks[i][j].smb_handle = fnum; + oplocks[i][j].handle = fnum_to_handle_smb(i, j, fnum); + oplocks[i][j].level = level; + oplocks[i][j].do_close = do_close; + tree = servers[i].smb_tree[j]; + } + } + } + + if (!tree) { + printf("Oplock break not for one of our trees!?\n"); + return false; + } + + if (!do_close) { + printf("oplock ack fnum=%d\n", fnum); + return smbcli_oplock_ack(tree, fnum, level); + } + + printf("oplock close fnum=%d\n", fnum); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.file.fnum = fnum; + io.close.in.write_time = 0; + req = smb_raw_close_send(tree, &io); + + if (req == NULL) { + printf("WARNING: close failed in oplock_handler_close\n"); + return false; + } + + req->async.fn = oplock_handler_close_recv_smb; + req->async.private_data = NULL; + + return true; +} + + +/* + the idle function tries to cope with getting an oplock break on a connection, and + an operation on another connection blocking until that break is acked + we check for operations on all transports in the idle function +*/ +static void idle_func_smb(struct smbcli_transport *transport, void *private_data) +{ + int i, j; + for (i=0;isession->transport) { + smbcli_transport_process(servers[i].smb_tree[j]->session->transport); + } + } + } + +} + +static void oplock_handler_close_recv_smb2(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_close io; + status = smb2_close_recv(req, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler\n"); + smb_panic("close failed in oplock_handler"); + } +} + +static void oplock_handler_ack_callback_smb2(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + status = smb2_break_recv(req, &br); + if (!NT_STATUS_IS_OK(status)) { + printf("oplock break ack failed in oplock_handler\n"); + smb_panic("oplock break ack failed in oplock_handler"); + } +} + +static bool send_oplock_ack_smb2(struct smb2_tree *tree, struct smb2_handle handle, + uint8_t level) +{ + struct smb2_break br; + struct smb2_request *req; + + ZERO_STRUCT(br); + br.in.file.handle = handle; + br.in.oplock_level = level; + br.in.reserved = gen_reserved8(); + br.in.reserved2 = gen_reserved32(); + + req = smb2_break_send(tree, &br); + if (req == NULL) return false; + req->async.fn = oplock_handler_ack_callback_smb2; + req->async.private_data = NULL; + return true; +} + +/* + the oplock handler will either ack the break or close the file +*/ +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + struct smb2_close io; + unsigned i, j; + bool do_close; + struct smb2_tree *tree = NULL; + struct smb2_request *req; + + srandom(current_op.seed); + do_close = gen_chance(50); + + i = ((uintptr_t)private_data) >> 8; + j = ((uintptr_t)private_data) & 0xFF; + + if (i >= NSERVERS || j >= NINSTANCES) { + printf("Bad private_data in oplock_handler\n"); + return false; + } + + oplocks[i][j].got_break = true; + oplocks[i][j].smb2_handle = *handle; + oplocks[i][j].handle = fnum_to_handle_smb2(i, j, *handle); + oplocks[i][j].level = level; + oplocks[i][j].do_close = do_close; + tree = talloc_get_type(servers[i].smb2_tree[j], struct smb2_tree); + + if (!tree) { + printf("Oplock break not for one of our trees!?\n"); + return false; + } + + if (!do_close) { + printf("oplock ack handle=%d\n", oplocks[i][j].handle); + return send_oplock_ack_smb2(tree, *handle, level); + } + + printf("oplock close fnum=%d\n", oplocks[i][j].handle); + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = 0; + req = smb2_close_send(tree, &io); + + if (req == NULL) { + printf("WARNING: close failed in oplock_handler_close\n"); + return false; + } + + req->async.fn = oplock_handler_close_recv_smb2; + req->async.private_data = NULL; + + return true; +} + + +/* + the idle function tries to cope with getting an oplock break on a connection, and + an operation on another connection blocking until that break is acked + we check for operations on all transports in the idle function +*/ +static void idle_func_smb2(struct smb2_transport *transport, void *private_data) +{ + int i, j; + for (i=0;isession->transport) { + // smb2_transport_process(servers[i].smb2_tree[j]->session->transport); + } + } + } + +} + + +/* + compare NTSTATUS, using checking ignored patterns +*/ +static bool compare_status(NTSTATUS status1, NTSTATUS status2) +{ + char *s; + + if (NT_STATUS_EQUAL(status1, status2)) return true; + + /* one code being an error and the other OK is always an error */ + if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) { + current_op.mismatch = nt_errstr(status1); + return false; + } + + /* if we are ignoring one of the status codes then consider this a match */ + if (ignore_pattern(nt_errstr(status1)) || + ignore_pattern(nt_errstr(status2))) { + return true; + } + + /* also support ignore patterns of the form NT_STATUS_XX:NT_STATUS_YY + meaning that the first server returns NT_STATUS_XX and the 2nd + returns NT_STATUS_YY */ + s = talloc_asprintf(current_op.mem_ctx, "%s:%s", + nt_errstr(status1), + nt_errstr(status2)); + if (ignore_pattern(s)) { + return true; + } + + current_op.mismatch = nt_errstr(status1); + return false; +} + +/* + check for pending packets on all connections +*/ +static void check_pending(void) +{ + int i, j; + + smb_msleep(20); + + for (j=0;jsession->transport); + } + } +} + +/* + check that the same oplock breaks have been received by all instances +*/ +static bool check_oplocks(const char *call) +{ + int i, j; + int tries = 0; + + if (!options.use_oplocks || options.smb2) { + /* no smb2 oplocks in gentest yet */ + return true; + } + +again: + check_pending(); + + for (j=0;jdacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ +} while(0) + +#define CHECK_ATTRIB(field) do { \ + if (!options.mask_indexing) { \ + CHECK_EQUAL(field); \ + } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ + (int)parm[0].field, (int)parm[1].field); \ + return false; \ + } \ +} while(0) + +#define CHECK_WSTR_EQUAL(field) do { \ + if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ + current_op.mismatch = #field; \ + printf("%s is NULL!\n", #field); \ + return false; \ + } \ + if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s - %s %s\n", #field, \ + parm[0].field.s, parm[1].field.s); \ + return false; \ + } \ + CHECK_EQUAL(field.private_length); \ +} while(0) + +#define CHECK_BLOB_EQUAL(field) do { \ + if (((parm[0].field.data == NULL && parm[1].field.data != NULL) || \ + (parm[1].field.data == NULL && parm[0].field.data != NULL) || \ + (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0)) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ + CHECK_EQUAL(field.length); \ +} while(0) + +#define CHECK_TIMES_EQUAL(field) do { \ + if (labs(parm[0].field - parm[1].field) > time_skew() && \ + !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ + (int)parm[0].field, (int)parm[1].field); \ + return false; \ + } \ +} while(0) + +#define CHECK_NTTIMES_EQUAL(field) do { \ + if (labs(nt_time_to_unix(parm[0].field) - \ + nt_time_to_unix(parm[1].field)) > time_skew() && \ + !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ + (int)nt_time_to_unix(parm[0].field), \ + (int)nt_time_to_unix(parm[1].field)); \ + return false; \ + } \ +} while(0) + + +/* + compare returned fileinfo structures +*/ +static bool cmp_fileinfo(int instance, + union smb_fileinfo parm[NSERVERS], + NTSTATUS status[NSERVERS]) +{ + int i; + enum smb_fileinfo_level level = parm[0].generic.level; + + if (level == RAW_FILEINFO_ALL_INFORMATION && + options.smb2) { + level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + } + + switch (level) { + case RAW_FILEINFO_GENERIC: + return false; + + case RAW_FILEINFO_GETATTR: + CHECK_ATTRIB(getattr.out.attrib); + CHECK_EQUAL(getattr.out.size); + CHECK_TIMES_EQUAL(getattr.out.write_time); + break; + + case RAW_FILEINFO_GETATTRE: + CHECK_TIMES_EQUAL(getattre.out.create_time); + CHECK_TIMES_EQUAL(getattre.out.access_time); + CHECK_TIMES_EQUAL(getattre.out.write_time); + CHECK_EQUAL(getattre.out.size); + CHECK_EQUAL(getattre.out.alloc_size); + CHECK_ATTRIB(getattre.out.attrib); + break; + + case RAW_FILEINFO_STANDARD: + CHECK_TIMES_EQUAL(standard.out.create_time); + CHECK_TIMES_EQUAL(standard.out.access_time); + CHECK_TIMES_EQUAL(standard.out.write_time); + CHECK_EQUAL(standard.out.size); + CHECK_EQUAL(standard.out.alloc_size); + CHECK_ATTRIB(standard.out.attrib); + break; + + case RAW_FILEINFO_EA_SIZE: + CHECK_TIMES_EQUAL(ea_size.out.create_time); + CHECK_TIMES_EQUAL(ea_size.out.access_time); + CHECK_TIMES_EQUAL(ea_size.out.write_time); + CHECK_EQUAL(ea_size.out.size); + CHECK_EQUAL(ea_size.out.alloc_size); + CHECK_ATTRIB(ea_size.out.attrib); + CHECK_EQUAL(ea_size.out.ea_size); + break; + + case RAW_FILEINFO_ALL_EAS: + CHECK_EQUAL(all_eas.out.num_eas); + for (i=0;i 0) { + parm[0].lockx.in.locks = talloc_array(current_op.mem_ctx, + struct smb_lock_entry, + nlocks); + for (n=0;ngeneric.level = levels[i].level; + + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_EA_SET: { + static struct ea_struct ea; + info->ea_set.in.num_eas = 1; + info->ea_set.in.eas = &ea; + info->ea_set.in.eas[0] = gen_ea_struct(); + } + break; + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + info->basic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); + break; + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); + break; + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); + break; + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); + break; + case RAW_SFILEINFO_RENAME_INFORMATION: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); + break; + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); + break; + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); + break; + case RAW_SFILEINFO_FULL_EA_INFORMATION: + info->full_ea_information.in.eas = gen_ea_list(); + break; + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SEC_DESC: + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_1023: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1039: + case RAW_SFILEINFO_1040: + case RAW_SFILEINFO_UNIX_INFO2: + /* Untested */ + break; + } +} +#endif + +/* + generate a fileinfo query structure +*/ +static void gen_setfileinfo(int instance, union smb_setfileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} + struct levels { + enum smb_setfileinfo_level level; + const char *name; + }; + struct levels smb_levels[] = { + LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), + LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), + LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(FULL_EA_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(PIPE_INFORMATION), LVL(VALID_DATA_INFORMATION), LVL(SHORT_NAME_INFORMATION), + LVL(1025), LVL(1027), LVL(1029), LVL(1030), LVL(1031), LVL(1032), LVL(1036), + LVL(1041), LVL(1042), LVL(1043), LVL(1044), + }; + struct levels smb2_levels[] = { + LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(FULL_EA_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(PIPE_INFORMATION), LVL(VALID_DATA_INFORMATION), LVL(SHORT_NAME_INFORMATION), + LVL(1025), LVL(1027), LVL(1029), LVL(1030), LVL(1031), LVL(1032), LVL(1036), + LVL(1041), LVL(1042), LVL(1043), LVL(1044), + }; + struct levels *levels = options.smb2?smb2_levels:smb_levels; + uint32_t num_levels = options.smb2?ARRAY_SIZE(smb2_levels):ARRAY_SIZE(smb_levels); + + do { + i = gen_int_range(0, num_levels-1); + } while (ignore_pattern(levels[i].name)); + + ZERO_STRUCTP(info); + info->generic.level = levels[i].level; + + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_EA_SET: { + static struct ea_struct ea; + info->ea_set.in.num_eas = 1; + info->ea_set.in.eas = &ea; + info->ea_set.in.eas[0] = gen_ea_struct(); + break; + } + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + info->basic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); + break; + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); + break; + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); + break; + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); + break; + case RAW_SFILEINFO_RENAME_INFORMATION: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); + break; + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); + break; + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); + break; + case RAW_SFILEINFO_FULL_EA_INFORMATION: + info->full_ea_information.in.eas = gen_ea_list(); + break; + + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SEC_DESC: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_INFO2: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_LINK_INFORMATION: + case RAW_SFILEINFO_PIPE_INFORMATION: + case RAW_SFILEINFO_VALID_DATA_INFORMATION: + case RAW_SFILEINFO_SHORT_NAME_INFORMATION: + case RAW_SFILEINFO_1027: + case RAW_SFILEINFO_1030: + case RAW_SFILEINFO_1031: + case RAW_SFILEINFO_1036: + case RAW_SFILEINFO_1041: + case RAW_SFILEINFO_1042: + case RAW_SFILEINFO_1043: + case RAW_SFILEINFO_1044: + /* Untested */ + break; + } +} + + + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo_smb(int instance, union smb_fileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD), + LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID), + LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO), + LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO), + LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION) + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; +} + +/* + generate qpathinfo operations +*/ +static bool handler_smb_qpathinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.path = gen_fname_open(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_CALL_SMB(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + +/* + generate qfileinfo operations +*/ +static bool handler_smb_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + +/* + generate setpathinfo operations +*/ +static bool handler_smb_spathinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + gen_setfileinfo(instance, &parm[0]); + parm[0].generic.in.file.path = gen_fname_open(instance); + + GEN_COPY_PARM; + + /* a special case for the fid in a RENAME */ + if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION && + parm[0].rename_information.in.root_fid != 0) { + GEN_SET_FNUM_SMB(rename_information.in.root_fid); + } + + GEN_CALL_SMB(smb_raw_setpathinfo(tree, &parm[i])); + + return true; +} + + +/* + generate setfileinfo operations +*/ +static bool handler_smb_sfileinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_setfileinfo(tree, &parm[i])); + + return true; +} + + +/* + this is called when a change notify reply comes in +*/ +static void async_notify_smb(struct smbcli_request *req) +{ + union smb_notify notify; + NTSTATUS status; + int i, j; + uint16_t tid = 0; + struct smbcli_transport *transport = req->transport; + + if (req->tree) { + tid = req->tree->tid; + } + + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); + if (NT_STATUS_IS_OK(status) && notify.nttrans.out.num_changes > 0) { + printf("notify tid=%d num_changes=%d action=%d name=%s\n", + tid, + notify.nttrans.out.num_changes, + notify.nttrans.out.changes[0].action, + notify.nttrans.out.changes[0].name.s); + } + + for (i=0;isession->transport && + tid == servers[i].smb_tree[j]->tid) { + notifies[i][j].notify_count++; + notifies[i][j].status = status; + notifies[i][j].notify = notify; + } + } + } +} + +/* + generate change notify operations +*/ +static bool handler_smb_notify(int instance) +{ + union smb_notify parm[NSERVERS]; + int n; + + ZERO_STRUCT(parm[0]); + parm[0].nttrans.level = RAW_NOTIFY_NTTRANS; + parm[0].nttrans.in.buffer_size = gen_io_count(); + parm[0].nttrans.in.completion_filter = gen_bits_mask(0xFF); + parm[0].nttrans.in.file.fnum = gen_fnum(instance); + parm[0].nttrans.in.recursive = gen_bool(); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(nttrans.in.file.fnum); + + for (n=0;nasync.fn = async_notify_smb; + } + + return true; +} + + +/* + generate ntcreatex operations +*/ +static bool handler_smb2_create(int instance) +{ + struct smb2_create parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.security_flags = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFF); + parm[0].in.oplock_level = gen_bits_levels(3, 90, 0x0, 70, 0x9, 100, 0xFF); + parm[0].in.impersonation_level = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFFFFFFFF); + parm[0].in.create_flags = gen_reserved64(); + parm[0].in.reserved = gen_reserved64(); + parm[0].in.desired_access = gen_access_mask(); + parm[0].in.file_attributes = gen_attrib(); + parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); + parm[0].in.create_disposition = gen_open_disp(); + parm[0].in.create_options = gen_create_options(); + parm[0].in.fname = gen_fname_open(instance); + parm[0].in.eas = gen_ea_list(); + parm[0].in.alloc_size = gen_alloc_size(); + parm[0].in.durable_open = gen_bool(); + parm[0].in.query_maximal_access = gen_bool(); + parm[0].in.timewarp = gen_timewarp(); + parm[0].in.query_on_disk_id = gen_bool(); + parm[0].in.sec_desc = gen_sec_desc(); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].in.oplock_level = 0; + } + + if (options.valid) { + parm[0].in.security_flags &= 3; + parm[0].in.oplock_level &= 9; + parm[0].in.impersonation_level &= 3; + } + + GEN_COPY_PARM; + GEN_CALL_SMB2(smb2_create(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.oplock_level); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.create_action); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_ATTRIB(out.file_attr); + CHECK_EQUAL(out.reserved2); + CHECK_EQUAL(out.maximal_access); + + /* ntcreatex creates a new file handle */ + ADD_HANDLE_SMB2(parm[0].in.fname, out.file.handle); + + return true; +} + +/* + generate close operations +*/ +static bool handler_smb2_close(int instance) +{ + struct smb2_close parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.file.handle.data[0] = gen_fnum_close(instance); + parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_close(tree, &parm[i])); + + CHECK_EQUAL(out.flags); + CHECK_EQUAL(out._pad); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_ATTRIB(out.file_attr); + + REMOVE_HANDLE_SMB2(in.file.handle); + + return true; +} + +/* + generate read operations +*/ +static bool handler_smb2_read(int instance) +{ + struct smb2_read parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.reserved = gen_reserved8(); + parm[0].in.length = gen_io_count(); + parm[0].in.offset = gen_offset(); + parm[0].in.min_count = gen_io_count(); + parm[0].in.channel = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.remaining = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.channel_offset = gen_bits_mask2(0x0, 0xFFFF); + parm[0].in.channel_length = gen_bits_mask2(0x0, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_read(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.remaining); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.data.length); + + return true; +} + +/* + generate write operations +*/ +static bool handler_smb2_write(int instance) +{ + struct smb2_write parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.offset = gen_offset(); + parm[0].in.unknown1 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.unknown2 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.data = data_blob_talloc(current_op.mem_ctx, NULL, + gen_io_count()); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_write(tree, &parm[i])); + + CHECK_EQUAL(out._pad); + CHECK_EQUAL(out.nwritten); + CHECK_EQUAL(out.unknown1); + + return true; +} + +/* + generate lockingx operations +*/ +static bool handler_smb2_lock(int instance) +{ + struct smb2_lock parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + int n; + + parm[0].level = RAW_LOCK_LOCKX; + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.lock_count = gen_lock_count(); + parm[0].in.lock_sequence = gen_reserved32(); + + parm[0].in.locks = talloc_array(current_op.mem_ctx, + struct smb2_lock_element, + parm[0].in.lock_count); + for (n=0;nsession->transport)); + + return true; +} + + + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo_smb2(int instance, union smb_fileinfo *info) +{ + int i; + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), + LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), LVL(SEC_DESC), + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; +} + +/* + generate qfileinfo operations +*/ +static bool handler_smb2_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.handle.data[0] = gen_fnum(instance); + + gen_fileinfo_smb2(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_getinfo_file(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + +/* + generate setfileinfo operations +*/ +static bool handler_smb2_sfileinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + gen_setfileinfo(instance, &parm[0]); + parm[0].generic.in.file.fnum = gen_fnum(instance); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_setinfo_file(tree, &parm[i])); + + return true; +} + +/* + wipe any relevant files +*/ +static void wipe_files(void) +{ + int i; + NTSTATUS status; + + if (options.skip_cleanup) { + return; + } + + for (i=0;i 0) { + printf("Deleted %d files on server %d\n", n, i); + } + } +} + +/* + dump the current seeds - useful for continuing a backtrack +*/ +static void dump_seeds(void) +{ + int i; + FILE *f; + + if (!options.seeds_file) { + return; + } + f = fopen("seeds.tmp", "w"); + if (!f) return; + + for (i=0;i 0 && base+chunk < options.numops && options.numops > 1; ) { + int i, max; + + chunk = MIN(chunk, options.numops / 2); + + /* mark this range as disabled */ + max = MIN(options.numops, base+chunk); + for (i=base;i 0); + + printf("Reduced to %d ops\n", options.numops); + ret = run_test(ev, lp_ctx); + if (ret != options.numops - 1) { + printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops); + } +} + +/* + start the main gentest process +*/ +static bool start_gentest(struct tevent_context *ev, + struct loadparm_context *lp_ctx) +{ + int op; + int ret; + + /* allocate the open_handles array */ + open_handles = calloc(options.max_open_handles, sizeof(open_handles[0])); + if (open_handles == NULL) { + printf("Unable to allocate memory for open_handles array.\n"); + exit(1); + } + + srandom(options.seed); + op_parms = calloc(options.numops, sizeof(op_parms[0])); + if (op_parms == NULL) { + printf("Unable to allocate memory for op_parms.\n"); + exit(1); + } + + /* generate the seeds - after this everything is deterministic */ + if (options.use_preset_seeds) { + int numops; + char **preset = file_lines_load(options.seeds_file, &numops, 0, NULL); + if (!preset) { + printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno)); + exit(1); + } + if (numops < options.numops) { + options.numops = numops; + } + for (op=0;op "); + + lp_ctx = samba_cmdline_get_lp_ctx(); + servers[0].credentials = cli_credentials_init(mem_ctx); + servers[1].credentials = cli_credentials_init(mem_ctx); + cli_credentials_guess(servers[0].credentials, lp_ctx); + cli_credentials_guess(servers[1].credentials, lp_ctx); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_UNCLIST: + lpcfg_set_cmdline(lp_ctx, "torture:unclist", poptGetOptArg(pc)); + break; + case OPT_USER1: + cli_credentials_parse_string(servers[0].credentials, + poptGetOptArg(pc), + CRED_SPECIFIED); + username_count++; + break; + case OPT_USER2: + cli_credentials_parse_string(servers[1].credentials, + poptGetOptArg(pc), + CRED_SPECIFIED); + username_count++; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (ignore_file) { + options.ignore_patterns = file_lines_load(ignore_file, NULL, 0, NULL); + } + + argv_new = discard_const_p(char *, poptGetArgs(pc)); + argc_new = argc; + for (i=0; i= 3)) { + usage(pc); + talloc_free(mem_ctx); + exit(1); + } + + setlinebuf(stdout); + + setup_logging("gentest", DEBUG_STDOUT); + + if (argc < 3 || argv[1][0] == '-') { + usage(pc); + talloc_free(mem_ctx); + exit(1); + } + + setup_logging(argv[0], DEBUG_STDOUT); + + for (i=0;i. +*/ + +#include "includes.h" +#include "param/param.h" +#include "param/loadparm.h" +#include "torture/smbtorture.h" +#include "lib/util/mkdir_p.h" +#include "dsdb/samdb/samdb.h" +#include "auth/session.h" +#include "lib/ldb/include/ldb.h" +#include "torture/gpo/proto.h" +#include +#include "lib/util/samba_util.h" +#include "util/tevent_ntstatus.h" + +#undef strncasecmp + +struct torture_suite *gpo_apply_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "apply"); + + torture_suite_add_simple_test(suite, "gpo_param_from_gpo", + torture_gpo_system_access_policies); + + suite->description = talloc_strdup(suite, "Group Policy apply tests"); + + return suite; +} + +static int exec_wait(struct torture_context *tctx, const char **gpo_update_cmd) +{ + NTSTATUS status; + int ret = 0; + struct tevent_req *req; + + req = samba_runcmd_send(tctx, + tctx->ev, + timeval_current_ofs(100, 0), + 2, 0, + gpo_update_cmd, + "gpo_reload_cmd", NULL); + if (req == NULL) { + return -1; + } + + if (!tevent_req_poll_ntstatus(req, tctx->ev, &status)) { + return -1; + } + if (samba_runcmd_recv(req, &ret) != 0) { + return -1; + } + return ret; +} + +static int unix2nttime(const char *sval) +{ + return (strtoll(sval, NULL, 10) * -1 / 60 / 60 / 24 / 10000000); +} + +#define GPODIR "addom.samba.example.com/Policies/"\ + "{31B2F340-016D-11D2-945F-00C04FB984F9}/MACHINE/Microsoft/"\ + "Windows NT/SecEdit" +#define GPOFILE "GptTmpl.inf" +#define GPTTMPL "[System Access]\n\ +MinimumPasswordAge = %d\n\ +MaximumPasswordAge = %d\n\ +MinimumPasswordLength = %d\n\ +PasswordComplexity = %d\n\ +" +#define GPTINI "addom.samba.example.com/Policies/"\ + "{31B2F340-016D-11D2-945F-00C04FB984F9}/GPT.INI" + +bool torture_gpo_system_access_policies(struct torture_context *tctx) +{ + TALLOC_CTX *ctx = talloc_new(tctx); + int ret, vers = 0, i; + const char *sysvol_path = NULL, *gpo_dir = NULL; + const char *gpo_file = NULL, *gpt_file = NULL; + struct ldb_context *samdb = NULL; + struct ldb_result *result; + const char *attrs[] = { + "minPwdAge", + "maxPwdAge", + "minPwdLength", + "pwdProperties", + NULL + }; + FILE *fp = NULL; + const char **gpo_update_cmd; + const char **gpo_unapply_cmd; + const char **gpo_update_force_cmd; + int minpwdcases[] = { 0, 1, 998 }; + int maxpwdcases[] = { 0, 1, 999 }; + int pwdlencases[] = { 0, 1, 14 }; + int pwdpropcases[] = { 0, 1, 1 }; + struct ldb_message *old_message = NULL; + const char **itr; + int gpo_update_len = 0; + + sysvol_path = lpcfg_path(lpcfg_service(tctx->lp_ctx, "sysvol"), + lpcfg_default_service(tctx->lp_ctx), tctx); + torture_assert(tctx, sysvol_path, "Failed to fetch the sysvol path"); + + /* Ensure the sysvol path exists */ + gpo_dir = talloc_asprintf(ctx, "%s/%s", sysvol_path, GPODIR); + mkdir_p(gpo_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + gpo_file = talloc_asprintf(ctx, "%s/%s", gpo_dir, GPOFILE); + + /* Get the gpo update command */ + gpo_update_cmd = lpcfg_gpo_update_command(tctx->lp_ctx); + torture_assert(tctx, gpo_update_cmd && gpo_update_cmd[0], + "Failed to fetch the gpo update command"); + + /* Open and read the samba db and store the initial password settings */ + samdb = samdb_connect(ctx, + tctx->ev, + tctx->lp_ctx, + system_session(tctx->lp_ctx), + NULL, + 0); + torture_assert(tctx, samdb, "Failed to connect to the samdb"); + + ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), + LDB_SCOPE_BASE, attrs, NULL); + torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, + "Searching the samdb failed"); + + old_message = result->msgs[0]; + + for (i = 0; i < 3; i++) { + /* Write out the sysvol */ + if ( (fp = fopen(gpo_file, "w")) ) { + fputs(talloc_asprintf(ctx, GPTTMPL, minpwdcases[i], + maxpwdcases[i], pwdlencases[i], + pwdpropcases[i]), fp); + fclose(fp); + } + + /* Update the version in the GPT.INI */ + gpt_file = talloc_asprintf(ctx, "%s/%s", sysvol_path, GPTINI); + if ( (fp = fopen(gpt_file, "r")) ) { + char line[256]; + while (fgets(line, 256, fp)) { + if (strncasecmp(line, "Version=", 8) == 0) { + vers = atoi(line+8); + break; + } + } + fclose(fp); + } + if ( (fp = fopen(gpt_file, "w")) ) { + char *data = talloc_asprintf(ctx, + "[General]\nVersion=%d\n", + ++vers); + fputs(data, fp); + fclose(fp); + } + + /* Run the gpo update command */ + ret = exec_wait(tctx, gpo_update_cmd); + + torture_assert(tctx, ret == 0, + "Failed to execute the gpo update command"); + ret = ldb_search(samdb, ctx, &result, + ldb_get_default_basedn(samdb), + LDB_SCOPE_BASE, attrs, NULL); + torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, + "Searching the samdb failed"); + + /* minPwdAge */ + torture_assert_int_equal(tctx, unix2nttime( + ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[0], + "")), minpwdcases[i], + "The minPwdAge was not applied"); + + /* maxPwdAge */ + torture_assert_int_equal(tctx, unix2nttime( + ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[1], + "")), maxpwdcases[i], + "The maxPwdAge was not applied"); + + /* minPwdLength */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[2], + -1), + pwdlencases[i], + "The minPwdLength was not applied"); + + /* pwdProperties */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[3], + -1), + pwdpropcases[i], + "The pwdProperties were not applied"); + } + + /* Reset settings, then verify a reapply doesn't force them back */ + for (i = 0; i < old_message->num_elements; i++) { + old_message->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + ret = ldb_modify(samdb, old_message); + torture_assert(tctx, ret == 0, "Failed to reset password settings."); + + ret = exec_wait(tctx, gpo_update_cmd); + torture_assert(tctx, ret == 0, + "Failed to execute the gpo update command"); + + /* Validate that the apply did nothing (without --force param) */ + ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), + LDB_SCOPE_BASE, attrs, NULL); + torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, + "Searching the samdb failed"); + /* minPwdAge */ + torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[0], + "")), + unix2nttime(ldb_msg_find_attr_as_string(old_message, + attrs[0], + "") + ), + "The minPwdAge was re-applied"); + /* maxPwdAge */ + torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[1], + "")), + unix2nttime(ldb_msg_find_attr_as_string(old_message, + attrs[1], + "") + ), + "The maxPwdAge was re-applied"); + /* minPwdLength */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[2], + -1), + ldb_msg_find_attr_as_int( + old_message, + attrs[2], + -2), + "The minPwdLength was re-applied"); + /* pwdProperties */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[3], + -1), + ldb_msg_find_attr_as_int( + old_message, + attrs[3], + -2), + "The pwdProperties were re-applied"); + + for (itr = gpo_update_cmd; *itr != NULL; itr++) { + gpo_update_len++; + } + + /* Run gpupdate --force and verify settings are re-applied */ + gpo_update_force_cmd = talloc_array(ctx, const char*, gpo_update_len+2); + for (i = 0; i < gpo_update_len; i++) { + gpo_update_force_cmd[i] = talloc_strdup(gpo_update_force_cmd, + gpo_update_cmd[i]); + } + gpo_update_force_cmd[i] = talloc_asprintf(gpo_update_force_cmd, + "--force"); + gpo_update_force_cmd[i+1] = NULL; + ret = exec_wait(tctx, gpo_update_force_cmd); + torture_assert(tctx, ret == 0, + "Failed to execute the gpupdate --force command"); + + ret = ldb_search(samdb, ctx, &result, + ldb_get_default_basedn(samdb), + LDB_SCOPE_BASE, attrs, NULL); + torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, + "Searching the samdb failed"); + + /* minPwdAge */ + torture_assert_int_equal(tctx, unix2nttime( + ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[0], + "")), minpwdcases[2], + "The minPwdAge was not applied"); + + /* maxPwdAge */ + torture_assert_int_equal(tctx, unix2nttime( + ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[1], + "")), maxpwdcases[2], + "The maxPwdAge was not applied"); + + /* minPwdLength */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[2], + -1), + pwdlencases[2], + "The minPwdLength was not applied"); + + /* pwdProperties */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[3], + -1), + pwdpropcases[2], + "The pwdProperties were not applied"); + + /* Unapply the settings and verify they are removed */ + gpo_unapply_cmd = talloc_array(ctx, const char*, gpo_update_len+2); + for (i = 0; i < gpo_update_len; i++) { + gpo_unapply_cmd[i] = talloc_strdup(gpo_unapply_cmd, + gpo_update_cmd[i]); + } + gpo_unapply_cmd[i] = talloc_asprintf(gpo_unapply_cmd, "--unapply"); + gpo_unapply_cmd[i+1] = NULL; + ret = exec_wait(tctx, gpo_unapply_cmd); + torture_assert(tctx, ret == 0, + "Failed to execute the gpo unapply command"); + ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), + LDB_SCOPE_BASE, attrs, NULL); + torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, + "Searching the samdb failed"); + /* minPwdAge */ + torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[0], + "")), + unix2nttime(ldb_msg_find_attr_as_string(old_message, + attrs[0], + "") + ), + "The minPwdAge was not unapplied"); + /* maxPwdAge */ + torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( + result->msgs[0], + attrs[1], + "")), + unix2nttime(ldb_msg_find_attr_as_string(old_message, + attrs[1], + "") + ), + "The maxPwdAge was not unapplied"); + /* minPwdLength */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[2], + -1), + ldb_msg_find_attr_as_int( + old_message, + attrs[2], + -2), + "The minPwdLength was not unapplied"); + /* pwdProperties */ + torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( + result->msgs[0], + attrs[3], + -1), + ldb_msg_find_attr_as_int( + old_message, + attrs[3], + -2), + "The pwdProperties were not unapplied"); + + talloc_free(ctx); + return true; +} diff --git a/source4/torture/gpo/gpo.c b/source4/torture/gpo/gpo.c new file mode 100644 index 0000000..4a06809 --- /dev/null +++ b/source4/torture/gpo/gpo.c @@ -0,0 +1,35 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) David Mulder 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/gpo/proto.h" + +NTSTATUS torture_gpo_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "gpo"); + + torture_suite_add_suite(suite, gpo_apply_suite(suite)); + + suite->description = talloc_strdup(suite, "Group Policy tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/gpo/wscript_build b/source4/torture/gpo/wscript_build new file mode 100644 index 0000000..d7b131f --- /dev/null +++ b/source4/torture/gpo/wscript_build @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +bld.SAMBA_MODULE('TORTURE_GPO', + source=''' + gpo.c + apply.c + ''', + subsystem='smbtorture', + deps='torture samba-util-core ldb', + internal_module=True, + autoproto='proto.h', + init_function='torture_gpo_init' + ) diff --git a/source4/torture/krb5/kdc-canon-heimdal.c b/source4/torture/krb5/kdc-canon-heimdal.c new file mode 100644 index 0000000..392329a --- /dev/null +++ b/source4/torture/krb5/kdc-canon-heimdal.c @@ -0,0 +1,1091 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (C) Andrew Bartlett 2005-2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "torture/smbtorture.h" +#include "torture/krb5/proto.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" +#include "auth/auth.h" +#include "auth/auth_sam_reply.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" + +#undef strcasecmp + +#define TEST_CANONICALIZE 0x0000001 +#define TEST_ENTERPRISE 0x0000002 +#define TEST_UPPER_USERNAME 0x0000008 +#define TEST_WIN2K 0x0000020 +#define TEST_UPN 0x0000040 +#define TEST_S4U2SELF 0x0000080 +#define TEST_REMOVEDOLLAR 0x0000100 +#define TEST_AS_REQ_SPN 0x0000200 +#define TEST_ALL 0x00003FF + +struct test_data { + const char *test_name; + const char *realm; + const char *real_realm; + const char *real_domain; + const char *username; + const char *real_username; + bool canonicalize; + bool enterprise; + bool upper_username; + bool win2k; + bool upn; + bool other_upn_suffix; + bool s4u2self; + bool removedollar; + bool as_req_spn; + bool spn_is_upn; + const char *krb5_service; + const char *krb5_hostname; +}; + +struct torture_krb5_context { + struct smb_krb5_context *smb_krb5_context; + struct torture_context *tctx; + struct addrinfo *server; + struct test_data *test_data; + int packet_count; +}; + +struct pac_data { + const char *principal_name; +}; + +/* + * A helper function which avoids touching the local databases to + * generate the session info, as we just want to verify the principal + * name that we found in the ticket not the full local token + */ +static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *principal_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct auth_user_info_dc *user_info_dc; + TALLOC_CTX *tmp_ctx; + struct pac_data *pac_data; + + if (pac_blob == NULL) { + DBG_ERR("pac_blob missing\n"); + return NT_STATUS_NO_IMPERSONATION_TOKEN; + } + + tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data); + + pac_data->principal_name = talloc_strdup(pac_data, principal_name); + if (!pac_data->principal_name) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx, + *pac_blob, + smb_krb5_context->krb5_context, + &user_info_dc, NULL, NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + if (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + nt_status = auth_generate_session_info(mem_ctx, + NULL, + NULL, + user_info_dc, session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* Check to see if we can pass the PAC across to the NETLOGON server for validation */ + +/* Also happens to be a really good one-step verification of our Kerberos stack */ + +static bool test_accept_ticket(struct torture_context *tctx, + struct cli_credentials *credentials, + const char *principal, + DATA_BLOB client_to_server) +{ + NTSTATUS status; + struct gensec_security *gensec_server_context; + DATA_BLOB server_to_client; + struct auth4_context *auth_context; + struct auth_session_info *session_info; + struct pac_data *pac_data; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + auth_context = talloc_zero(tmp_ctx, struct auth4_context); + torture_assert(tctx, auth_context != NULL, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_name(gensec_server_context, "krb5"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + /* Do a client-server update dance */ + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_data = talloc_get_type(auth_context->private_data, struct pac_data); + + torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context"); + torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present"); + torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name"); + return true; +} + +/* + * This function is set in torture_krb5_init_context_canon as krb5 + * send_and_recv function. This allows us to override what server the + * test is aimed at, and to inspect the packets just before they are + * sent to the network, and before they are processed on the recv + * side. + * + */ +static krb5_error_code test_krb5_send_to_realm_canon_override(struct smb_krb5_context *smb_krb5_context, + void *data, /* struct torture_krb5_context */ + krb5_const_realm realm, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + krb5_error_code k5ret; + + struct torture_krb5_context *test_context + = talloc_get_type_abort(data, struct torture_krb5_context); + + SMB_ASSERT(smb_krb5_context == test_context->smb_krb5_context); + + k5ret = smb_krb5_send_and_recv_func_forced_tcp(smb_krb5_context, + test_context->server, + timeout, + send_buf, + recv_buf); + if (k5ret != 0) { + return k5ret; + } + + test_context->packet_count++; + + return k5ret; +} + +static int test_context_destructor(struct torture_krb5_context *test_context) +{ + freeaddrinfo(test_context->server); + return 0; +} + + +static bool torture_krb5_init_context_canon(struct torture_context *tctx, + struct test_data *test_data, + struct torture_krb5_context **torture_krb5_context) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + krb5_error_code k5ret; + bool ok; + + struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context); + torture_assert(tctx, test_context != NULL, "Failed to allocate"); + + test_context->test_data = test_data; + test_context->tctx = tctx; + + k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context); + torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); + + ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST); + torture_assert(tctx, ok, "Failed to parse target server"); + + talloc_set_destructor(test_context, test_context_destructor); + + set_sockaddr_port(test_context->server->ai_addr, 88); + + k5ret = smb_krb5_set_send_to_kdc_func(test_context->smb_krb5_context, + test_krb5_send_to_realm_canon_override, + NULL, /* send_to_kdc */ + test_context); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + *torture_krb5_context = test_context; + return true; +} + + +static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data) +{ + krb5_error_code k5ret; + krb5_get_init_creds_opt *krb_options = NULL; + struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data); + krb5_principal principal; + krb5_principal krbtgt_other; + krb5_principal expected_principal; + const char *principal_string = NULL; + char *krbtgt_other_string; + int principal_flags; + const char *expected_principal_string = NULL; + char *expected_unparse_principal_string; + int expected_principal_flags; + char *got_principal_string; + char *assertion_message; + const char *password = cli_credentials_get_password( + samba_cmdline_get_creds()); + krb5_context k5_context; + struct torture_krb5_context *test_context; + bool ok; + krb5_creds my_creds; + krb5_creds *server_creds; + krb5_ccache ccache; + krb5_auth_context auth_context; + char *cc_name; + krb5_data in_data, enc_ticket; + krb5_get_creds_opt opt; + + const char *spn = NULL; + const char *spn_real_realm = NULL; + const char *upn = torture_setting_string(tctx, "krb5-upn", ""); + test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host"); + test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", ""); + + /* + * If we have not passed a UPN on the command line, + * then skip the UPN tests. + */ + if (test_data->upn && upn[0] == '\0') { + torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run"); + } + + /* + * If we have not passed a SPN on the command line, + * then skip the SPN tests. + */ + if (test_data->as_req_spn && test_data->krb5_hostname[0] == '\0') { + torture_skip(tctx, "This test needs a hostname specified as --option=torture:krb5-hostname=hostname.example.com and optionally --option=torture:krb5-service=service (defaults to host) to run"); + } + + if (test_data->removedollar && + !torture_setting_bool(tctx, "run_removedollar_test", false)) + { + torture_skip(tctx, "--option=torture:run_removedollar_test=true not specified"); + } + + test_data->realm = test_data->real_realm; + + if (test_data->upn) { + char *p; + test_data->username = talloc_strdup(test_data, upn); + p = strchr(test_data->username, '@'); + if (p) { + *p = '\0'; + p++; + } + /* + * Test the UPN behaviour carefully. We can + * test in two different modes, depending on + * what UPN has been set up for us. + * + * If the UPN is in our realm, then we do all the tests with this name also. + * + * If the UPN is not in our realm, then we + * expect the tests that replace the realm to + * fail (as it won't match) + */ + if (strcasecmp(p, test_data->real_realm) != 0) { + test_data->other_upn_suffix = true; + } else { + test_data->other_upn_suffix = false; + } + + /* + * This lets us test the combination of the UPN prefix + * with a valid domain, without adding even more + * combinations + */ + test_data->realm = p; + } + + ok = torture_krb5_init_context_canon(tctx, test_data, &test_context); + torture_assert(tctx, ok, "torture_krb5_init_context failed"); + k5_context = test_context->smb_krb5_context->krb5_context; + + test_data->realm = strupper_talloc(test_data, test_data->realm); + if (test_data->upper_username) { + test_data->username = strupper_talloc(test_data, test_data->username); + } else { + test_data->username = talloc_strdup(test_data, test_data->username); + } + + if (test_data->removedollar) { + char *p; + + p = strchr_m(test_data->username, '$'); + torture_assert(tctx, p != NULL, talloc_asprintf(tctx, + "username[%s] contains no '$'\n", + test_data->username)); + *p = '\0'; + } + + spn = talloc_asprintf(test_data, "%s/%s@%s", + test_data->krb5_service, + test_data->krb5_hostname, + test_data->realm); + + spn_real_realm = talloc_asprintf(test_data, "%s/%s@%s", + test_data->krb5_service, + test_data->krb5_hostname, + test_data->real_realm); + + if (!test_data->canonicalize && test_data->enterprise) { + torture_skip(tctx, + "This test combination " + "is skipped intentionally"); + } + + if (test_data->as_req_spn) { + if (test_data->enterprise) { + torture_skip(tctx, + "This test combination " + "is skipped intentionally"); + } + principal_string = spn; + } else { + principal_string = talloc_asprintf(test_data, + "%s@%s", + test_data->username, + test_data->realm); + + } + + test_data->spn_is_upn + = (strcasecmp(upn, spn) == 0); + + /* + * If we are set to canonicalize, we get back the fixed UPPER + * case realm, and the real username (ie matching LDAP + * samAccountName) + * + * Otherwise, if we are set to enterprise, we + * get back the whole principal as-sent + * + * Finally, if we are not set to canonicalize, we get back the + * fixed UPPER case realm, but the as-sent username + */ + if (test_data->as_req_spn && !test_data->spn_is_upn) { + expected_principal_string = spn; + } else if (test_data->canonicalize) { + expected_principal_string = talloc_asprintf(test_data, + "%s@%s", + test_data->real_username, + test_data->real_realm); + } else if (test_data->as_req_spn && test_data->spn_is_upn) { + expected_principal_string = spn_real_realm; + } else { + expected_principal_string = talloc_asprintf(test_data, + "%s@%s", + test_data->username, + test_data->real_realm); + } + + if (test_data->enterprise) { + principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE; + } else { + if (test_data->upn && test_data->other_upn_suffix) { + torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals"); + } + principal_flags = 0; + } + + if (test_data->canonicalize) { + expected_principal_flags = 0; + } else { + expected_principal_flags = principal_flags; + } + + torture_assert_int_equal(tctx, + krb5_parse_name_flags(k5_context, + principal_string, + principal_flags, + &principal), + 0, "krb5_parse_name_flags failed"); + torture_assert_int_equal(tctx, + krb5_parse_name_flags(k5_context, + expected_principal_string, + expected_principal_flags, + &expected_principal), + 0, "krb5_parse_name_flags failed"); + + if (test_data->as_req_spn) { + if (test_data->upn) { + krb5_principal_set_type(k5_context, + principal, + KRB5_NT_PRINCIPAL); + krb5_principal_set_type(k5_context, + expected_principal, + KRB5_NT_PRINCIPAL); + } else { + krb5_principal_set_type(k5_context, + principal, + KRB5_NT_SRV_HST); + krb5_principal_set_type(k5_context, + expected_principal, + KRB5_NT_SRV_HST); + } + } + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + expected_principal, + &expected_unparse_principal_string), + 0, "krb5_unparse_name failed"); + /* + * Prepare a AS-REQ and run the TEST_AS_REQ tests + * + */ + + test_context->packet_count = 0; + + /* + * Set the canonicalize flag if this test requires it + */ + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(k5_context, &krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_canonicalize(k5_context, + krb_options, + test_data->canonicalize), + 0, "krb5_get_init_creds_opt_set_canonicalize failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_win2k(k5_context, + krb_options, + test_data->win2k), + 0, "krb5_get_init_creds_opt_set_win2k failed"); + + k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal, + password, NULL, NULL, 0, + NULL, krb_options); + + if (test_context->test_data->as_req_spn + && !test_context->test_data->spn_is_upn) { + torture_assert_int_equal(tctx, k5ret, + KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, + "Got wrong error_code from " + "krb5_get_init_creds_password"); + /* We can't proceed with more checks */ + return true; + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } + + torture_assert(tctx, + test_context->packet_count > 1, + "Expected krb5_get_init_creds_password to send more packets"); + + /* + * Assert that the reply was with the correct type of + * principal, depending on the flags we set + */ + if (test_data->canonicalize == false && test_data->as_req_spn) { + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_SRV_HST, + "smb_krb5_init_context gave incorrect client->name.name_type"); + } else { + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + } + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + my_creds.client, &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_xfree(got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, + my_creds.client, expected_principal), + assertion_message); + + + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.server), KRB5_NT_SRV_INST, + "smb_krb5_init_context gave incorrect server->name.name_type"); + + torture_assert_int_equal(tctx, + krb5_principal_get_num_comp(k5_context, + my_creds.server), 2, + "smb_krb5_init_context gave incorrect number of components in my_creds.server->name"); + + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 0), + "krbtgt", + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); + + if (test_data->canonicalize) { + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 1), + test_data->real_realm, + + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); + } else { + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 1), + test_data->realm, + + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); + } + torture_assert_str_equal(tctx, + krb5_principal_get_realm(k5_context, + my_creds.server), + test_data->real_realm, + "smb_krb5_init_context gave incorrect my_creds.server->realm"); + + /* Store the result of the 'kinit' above into a memory ccache */ + cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name); + torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name, + &ccache), + 0, "krb5_cc_resolve failed"); + + torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context, + ccache, my_creds.client), + 0, "krb5_cc_initialize failed"); + + torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context, + ccache, &my_creds), + 0, "krb5_cc_store_cred failed"); + + /* + * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests + * + * This tests krb5_get_creds behaviour, which allows us to set + * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal + */ + + krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm); + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &krbtgt_other, + test_data->real_realm, "krbtgt", + test_data->real_domain, NULL), + 0, "krb5_make_principal failed"); + + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(k5_context, &opt), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_CANONICALIZE); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_NO_STORE); + + /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */ + k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds); + + { + /* + * In these situations, the code above does not store a + * principal in the credentials cache matching what + * krb5_get_creds() needs without talking to the KDC, so the + * test fails with looping detected because when we set + * canonicalize we confuse the client libs. + * + */ + assertion_message = talloc_asprintf(tctx, + "krb5_get_creds for %s should have failed with looping detected: %s", + krbtgt_other_string, + smb_get_krb5_error_message(k5_context, k5ret, + tctx)); + + torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message); + torture_assert_int_equal(tctx, + test_context->packet_count, + 2, "Expected krb5_get_creds to send packets"); + } + + /* + * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests + * + * This tests krb5_get_creds behaviour, which allows us to set + * the KRB5_GC_CANONICALIZE option + */ + + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(k5_context, &opt), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_CANONICALIZE); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_NO_STORE); + + if (test_data->s4u2self) { + torture_assert_int_equal(tctx, + krb5_get_creds_opt_set_impersonate(k5_context, + opt, + principal), + 0, "krb5_get_creds_opt_set_impersonate failed"); + } + + /* Confirm if we can get a ticket to our own name */ + k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds); + + /* + * In these situations, the code above does not store a + * principal in the credentials cache matching what + * krb5_get_creds() needs, so the test fails. + * + */ + { + assertion_message = talloc_asprintf(tctx, + "krb5_get_creds for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, + tctx)); + + /* + * Only machine accounts (strictly, accounts with a + * servicePrincipalName) can expect this test to succeed + */ + if (torture_setting_bool(tctx, "expect_machine_account", false) + && (test_data->enterprise + || test_data->spn_is_upn + || test_data->upn == false)) { + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context, + ccache, server_creds), + 0, "krb5_cc_store_cred failed"); + + torture_assert_int_equal(tctx, + krb5_free_creds(k5_context, + server_creds), + 0, "krb5_free_cred_contents failed"); + + torture_assert_int_equal(tctx, + test_context->packet_count, + 1, "Expected krb5_get_creds to send one packet"); + + } else { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, + assertion_message); + /* Account for get_cred_kdc_capath() and get_cred_kdc_referral() fallback */ + torture_assert_int_equal(tctx, + test_context->packet_count, + 2, "Expected krb5_get_creds to send 2 packets"); + } + } + + /* + * Confirm getting a ticket to pass to the server, running + * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage. + * + * This triggers the client to attempt to get a + * cross-realm ticket between the alternate names of + * the server, and we need to confirm that behaviour. + * + */ + + test_context->packet_count = 0; + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + AP_OPTS_USE_SUBKEY, + principal, + &in_data, ccache, + &enc_ticket); + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req_exact for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + /* + * Only machine accounts (strictly, accounts with a + * servicePrincipalName) can expect this test to succeed + */ + if (torture_setting_bool(tctx, "expect_machine_account", false) + && (test_data->enterprise || + (test_context->test_data->as_req_spn + || test_context->test_data->spn_is_upn) + || test_data->upn == false)) { + DATA_BLOB client_to_server; + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length); + + /* This is very weird */ + if (test_data->canonicalize == false + && test_context->test_data->as_req_spn + && test_context->test_data->spn_is_upn + && test_context->test_data->s4u2self) { + + torture_assert(tctx, + test_accept_ticket(tctx, + samba_cmdline_get_creds(), + spn_real_realm, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + } else if (test_data->canonicalize == true + && test_context->test_data->as_req_spn + && test_context->test_data->spn_is_upn + && test_context->test_data->s4u2self) { + + torture_assert(tctx, + test_accept_ticket(tctx, + samba_cmdline_get_creds(), + expected_principal_string, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + } else if (test_data->canonicalize == true + && test_data->enterprise == false + && test_context->test_data->upn + && test_context->test_data->spn_is_upn + && test_context->test_data->s4u2self) { + + torture_assert(tctx, + test_accept_ticket(tctx, + samba_cmdline_get_creds(), + expected_principal_string, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + } else if (test_data->canonicalize == false + && test_context->test_data->upn + && test_context->test_data->spn_is_upn + && test_context->test_data->s4u2self) { + + const char *accept_expected_principal_string + = talloc_asprintf(test_data, + "%s@%s", + test_data->username, + test_data->real_realm); + + torture_assert(tctx, + test_accept_ticket(tctx, + samba_cmdline_get_creds(), + accept_expected_principal_string, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + } else { + + torture_assert(tctx, + test_accept_ticket(tctx, + samba_cmdline_get_creds(), + expected_unparse_principal_string, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + } + krb5_data_free(&enc_ticket); + } else { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, + assertion_message); + } + + /* + * Confirm getting a ticket to pass to the server, running + * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage + * + * This triggers the client to attempt to get a + * cross-realm ticket between the alternate names of + * the server, and we need to confirm that behaviour. + * + */ + + if (*test_data->krb5_service && *test_data->krb5_hostname) { + krb5_principal host_principal_srv_inst; + /* + * This tries to guess when the krb5 libs will ask for a + * cross-realm ticket, and when they will just ask the KDC + * directly. + */ + test_context->packet_count = 0; + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req(k5_context, + &auth_context, + 0, + test_data->krb5_service, + test_data->krb5_hostname, + &in_data, ccache, + &enc_ticket); + + { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s/%s failed: %s", + test_data->krb5_service, + test_data->krb5_hostname, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + if (test_data->spn_is_upn == false) { + /* + * Only in these cases would the above + * code have needed to send packets to + * the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + } + + + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &host_principal_srv_inst, + test_data->real_realm, + strupper_talloc(tctx, test_data->krb5_service), + test_data->krb5_hostname, + NULL), + 0, "krb5_make_principal failed"); + + krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST); + + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + 0, + host_principal_srv_inst, + &in_data, ccache, + &enc_ticket); + krb5_free_principal(k5_context, host_principal_srv_inst); + { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s", + test_data->krb5_service, + test_data->krb5_hostname, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + + + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &host_principal_srv_inst, + test_data->real_realm, + test_data->krb5_service, + strupper_talloc(tctx, test_data->krb5_hostname), + NULL), + 0, "krb5_make_principal failed"); + + krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST); + + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + 0, + host_principal_srv_inst, + &in_data, ccache, + &enc_ticket); + krb5_free_principal(k5_context, host_principal_srv_inst); + { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s", + test_data->krb5_service, + test_data->krb5_hostname, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + } + + /* + * Confirm getting a ticket for the same krbtgt/realm that we + * got back with the initial ticket, running the + * TEST_TGS_REQ_KRBTGT stage. + * + */ + + test_context->packet_count = 0; + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + 0, + my_creds.server, + &in_data, ccache, + &enc_ticket); + + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req_exact for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + krb5_free_principal(k5_context, principal); + krb5_get_init_creds_opt_free(k5_context, krb_options); + + torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds), + 0, "krb5_free_cred_contents failed"); + + return true; +} + +struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) +{ + unsigned int i; + struct torture_suite *suite = torture_suite_create(mem_ctx, "canon"); + suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests"); + + for (i = 0; i < TEST_ALL; i++) { + char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s", + (i & TEST_CANONICALIZE) ? "canon" : "no-canon", + (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise", + (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user", + (i & TEST_WIN2K) ? "win2k" : "no-win2k", + (i & TEST_UPN) ? "upn" : + ((i & TEST_AS_REQ_SPN) ? "spn" : + ((i & TEST_REMOVEDOLLAR) ? "removedollar" : "samaccountname")), + (i & TEST_S4U2SELF) ? "s4u2self" : "normal"); + struct torture_suite *sub_suite = torture_suite_create(mem_ctx, name); + + struct test_data *test_data = talloc_zero(suite, struct test_data); + if (i & TEST_UPN) { + if (i & TEST_AS_REQ_SPN) { + continue; + } + } + if ((i & TEST_UPN) || (i & TEST_AS_REQ_SPN)) { + if (i & TEST_REMOVEDOLLAR) { + continue; + } + } + + test_data->test_name = name; + test_data->real_realm + = strupper_talloc(test_data, + cli_credentials_get_realm( + samba_cmdline_get_creds())); + test_data->real_domain = cli_credentials_get_domain( + samba_cmdline_get_creds()); + test_data->username = cli_credentials_get_username( + samba_cmdline_get_creds()); + test_data->real_username = cli_credentials_get_username( + samba_cmdline_get_creds()); + test_data->canonicalize = (i & TEST_CANONICALIZE) != 0; + test_data->enterprise = (i & TEST_ENTERPRISE) != 0; + test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0; + test_data->win2k = (i & TEST_WIN2K) != 0; + test_data->upn = (i & TEST_UPN) != 0; + test_data->s4u2self = (i & TEST_S4U2SELF) != 0; + test_data->removedollar = (i & TEST_REMOVEDOLLAR) != 0; + test_data->as_req_spn = (i & TEST_AS_REQ_SPN) != 0; + torture_suite_add_simple_tcase_const(sub_suite, name, torture_krb5_as_req_canon, + test_data); + torture_suite_add_suite(suite, sub_suite); + + } + return suite; +} diff --git a/source4/torture/krb5/kdc-heimdal.c b/source4/torture/krb5/kdc-heimdal.c new file mode 100644 index 0000000..d665977 --- /dev/null +++ b/source4/torture/krb5/kdc-heimdal.c @@ -0,0 +1,1065 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (C) Andrew Bartlett 2005-2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "torture/smbtorture.h" +#include "torture/winbind/proto.h" +#include "torture/krb5/proto.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" + +#define krb5_is_app_tag(dat,tag) \ + ((dat != NULL) && (dat)->length && \ + (((((char *)(dat)->data)[0] & ~0x20) == ((tag) | 0x40)))) + +#define krb5_is_krb_error(dat) krb5_is_app_tag(dat, 30) + +enum torture_krb5_test { + TORTURE_KRB5_TEST_PLAIN, + TORTURE_KRB5_TEST_PAC_REQUEST, + TORTURE_KRB5_TEST_BREAK_PW, + TORTURE_KRB5_TEST_CLOCK_SKEW, + TORTURE_KRB5_TEST_AES, + TORTURE_KRB5_TEST_RC4, + TORTURE_KRB5_TEST_AES_RC4, + + /* + * This is in and out of the client. + * Out refers to requests, in refers to replies + */ + TORTURE_KRB5_TEST_CHANGE_SERVER_OUT, + TORTURE_KRB5_TEST_CHANGE_SERVER_IN, + TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH, +}; + +struct torture_krb5_context { + struct torture_context *tctx; + struct addrinfo *server; + enum torture_krb5_test test; + int packet_count; + AS_REQ as_req; + AS_REP as_rep; + const char *krb5_service; + const char *krb5_hostname; +}; + +/* + * Confirm that the outgoing packet meets certain expectations. This + * should be extended to further assert the correct and expected + * behaviour of the krb5 libs, so we know what we are sending to the + * server. + * + */ + +static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, krb5_data *send_buf) +{ + size_t used; + switch (test_context->test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_PAC_REQUEST: + case TORTURE_KRB5_TEST_BREAK_PW: + case TORTURE_KRB5_TEST_CLOCK_SKEW: + case TORTURE_KRB5_TEST_AES: + case TORTURE_KRB5_TEST_RC4: + case TORTURE_KRB5_TEST_AES_RC4: + case TORTURE_KRB5_TEST_CHANGE_SERVER_IN: + torture_assert_int_equal(test_context->tctx, + decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, + "decode_AS_REQ failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); + break; + case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT: + case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH: + { + AS_REQ mod_as_req; + krb5_error_code k5ret; + krb5_data modified_send_buf; + torture_assert_int_equal(test_context->tctx, + decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, + "decode_AS_REQ failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); + + /* Only change it if configured with --option=torture:krb5-hostname= */ + if (test_context->krb5_hostname[0] == '\0') { + break; + } + + mod_as_req = test_context->as_req; + + torture_assert_int_equal(test_context->tctx, + mod_as_req.req_body.sname->name_string.len, 2, + "Sending wrong mod_as_req.req_body->sname.name_string.len"); + free(mod_as_req.req_body.sname->name_string.val[0]); + free(mod_as_req.req_body.sname->name_string.val[1]); + mod_as_req.req_body.sname->name_string.val[0] = strdup(test_context->krb5_service); + mod_as_req.req_body.sname->name_string.val[1] = strdup(test_context->krb5_hostname); + + ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf.data, modified_send_buf.length, + &mod_as_req, &used, k5ret); + torture_assert_int_equal(test_context->tctx, + k5ret, 0, + "encode_AS_REQ failed"); + + *send_buf = modified_send_buf; + break; + } + } + return true; +} + +static bool torture_check_krb5_error(struct torture_krb5_context *test_context, + const krb5_data *reply, + krb5_error_code expected_error, + bool check_pa_data) +{ + KRB_ERROR error = { 0 }; + size_t used = 0; + int rc; + + rc = decode_KRB_ERROR(reply->data, reply->length, &error, &used); + torture_assert_int_equal(test_context->tctx, + rc, 0, + "decode_KRB_ERROR failed"); + + torture_assert_int_equal(test_context->tctx, + used, reply->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + error.pvno, 5, + "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, + error.error_code, expected_error - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + + if (check_pa_data) { + METHOD_DATA m; + size_t len; + int i; + bool found_enc_ts = false; + bool found_etype_info2 = false; + torture_assert(test_context->tctx, + error.e_data != NULL, + "No e-data returned"); + + rc = decode_METHOD_DATA(error.e_data->data, + error.e_data->length, + &m, + &len); + torture_assert_int_equal(test_context->tctx, + rc, 0, + "Got invalid method data"); + + torture_assert(test_context->tctx, + m.len > 0, + "No PA_DATA given"); + for (i = 0; i < m.len; i++) { + if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) { + found_enc_ts = true; + } + else if (m.val[i].padata_type == KRB5_PADATA_ETYPE_INFO2) { + found_etype_info2 = true; + } + } + torture_assert(test_context->tctx, + found_etype_info2, + "PADATA_ETYPE_INFO2 not found"); + if (expected_error != KRB5KDC_ERR_PREAUTH_FAILED) + torture_assert(test_context->tctx, + found_enc_ts, + "Encrypted timestamp not found"); + } + + free_KRB_ERROR(&error); + + return true; +} + +static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context, + const krb5_data *reply, + const krb5_enctype* allowed_enctypes) +{ + ENCTYPE reply_enctype = { 0 }; + size_t used = 0; + int rc; + int expected_enctype = ETYPE_NULL; + + rc = decode_AS_REP(reply->data, + reply->length, + &test_context->as_rep, + &used); + torture_assert_int_equal(test_context->tctx, + rc, 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, + used, reply->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->as_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + + if (test_context->as_req.padata) { + /* + * If the AS-REQ contains a PA-ENC-TIMESTAMP, then + * that encryption type is used to determine the reply + * enctype. + */ + int i = 0; + const PA_DATA *pa = krb5_find_padata(test_context->as_req.padata->val, + test_context->as_req.padata->len, + KRB5_PADATA_ENC_TIMESTAMP, + &i); + if (pa) { + EncryptedData ed; + size_t len; + krb5_error_code ret = decode_EncryptedData(pa->padata_value.data, + pa->padata_value.length, + &ed, &len); + torture_assert_int_equal(test_context->tctx, + ret, + 0, + "decode_EncryptedData failed"); + expected_enctype = ed.etype; + free_EncryptedData(&ed); + } + } + if (expected_enctype == ETYPE_NULL) { + /* + * Otherwise, find the strongest enctype contained in + * the AS-REQ supported enctypes list. + */ + const krb5_enctype *p = NULL; + + for (p = krb5_kerberos_enctypes(NULL); *p != (krb5_enctype)ETYPE_NULL; ++p) { + int j; + + if ((*p == (krb5_enctype)ETYPE_AES256_CTS_HMAC_SHA1_96 || + *p == (krb5_enctype)ETYPE_AES128_CTS_HMAC_SHA1_96) && + !test_context->as_req.req_body.kdc_options.canonicalize) + { + /* + * AES encryption types are only used here when + * we set the canonicalize flag, as the salt + * needs to match. + */ + continue; + } + + for (j = 0; j < test_context->as_req.req_body.etype.len; ++j) { + krb5_enctype etype = test_context->as_req.req_body.etype.val[j]; + if (*p == etype) { + expected_enctype = etype; + break; + } + } + + if (expected_enctype != (krb5_enctype)ETYPE_NULL) { + break; + } + } + } + + { + /* Ensure the enctype to check against is an expected type. */ + const krb5_enctype *p = NULL; + bool found = false; + for (p = allowed_enctypes; *p != (krb5_enctype)ETYPE_NULL; ++p) { + if (*p == expected_enctype) { + found = true; + break; + } + } + + torture_assert(test_context->tctx, + found, + "Calculated enctype not in allowed list"); + } + + reply_enctype = test_context->as_rep.enc_part.etype; + torture_assert_int_equal(test_context->tctx, + reply_enctype, expected_enctype, + "Ticket encrypted with invalid algorithm"); + + return true; +} + +/* + * Confirm that the incoming packet from the KDC meets certain + * expectations. This uses a switch and the packet count to work out + * what test we are in, and where in the test we are, so we can assert + * on the expected reply packets from the KDC. + * + */ + +static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + bool ok; + + switch (test_context->test) + { + case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT: + case TORTURE_KRB5_TEST_PLAIN: + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->as_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + if (test_context->test == TORTURE_KRB5_TEST_PLAIN) { + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { + torture_assert_int_not_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + } + } + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + + /* + * Confirm correct error codes when we ask for the PAC. This behaviour is rather odd... + */ + case TORTURE_KRB5_TEST_PAC_REQUEST: + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno"); + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + + /* + * Confirm correct error codes when we deliberately send the wrong password + */ + case TORTURE_KRB5_TEST_BREAK_PW: + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if (test_context->packet_count == 1) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_FAILED, + true); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + + /* + * Confirm correct error codes when we deliberately skew the client clock + */ + case TORTURE_KRB5_TEST_CLOCK_SKEW: + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if (test_context->packet_count == 1) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KRB_AP_ERR_SKEW, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + case TORTURE_KRB5_TEST_AES: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n"); + + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if (krb5_is_krb_error(recv_buf)) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KRB_ERR_RESPONSE_TOO_BIG, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else { + const krb5_enctype allowed_enctypes[] = { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ETYPE_NULL + }; + ok = torture_check_krb5_as_rep_enctype(test_context, + recv_buf, + allowed_enctypes); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_as_rep_enctype failed"); + } + + torture_assert(test_context->tctx, + test_context->packet_count < 3, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_RC4: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n"); + + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if (krb5_is_krb_error(recv_buf)) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KRB_ERR_RESPONSE_TOO_BIG, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else { + const krb5_enctype allowed_enctypes[] = { + KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, + ETYPE_NULL + }; + ok = torture_check_krb5_as_rep_enctype(test_context, + recv_buf, + allowed_enctypes); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_as_rep_enctype failed"); + } + + torture_assert(test_context->tctx, + test_context->packet_count < 3, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_AES_RC4: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n"); + + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if (krb5_is_krb_error(recv_buf)) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KRB_ERR_RESPONSE_TOO_BIG, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else { + const krb5_enctype allowed_enctypes[] = { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, + ETYPE_NULL + }; + ok = torture_check_krb5_as_rep_enctype(test_context, + recv_buf, + allowed_enctypes); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_as_rep_enctype failed"); + } + + torture_assert(test_context->tctx, + test_context->packet_count < 3, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_CHANGE_SERVER_IN: + case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH: + { + AS_REP mod_as_rep; + krb5_error_code k5ret; + krb5_data modified_recv_buf; + if (test_context->packet_count == 0) { + ok = torture_check_krb5_error(test_context, + recv_buf, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert(test_context->tctx, + ok, + "torture_check_krb5_error failed"); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.sname.name_string.len, 2, + "Got wrong as_rep->ticket.sname.name_string.len"); + free(test_context->as_rep.ticket.sname.name_string.val[0]); + free(test_context->as_rep.ticket.sname.name_string.val[1]); + test_context->as_rep.ticket.sname.name_string.val[0] = strdup("bad"); + test_context->as_rep.ticket.sname.name_string.val[1] = strdup("mallory"); + + mod_as_rep = test_context->as_rep; + + ASN1_MALLOC_ENCODE(AS_REP, modified_recv_buf.data, modified_recv_buf.length, + &mod_as_rep, &used, k5ret); + torture_assert_int_equal(test_context->tctx, + k5ret, 0, + "encode_AS_REQ failed"); + krb5_data_free(recv_buf); + + *recv_buf = modified_recv_buf; + free_AS_REQ(&test_context->as_req); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + + break; + } + } + + + return true; +} + + +/* + * This function is set in torture_krb5_init_context as krb5 + * send_and_recv function. This allows us to override what server the + * test is aimed at, and to inspect the packets just before they are + * sent to the network, and before they are processed on the recv + * side. + * + * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test() + * functions are implement the actual tests. + * + * When this asserts, the caller will get a spurious 'cannot contact + * any KDC' message. + * + */ +static krb5_error_code test_krb5_send_to_realm_override( + struct smb_krb5_context *smb_krb5_context, + void *data, /* struct torture_krb5_context */ + krb5_const_realm realm, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + krb5_error_code k5ret; + bool ok; + krb5_data modified_send_buf = *send_buf; + + struct torture_krb5_context *test_context + = talloc_get_type_abort(data, struct torture_krb5_context); + + ok = torture_krb5_pre_send_test(test_context, &modified_send_buf); + if (ok == false) { + return EINVAL; + } + + k5ret = smb_krb5_send_and_recv_func_forced_tcp(smb_krb5_context, + test_context->server, + timeout, + &modified_send_buf, + recv_buf); + if (k5ret != 0) { + return k5ret; + } + ok = torture_krb5_post_recv_test(test_context, recv_buf); + if (ok == false) { + return EINVAL; + } + + test_context->packet_count++; + + return k5ret; +} + +static int test_context_destructor(struct torture_krb5_context *test_context) +{ + freeaddrinfo(test_context->server); + return 0; +} + + +static bool torture_krb5_init_context(struct torture_context *tctx, + enum torture_krb5_test test, + struct smb_krb5_context **smb_krb5_context) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + krb5_error_code k5ret; + bool ok; + + struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context); + torture_assert(tctx, test_context != NULL, "Failed to allocate"); + + test_context->test = test; + test_context->tctx = tctx; + + test_context->krb5_service = torture_setting_string(tctx, "krb5-service", "host"); + test_context->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", ""); + + k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); + torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); + + ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST); + torture_assert(tctx, ok, "Failed to parse target server"); + + talloc_set_destructor(test_context, test_context_destructor); + + set_sockaddr_port(test_context->server->ai_addr, 88); + + k5ret = smb_krb5_set_send_to_kdc_func((*smb_krb5_context), + test_krb5_send_to_realm_override, + NULL, /* send_to_kdc */ + test_context); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + return true; +} + +static bool torture_krb5_as_req_creds(struct torture_context *tctx, + struct cli_credentials *credentials, + enum torture_krb5_test test) +{ + krb5_error_code k5ret; + bool ok; + krb5_creds my_creds; + krb5_principal principal; + struct smb_krb5_context *smb_krb5_context; + krb5_context k5_context; + enum credentials_obtained obtained; + const char *error_string; + const char *password = cli_credentials_get_password(credentials); + const char *expected_principal_string; + krb5_get_init_creds_opt *krb_options = NULL; + const char *realm; + const char *krb5_hostname = torture_setting_string(tctx, "krb5-hostname", ""); + + + ok = torture_krb5_init_context(tctx, test, &smb_krb5_context); + torture_assert(tctx, ok, "torture_krb5_init_context failed"); + k5_context = smb_krb5_context->krb5_context; + + expected_principal_string + = cli_credentials_get_principal(credentials, + tctx); + + realm = strupper_talloc(tctx, cli_credentials_get_realm(credentials)); + k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context, + &principal, &obtained, &error_string); + torture_assert_int_equal(tctx, k5ret, 0, error_string); + + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT: + case TORTURE_KRB5_TEST_CHANGE_SERVER_IN: + case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH: + break; + + case TORTURE_KRB5_TEST_PAC_REQUEST: + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, krb_options, true), + 0, "krb5_get_init_creds_opt_set_pac_request failed"); + break; + + case TORTURE_KRB5_TEST_BREAK_PW: + password = "NOT the password"; + break; + + case TORTURE_KRB5_TEST_CLOCK_SKEW: + torture_assert_int_equal(tctx, + krb5_set_real_time(smb_krb5_context->krb5_context, time(NULL) + 3600, 0), + 0, "krb5_set_real_time failed"); + break; + + case TORTURE_KRB5_TEST_AES: { + static krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 }; + + k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + k5ret, 0, + "krb5_get_init_creds_opt_alloc failed"); + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype_list, + 1); + break; + } + case TORTURE_KRB5_TEST_RC4: { + static krb5_enctype etype_list[] = { KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 }; + + k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + k5ret, 0, + "krb5_get_init_creds_opt_alloc failed"); + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype_list, + 1); + break; + } + case TORTURE_KRB5_TEST_AES_RC4: { + static krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 }; + + k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + k5ret, 0, + "krb5_get_init_creds_opt_alloc failed"); + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype_list, + 2); + break; + } + + } /* end switch */ + + k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, + password, NULL, NULL, 0, + NULL, krb_options); + krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options); + + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_CHANGE_SERVER_IN: + case TORTURE_KRB5_TEST_PAC_REQUEST: + case TORTURE_KRB5_TEST_AES: + case TORTURE_KRB5_TEST_RC4: + case TORTURE_KRB5_TEST_AES_RC4: + { + char *got_principal_string; + char *assertion_message; + torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + my_creds.client, + &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_xfree(got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, + my_creds.client, + principal), + assertion_message); + + + torture_assert_str_equal(tctx, + my_creds.server->name.name_string.val[0], + "krbtgt", + "Mismatch in name between AS_REP and expected response, expected krbtgt"); + torture_assert_str_equal(tctx, + my_creds.server->name.name_string.val[1], + realm, + "Mismatch in realm part of krbtgt/ in AS_REP, expected krbtgt/REALM@REALM"); + + torture_assert_str_equal(tctx, + my_creds.server->realm, + realm, + "Mismatch in server realm in AS_REP, expected krbtgt/REALM@REALM"); + + break; + } + case TORTURE_KRB5_TEST_BREAK_PW: + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed"); + return true; + + case TORTURE_KRB5_TEST_CLOCK_SKEW: + torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed"); + return true; + + case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT: + case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH: + { + char *got_principal_string; + char *assertion_message; + + if (krb5_hostname[0] != '\0') { + torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_BAD_INTEGRITY, "krb5_get_init_creds_password should have failed"); + return true; + } + + torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + my_creds.client, + &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_xfree(got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, + my_creds.client, + principal), + assertion_message); + + break; + } + } + + k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed"); + + return true; +} + +static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_PLAIN); +} + +static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) +{ + if (torture_setting_bool(tctx, "expect_rodc", false)) { + torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users"); + } + return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_PAC_REQUEST); +} + +static bool torture_krb5_as_req_break_pw(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_BREAK_PW); +} + +static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_CLOCK_SKEW); +} + +static bool torture_krb5_as_req_aes(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_AES); +} + +static bool torture_krb5_as_req_rc4(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_RC4); +} + +static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_AES_RC4); +} + +/* Checking for the "Orpheus' Lyre" attack */ +static bool torture_krb5_as_req_change_server_out(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_CHANGE_SERVER_OUT); +} + +static bool torture_krb5_as_req_change_server_in(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_CHANGE_SERVER_IN); +} + +static bool torture_krb5_as_req_change_server_both(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH); +} + +NTSTATUS torture_krb5_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "krb5"); + struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc"); + suite->description = talloc_strdup(suite, "Kerberos tests"); + kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests"); + + torture_suite_add_simple_test(kdc_suite, "as-req-cmdline", + torture_krb5_as_req_cmdline); + + torture_suite_add_simple_test(kdc_suite, "as-req-pac-request", + torture_krb5_as_req_pac_request); + + torture_suite_add_simple_test(kdc_suite, "as-req-break-pw", + torture_krb5_as_req_break_pw); + + torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew", + torture_krb5_as_req_clock_skew); + + torture_suite_add_simple_test(kdc_suite, + "as-req-aes", + torture_krb5_as_req_aes); + + torture_suite_add_simple_test(kdc_suite, + "as-req-rc4", + torture_krb5_as_req_rc4); + + torture_suite_add_simple_test(kdc_suite, + "as-req-aes-rc4", + torture_krb5_as_req_aes_rc4); + + /* + * This is in and out of the client. + * Out refers to requests, in refers to replies + */ + torture_suite_add_simple_test(kdc_suite, + "as-req-change-server-in", + torture_krb5_as_req_change_server_in); + + torture_suite_add_simple_test(kdc_suite, + "as-req-change-server-out", + torture_krb5_as_req_change_server_out); + + torture_suite_add_simple_test(kdc_suite, + "as-req-change-server-both", + torture_krb5_as_req_change_server_both); + + torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite)); + torture_suite_add_suite(suite, kdc_suite); + + torture_register_suite(ctx, suite); + return NT_STATUS_OK; +} diff --git a/source4/torture/krb5/kdc-mit.c b/source4/torture/krb5/kdc-mit.c new file mode 100644 index 0000000..5085966 --- /dev/null +++ b/source4/torture/krb5/kdc-mit.c @@ -0,0 +1,795 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (c) 2016 Andreas Schneider + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "torture/smbtorture.h" +#include "torture/winbind/proto.h" +#include "torture/krb5/proto.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" + +#define krb5_is_app_tag(dat,tag) \ + ((dat != NULL) && (dat)->length && \ + ((((dat)->data[0] & ~0x20) == ((tag) | 0x40)))) + +#define krb5_is_as_req(dat) krb5_is_app_tag(dat, 10) +#define krb5_is_as_rep(dat) krb5_is_app_tag(dat, 11) +#define krb5_is_krb_error(dat) krb5_is_app_tag(dat, 30) + +enum torture_krb5_test { + TORTURE_KRB5_TEST_PLAIN, + TORTURE_KRB5_TEST_PAC_REQUEST, + TORTURE_KRB5_TEST_BREAK_PW, + TORTURE_KRB5_TEST_CLOCK_SKEW, + TORTURE_KRB5_TEST_AES, + TORTURE_KRB5_TEST_RC4, + TORTURE_KRB5_TEST_AES_RC4, +}; + +struct torture_krb5_context { + struct torture_context *tctx; + krb5_context krb5_context; + enum torture_krb5_test test; + int recv_packet_count; + krb5_kdc_req *as_req; + krb5_kdc_rep *as_rep; +}; + +krb5_error_code decode_krb5_error(const krb5_data *output, krb5_error **rep); + +krb5_error_code decode_krb5_as_req(const krb5_data *output, krb5_kdc_req **req); +krb5_error_code decode_krb5_as_rep(const krb5_data *output, krb5_kdc_rep **rep); + +krb5_error_code decode_krb5_padata_sequence(const krb5_data *output, krb5_pa_data ***rep); + +void krb5_free_kdc_req(krb5_context ctx, krb5_kdc_req *req); +void krb5_free_kdc_rep(krb5_context ctx, krb5_kdc_rep *rep); +void krb5_free_pa_data(krb5_context ctx, krb5_pa_data **data); + +static bool torture_check_krb5_as_req(struct torture_krb5_context *test_context, + krb5_context context, + const krb5_data *message) +{ + krb5_error_code code; + int nktypes; + + code = decode_krb5_as_req(message, &test_context->as_req); + torture_assert_int_equal(test_context->tctx, + code, 0, + "decode_as_req failed"); + torture_assert_int_equal(test_context->tctx, + test_context->as_req->msg_type, + KRB5_AS_REQ, + "Not a AS REQ"); + + nktypes = test_context->as_req->nktypes; + torture_assert_int_not_equal(test_context->tctx, + nktypes, 0, + "No keytypes"); + + return true; +} + +static krb5_error_code torture_krb5_pre_send_test(krb5_context context, + void *data, + const krb5_data *realm, + const krb5_data *message, + krb5_data **new_message_out, + krb5_data **new_reply_out) +{ + bool ok; + struct torture_krb5_context *test_context = + (struct torture_krb5_context *)data; + + switch (test_context->test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_PAC_REQUEST: + case TORTURE_KRB5_TEST_BREAK_PW: + case TORTURE_KRB5_TEST_CLOCK_SKEW: + case TORTURE_KRB5_TEST_AES: + case TORTURE_KRB5_TEST_RC4: + case TORTURE_KRB5_TEST_AES_RC4: + ok = torture_check_krb5_as_req(test_context, + context, + message); + if (!ok) { + return KRB5KDC_ERR_BADOPTION; + } + break; + } + + return 0; +} + +/* + * We need these function to validate packets because our torture macros + * do a 'return false' on error. + */ +static bool torture_check_krb5_error(struct torture_krb5_context *test_context, + krb5_context context, + const krb5_data *reply, + krb5_error_code error_code, + bool check_pa_data) + +{ + krb5_error *krb_error; + krb5_error_code code; + + code = decode_krb5_error(reply, &krb_error); + torture_assert_int_equal(test_context->tctx, + code, + 0, + "decode_krb5_error failed"); + + torture_assert_int_equal(test_context->tctx, + krb_error->error, + error_code - KRB5KDC_ERR_NONE, + "Got wrong error code"); + + if (check_pa_data) { + krb5_pa_data **d, **pa_data = NULL; + bool timestamp_found = false; + + torture_assert_int_not_equal(test_context->tctx, + krb_error->e_data.length, 0, + "No e-data returned"); + + code = decode_krb5_padata_sequence(&krb_error->e_data, + &pa_data); + torture_assert_int_equal(test_context->tctx, + code, + 0, + "decode_krb5_padata_sequence failed"); + + for (d = pa_data; d != NULL; d++) { + if ((*d)->pa_type == KRB5_PADATA_ENC_TIMESTAMP) { + timestamp_found = true; + break; + } + } + torture_assert(test_context->tctx, + timestamp_found, + "Encrypted timestamp not found"); + + krb5_free_pa_data(context, pa_data); + } + + krb5_free_error(context, krb_error); + + return true; +} + +static bool torture_check_krb5_as_rep(struct torture_krb5_context *test_context, + krb5_context context, + const krb5_data *reply) +{ + krb5_error_code code; + bool ok; + + code = decode_krb5_as_rep(reply, &test_context->as_rep); + torture_assert_int_equal(test_context->tctx, + code, + 0, + "decode_krb5_as_rep failed"); + + torture_assert(test_context->tctx, + test_context->as_rep->ticket->enc_part.kvno, + "No KVNO set"); + + ok = torture_setting_bool(test_context->tctx, + "expect_cached_at_rodc", + false); + if (ok) { + torture_assert_int_not_equal(test_context->tctx, + test_context->as_rep->ticket->enc_part.kvno & 0xFFFF0000, + 0, + "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + test_context->as_rep->ticket->enc_part.kvno & 0xFFFF0000, + 0, + "Unexpecedly got a RODC number in the KVNO"); + } + + return true; +} + +static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context, + krb5_context context, + const krb5_data *reply, + krb5_enctype expected_enctype) +{ + krb5_enctype reply_enctype; + bool ok; + + ok = torture_check_krb5_as_rep(test_context, + context, + reply); + if (!ok) { + return false; + } + + reply_enctype = test_context->as_rep->enc_part.enctype; + + torture_assert_int_equal(test_context->tctx, + reply_enctype, expected_enctype, + "Ticket encrypted with invalid algorithm"); + + return true; +} + +static krb5_error_code torture_krb5_post_recv_test(krb5_context context, + void *data, + krb5_error_code kdc_code, + const krb5_data *realm, + const krb5_data *message, + const krb5_data *reply, + krb5_data **new_reply_out) +{ + struct torture_krb5_context *test_context = + (struct torture_krb5_context *)data; + krb5_error_code code; + bool ok = true; + + torture_comment(test_context->tctx, + "PACKET COUNT = %d\n", + test_context->recv_packet_count); + + torture_comment(test_context->tctx, + "KRB5_AS_REP = %d\n", + krb5_is_as_req(reply)); + + torture_comment(test_context->tctx, + "KRB5_ERROR = %d\n", + krb5_is_krb_error(reply)); + + torture_comment(test_context->tctx, + "KDC ERROR CODE = %d\n", + kdc_code); + + switch (test_context->test) + { + case TORTURE_KRB5_TEST_PLAIN: + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } else { + ok = torture_check_krb5_as_rep(test_context, + context, + reply); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_as_rep failed"); + } + + torture_assert_goto(test_context->tctx, + test_context->recv_packet_count < 2, + ok, + out, + "Too many packets"); + + break; + case TORTURE_KRB5_TEST_PAC_REQUEST: + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KRB_ERR_RESPONSE_TOO_BIG, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } else if (test_context->recv_packet_count == 1) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } else if (krb5_is_krb_error(reply)) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KRB_ERR_RESPONSE_TOO_BIG, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } else { + ok = torture_check_krb5_as_rep(test_context, + context, + reply); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_as_rep failed"); + } + + torture_assert_goto(test_context->tctx, + test_context->recv_packet_count < 3, + ok, + out, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_BREAK_PW: + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + if (!ok) { + goto out; + } + } else if (test_context->recv_packet_count == 1) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_FAILED, + true); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } + + torture_assert_goto(test_context->tctx, + test_context->recv_packet_count < 2, + ok, + out, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_CLOCK_SKEW: + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + if (!ok) { + goto out; + } + } else if (test_context->recv_packet_count == 1) { + /* + * This only works if kdc_timesync 0 is set in krb5.conf + * + * See commit 5f39a4438eafd693a3eb8366bbc3901efe62e538 + * in the MIT Kerberos source tree. + */ + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KRB_AP_ERR_SKEW, + false); + torture_assert_goto(test_context->tctx, + ok, + ok, + out, + "torture_check_krb5_error failed"); + } + + torture_assert_goto(test_context->tctx, + test_context->recv_packet_count < 2, + ok, + out, + "Too many packets"); + break; + case TORTURE_KRB5_TEST_AES: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n"); + + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + if (!ok) { + goto out; + } + } else { + ok = torture_check_krb5_as_rep_enctype(test_context, + context, + reply, + ENCTYPE_AES256_CTS_HMAC_SHA1_96); + if (!ok) { + goto out; + } + } + break; + case TORTURE_KRB5_TEST_RC4: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n"); + + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + if (!ok) { + goto out; + } + } else { + ok = torture_check_krb5_as_rep_enctype(test_context, + context, + reply, + ENCTYPE_ARCFOUR_HMAC); + if (!ok) { + goto out; + } + } + break; + case TORTURE_KRB5_TEST_AES_RC4: + torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n"); + + if (test_context->recv_packet_count == 0) { + ok = torture_check_krb5_error(test_context, + context, + reply, + KRB5KDC_ERR_PREAUTH_REQUIRED, + false); + if (!ok) { + goto out; + } + } else { + ok = torture_check_krb5_as_rep_enctype(test_context, + context, + reply, + ENCTYPE_AES256_CTS_HMAC_SHA1_96); + if (!ok) { + goto out; + } + } + break; + } + + code = kdc_code; +out: + if (!ok) { + code = EINVAL; + } + + /* Cleanup */ + krb5_free_kdc_req(test_context->krb5_context, test_context->as_req); + krb5_free_kdc_rep(test_context->krb5_context, test_context->as_rep); + + test_context->recv_packet_count++; + + return code; +} + +static bool torture_krb5_init_context(struct torture_context *tctx, + enum torture_krb5_test test, + struct smb_krb5_context **smb_krb5_context) +{ + krb5_error_code code; + + struct torture_krb5_context *test_context = talloc_zero(tctx, + struct torture_krb5_context); + torture_assert(tctx, test_context != NULL, "Failed to allocate"); + + test_context->test = test; + test_context->tctx = tctx; + + code = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); + torture_assert_int_equal(tctx, code, 0, "smb_krb5_init_context failed"); + + test_context->krb5_context = (*smb_krb5_context)->krb5_context; + + krb5_set_kdc_send_hook((*smb_krb5_context)->krb5_context, + torture_krb5_pre_send_test, + test_context); + + krb5_set_kdc_recv_hook((*smb_krb5_context)->krb5_context, + torture_krb5_post_recv_test, + test_context); + + return true; +} +static bool torture_krb5_as_req_creds(struct torture_context *tctx, + struct cli_credentials *credentials, + enum torture_krb5_test test) +{ + krb5_get_init_creds_opt *krb_options = NULL; + struct smb_krb5_context *smb_krb5_context; + enum credentials_obtained obtained; + const char *error_string; + const char *password; + krb5_principal principal; + krb5_error_code code; + krb5_creds my_creds; + bool ok; + + ok = torture_krb5_init_context(tctx, test, &smb_krb5_context); + torture_assert(tctx, ok, "torture_krb5_init_context failed"); + + code = principal_from_credentials(tctx, + credentials, + smb_krb5_context, + &principal, + &obtained, + &error_string); + torture_assert_int_equal(tctx, code, 0, error_string); + + password = cli_credentials_get_password(credentials); + + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + break; + case TORTURE_KRB5_TEST_PAC_REQUEST: +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST + code = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + code, 0, + "krb5_get_init_creds_opt_alloc failed"); + + code = krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, + krb_options, + 1); + torture_assert_int_equal(tctx, + code, 0, + "krb5_get_init_creds_opt_set_pac_request failed"); +#endif + break; + case TORTURE_KRB5_TEST_BREAK_PW: + password = "NOT the password"; + break; + case TORTURE_KRB5_TEST_CLOCK_SKEW: + code = krb5_set_real_time(smb_krb5_context->krb5_context, + time(NULL) + 3600, + 0); + torture_assert_int_equal(tctx, + code, 0, + "krb5_set_real_time failed"); + break; + case TORTURE_KRB5_TEST_AES: { + krb5_enctype etype[] = { ENCTYPE_AES256_CTS_HMAC_SHA1_96 }; + + code = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + code, 0, + "krb5_get_init_creds_opt_alloc failed"); + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype, + 1); + break; + } + case TORTURE_KRB5_TEST_RC4: { + krb5_enctype etype[] = { ENCTYPE_ARCFOUR_HMAC }; + + code = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + code, 0, + "krb5_get_init_creds_opt_alloc failed"); + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype, + 1); + break; + } + case TORTURE_KRB5_TEST_AES_RC4: { + krb5_enctype etype[] = { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_ARCFOUR_HMAC }; + + code = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, + &krb_options); + torture_assert_int_equal(tctx, + code, 0, + "krb5_get_init_creds_opt_alloc failed"); + + + krb5_get_init_creds_opt_set_etype_list(krb_options, + etype, + 2); + break; + } + } + + code = krb5_get_init_creds_password(smb_krb5_context->krb5_context, + &my_creds, + principal, + password, + NULL, + NULL, + 0, + NULL, + krb_options); + krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, + krb_options); + + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_PAC_REQUEST: + case TORTURE_KRB5_TEST_AES: + case TORTURE_KRB5_TEST_RC4: + case TORTURE_KRB5_TEST_AES_RC4: + torture_assert_int_equal(tctx, + code, + 0, + "krb5_get_init_creds_password failed"); + break; + case TORTURE_KRB5_TEST_BREAK_PW: + torture_assert_int_equal(tctx, + code, + KRB5KDC_ERR_PREAUTH_FAILED, + "krb5_get_init_creds_password should " + "have failed"); + return true; + case TORTURE_KRB5_TEST_CLOCK_SKEW: + torture_assert_int_equal(tctx, + code, + KRB5KRB_AP_ERR_SKEW, + "krb5_get_init_creds_password should " + "have failed"); + return true; + } + + krb5_free_cred_contents(smb_krb5_context->krb5_context, + &my_creds); + + return true; +} + +static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_PLAIN); +} + +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST +static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) +{ + bool ok; + + ok = torture_setting_bool(tctx, "expect_rodc", false); + if (ok) { + torture_skip(tctx, + "This test needs further investigation in the " + "RODC case against a Windows DC, in particular " + "with non-cached users"); + } + return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_PAC_REQUEST); +} +#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST */ + +static bool torture_krb5_as_req_break_pw(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_BREAK_PW); +} + +static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_CLOCK_SKEW); +} + +static bool torture_krb5_as_req_aes(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_AES); +} + +static bool torture_krb5_as_req_rc4(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_RC4); +} + +static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, + samba_cmdline_get_creds(), + TORTURE_KRB5_TEST_AES_RC4); +} + +NTSTATUS torture_krb5_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "krb5"); + struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc"); + suite->description = talloc_strdup(suite, "Kerberos tests"); + kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests"); + + torture_suite_add_simple_test(kdc_suite, + "as-req-cmdline", + torture_krb5_as_req_cmdline); + +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST + /* Only available with MIT Kerveros 1.15 and newer */ + torture_suite_add_simple_test(kdc_suite, "as-req-pac-request", + torture_krb5_as_req_pac_request); +#endif + + torture_suite_add_simple_test(kdc_suite, "as-req-break-pw", + torture_krb5_as_req_break_pw); + + /* This only works if kdc_timesync 0 is set in krb5.conf */ + torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew", + torture_krb5_as_req_clock_skew); + +#if 0 + torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite)); +#endif + torture_suite_add_simple_test(kdc_suite, + "as-req-aes", + torture_krb5_as_req_aes); + + torture_suite_add_simple_test(kdc_suite, + "as-req-rc4", + torture_krb5_as_req_rc4); + + torture_suite_add_simple_test(kdc_suite, + "as-req-aes-rc4", + torture_krb5_as_req_aes_rc4); + + torture_suite_add_suite(suite, kdc_suite); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/krb5/wscript_build b/source4/torture/krb5/wscript_build new file mode 100644 index 0000000..f59aa88 --- /dev/null +++ b/source4/torture/krb5/wscript_build @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +if bld.CONFIG_SET('AD_DC_BUILD_IS_ENABLED'): + if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'): + bld.SAMBA_MODULE('TORTURE_KRB5', + source='kdc-heimdal.c kdc-canon-heimdal.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_krb5_init', + deps='authkrb5 torture KERBEROS_UTIL', + internal_module=True) + else: + bld.SAMBA_MODULE('TORTURE_KRB5', + source='kdc-mit.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_krb5_init', + deps='authkrb5 torture KERBEROS_UTIL', + internal_module=True) diff --git a/source4/torture/ldap/basic.c b/source4/torture/ldap/basic.c new file mode 100644 index 0000000..ff9207e --- /dev/null +++ b/source4/torture/ldap/basic.c @@ -0,0 +1,1004 @@ +/* + Unix SMB/CIFS Implementation. + LDAP protocol helper functions for SAMBA + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Simo Sorce 2004 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "ldb_wrap.h" +#include "libcli/ldap/ldap_client.h" +#include "lib/cmdline/cmdline.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#undef strcasecmp + +static bool test_bind_sasl(struct torture_context *tctx, + struct ldap_connection *conn, struct cli_credentials *creds) +{ + NTSTATUS status; + bool ret = true; + + printf("Testing sasl bind as user\n"); + + status = torture_ldap_bind_sasl(conn, creds, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + } + + return ret; +} + +static bool test_multibind(struct ldap_connection *conn, const char *userdn, const char *password) +{ + NTSTATUS status, expected; + bool ok; + + printf("Testing multiple binds on a single connection as anonymous and user\n"); + + status = torture_ldap_bind(conn, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + printf("1st bind as anonymous failed with %s\n", + nt_errstr(status)); + return false; + } + + expected = NT_STATUS_LDAP(LDAP_STRONG_AUTH_REQUIRED); + status = torture_ldap_bind(conn, userdn, password); + + ok = NT_STATUS_EQUAL(status, expected); + if (!ok) { + printf("2nd bind as authenticated user should have " + "failed with: %s, got %s\n", + nt_errstr(expected), + nt_errstr(status)); + return false; + } + + return true; +} + +static bool test_search_rootDSE(struct ldap_connection *conn, const char **basedn, + const char ***partitions) +{ + bool ret = true; + struct ldap_message *msg, *result; + struct ldap_request *req; + int i; + struct ldap_SearchResEntry *r; + NTSTATUS status; + + printf("Testing RootDSE Search\n"); + + *basedn = NULL; + + if (partitions != NULL) { + *partitions = const_str_list(str_list_make_empty(conn)); + } + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + msg->type = LDAP_TAG_SearchRequest; + msg->r.SearchRequest.basedn = ""; + msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE; + msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; + msg->r.SearchRequest.timelimit = 0; + msg->r.SearchRequest.sizelimit = 0; + msg->r.SearchRequest.attributesonly = false; + msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)"); + msg->r.SearchRequest.num_attributes = 0; + msg->r.SearchRequest.attributes = NULL; + + req = ldap_request_send(conn, msg); + if (req == NULL) { + printf("Could not setup ldap search\n"); + return false; + } + + status = ldap_result_one(req, &result, LDAP_TAG_SearchResultEntry); + if (!NT_STATUS_IS_OK(status)) { + printf("search failed - %s\n", nt_errstr(status)); + return false; + } + + printf("received %d replies\n", req->num_replies); + + r = &result->r.SearchResultEntry; + + DEBUG(1,("\tdn: %s\n", r->dn)); + for (i=0; inum_attributes; i++) { + unsigned int j; + for (j=0; jattributes[i].num_values; j++) { + DEBUG(1,("\t%s: %d %.*s\n", r->attributes[i].name, + (int)r->attributes[i].values[j].length, + (int)r->attributes[i].values[j].length, + (char *)r->attributes[i].values[j].data)); + if (!(*basedn) && + strcasecmp("defaultNamingContext",r->attributes[i].name)==0) { + *basedn = talloc_asprintf(conn, "%.*s", + (int)r->attributes[i].values[j].length, + (char *)r->attributes[i].values[j].data); + } + if ((partitions != NULL) && + (strcasecmp("namingContexts", r->attributes[i].name) == 0)) { + char *entry = talloc_asprintf(conn, "%.*s", + (int)r->attributes[i].values[j].length, + (char *)r->attributes[i].values[j].data); + *partitions = str_list_add(*partitions, entry); + } + } + } + + return ret; +} + +static bool test_search_rootDSE_empty_substring(struct ldap_connection *conn) +{ + bool ret = true; + struct ldap_message *msg, *result; + struct ldap_request *req; + NTSTATUS status; + + printf("Testing RootDSE Search with objectclass= substring filter\n"); + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + msg->type = LDAP_TAG_SearchRequest; + msg->r.SearchRequest.basedn = ""; + msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE; + msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; + msg->r.SearchRequest.timelimit = 0; + msg->r.SearchRequest.sizelimit = 0; + msg->r.SearchRequest.attributesonly = false; + msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)"); + msg->r.SearchRequest.tree->operation = LDB_OP_SUBSTRING; + msg->r.SearchRequest.tree->u.substring.attr = "objectclass"; + msg->r.SearchRequest.tree->u.substring.start_with_wildcard = 1; + msg->r.SearchRequest.tree->u.substring.end_with_wildcard = 1; + msg->r.SearchRequest.tree->u.substring.chunks = NULL; + msg->r.SearchRequest.num_attributes = 0; + msg->r.SearchRequest.attributes = NULL; + + req = ldap_request_send(conn, msg); + if (req == NULL) { + printf("Could not setup ldap search\n"); + return false; + } + + status = ldap_result_one(req, &result, LDAP_TAG_SearchResultEntry); + if (!NT_STATUS_IS_OK(status)) { + printf("looking for search result reply failed - %s\n", nt_errstr(status)); + return false; + } + + printf("received %d replies\n", req->num_replies); + + return ret; +} + +static bool test_search_auth_empty_substring(struct ldap_connection *conn, const char *basedn) +{ + bool ret = true; + struct ldap_message *msg, *result; + struct ldap_request *req; + NTSTATUS status; + struct ldap_Result *r; + + printf("Testing authenticated base Search with objectclass= substring filter\n"); + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + msg->type = LDAP_TAG_SearchRequest; + msg->r.SearchRequest.basedn = basedn; + msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE; + msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; + msg->r.SearchRequest.timelimit = 0; + msg->r.SearchRequest.sizelimit = 0; + msg->r.SearchRequest.attributesonly = false; + msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)"); + msg->r.SearchRequest.tree->operation = LDB_OP_SUBSTRING; + msg->r.SearchRequest.tree->u.substring.attr = "objectclass"; + msg->r.SearchRequest.tree->u.substring.start_with_wildcard = 1; + msg->r.SearchRequest.tree->u.substring.end_with_wildcard = 1; + msg->r.SearchRequest.tree->u.substring.chunks = NULL; + msg->r.SearchRequest.num_attributes = 0; + msg->r.SearchRequest.attributes = NULL; + + req = ldap_request_send(conn, msg); + if (req == NULL) { + printf("Could not setup ldap search\n"); + return false; + } + + status = ldap_result_one(req, &result, LDAP_TAG_SearchResultDone); + if (!NT_STATUS_IS_OK(status)) { + printf("looking for search result done failed - %s\n", nt_errstr(status)); + return false; + } + + printf("received %d replies\n", req->num_replies); + + r = &result->r.SearchResultDone; + + if (r->resultcode != LDAP_SUCCESS) { + printf("search result done gave error - %s\n", ldb_strerror(r->resultcode)); + return false; + } + + return ret; +} + +static bool test_compare_sasl(struct ldap_connection *conn, const char *basedn) +{ + struct ldap_message *msg, *rep; + struct ldap_request *req; + const char *val; + NTSTATUS status; + + printf("Testing SASL Compare: %s\n", basedn); + + if (!basedn) { + return false; + } + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + msg->type = LDAP_TAG_CompareRequest; + msg->r.CompareRequest.dn = basedn; + msg->r.CompareRequest.attribute = talloc_strdup(msg, "objectClass"); + val = "domain"; + msg->r.CompareRequest.value = data_blob_talloc(msg, val, strlen(val)); + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_CompareResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap compare request - %s\n", nt_errstr(status)); + return false; + } + + DEBUG(5,("Code: %d DN: [%s] ERROR:[%s] REFERRAL:[%s]\n", + rep->r.CompareResponse.resultcode, + rep->r.CompareResponse.dn, + rep->r.CompareResponse.errormessage, + rep->r.CompareResponse.referral)); + + return true; +} + +/* + * This takes an AD error message and splits it into the WERROR code + * (WERR_DS_GENERIC if none found) and the reason (remaining string). + */ +static WERROR ad_error(const char *err_msg, char **reason) +{ + WERROR err = W_ERROR(strtol(err_msg, reason, 16)); + + if ((reason != NULL) && (*reason[0] != ':')) { + return WERR_DS_GENERIC_ERROR; /* not an AD std error message */ + } + + if (reason != NULL) { + *reason += 2; /* skip ": " */ + } + return err; +} + +/* This has to be done using the LDAP API since the LDB API does only transmit + * the error code and not the error message. */ +static bool test_error_codes(struct torture_context *tctx, + struct ldap_connection *conn, const char *basedn) +{ + struct ldap_message *msg, *rep; + struct ldap_request *req; + const char *err_code_str; + char *endptr; + WERROR err; + NTSTATUS status; + + printf("Testing the most important error code -> error message conversions!\n"); + + if (!basedn) { + return false; + } + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + printf(" Try a wrong addition\n"); + + msg->type = LDAP_TAG_AddRequest; + msg->r.AddRequest.dn = basedn; + msg->r.AddRequest.num_attributes = 0; + msg->r.AddRequest.attributes = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_AddResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap add request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.AddResponse.resultcode == 0) + || (rep->r.AddResponse.errormessage == NULL) + || (strtol(rep->r.AddResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.AddResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_REFERRAL)) + || (rep->r.AddResponse.resultcode != LDAP_REFERRAL)) { + return false; + } + if ((rep->r.AddResponse.referral == NULL) + || (strstr(rep->r.AddResponse.referral, basedn) == NULL)) { + return false; + } + + printf(" Try another wrong addition\n"); + + msg->type = LDAP_TAG_AddRequest; + msg->r.AddRequest.dn = ""; + msg->r.AddRequest.num_attributes = 0; + msg->r.AddRequest.attributes = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_AddResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap add request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.AddResponse.resultcode == 0) + || (rep->r.AddResponse.errormessage == NULL) + || (strtol(rep->r.AddResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.AddResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_ROOT_MUST_BE_NC) && + !W_ERROR_EQUAL(err, WERR_DS_NAMING_VIOLATION)) + || (rep->r.AddResponse.resultcode != LDAP_NAMING_VIOLATION)) { + return false; + } + + printf(" Try a wrong modification\n"); + + msg->type = LDAP_TAG_ModifyRequest; + msg->r.ModifyRequest.dn = basedn; + msg->r.ModifyRequest.num_mods = 0; + msg->r.ModifyRequest.mods = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap modifification request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyResponse.resultcode == 0) + || (rep->r.ModifyResponse.errormessage == NULL) + || (strtol(rep->r.ModifyResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) && + !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM)) + || (rep->r.ModifyResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) { + return false; + } + + printf(" Try another wrong modification\n"); + + msg->type = LDAP_TAG_ModifyRequest; + msg->r.ModifyRequest.dn = ""; + msg->r.ModifyRequest.num_mods = 0; + msg->r.ModifyRequest.mods = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap modifification request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyResponse.resultcode == 0) + || (rep->r.ModifyResponse.errormessage == NULL) + || (strtol(rep->r.ModifyResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) && + !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM)) + || (rep->r.ModifyResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) { + return false; + } + + printf(" Try a wrong removal\n"); + + msg->type = LDAP_TAG_DelRequest; + msg->r.DelRequest.dn = basedn; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_DelResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap removal request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.DelResponse.resultcode == 0) + || (rep->r.DelResponse.errormessage == NULL) + || (strtol(rep->r.DelResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.DelResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_CANT_DELETE) && + !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM)) + || (rep->r.DelResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) { + return false; + } + + printf(" Try another wrong removal\n"); + + msg->type = LDAP_TAG_DelRequest; + msg->r.DelRequest.dn = ""; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_DelResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap removal request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.DelResponse.resultcode == 0) + || (rep->r.DelResponse.errormessage == NULL) + || (strtol(rep->r.DelResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.DelResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_OBJ_NOT_FOUND) && + !W_ERROR_EQUAL(err, WERR_DS_NO_SUCH_OBJECT)) + || (rep->r.DelResponse.resultcode != LDAP_NO_SUCH_OBJECT)) { + return false; + } + + printf(" Try a wrong rename\n"); + + msg->type = LDAP_TAG_ModifyDNRequest; + msg->r.ModifyDNRequest.dn = basedn; + msg->r.ModifyDNRequest.newrdn = "dc=test"; + msg->r.ModifyDNRequest.deleteolddn = true; + msg->r.ModifyDNRequest.newsuperior = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap rename request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyDNResponse.resultcode == 0) + || (rep->r.ModifyDNResponse.errormessage == NULL) + || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_NO_PARENT_OBJECT) && + !W_ERROR_EQUAL(err, WERR_DS_GENERIC_ERROR)) + || (rep->r.ModifyDNResponse.resultcode != LDAP_OTHER)) { + return false; + } + + printf(" Try another wrong rename\n"); + + msg->type = LDAP_TAG_ModifyDNRequest; + msg->r.ModifyDNRequest.dn = basedn; + msg->r.ModifyDNRequest.newrdn = basedn; + msg->r.ModifyDNRequest.deleteolddn = true; + msg->r.ModifyDNRequest.newsuperior = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap rename request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyDNResponse.resultcode == 0) + || (rep->r.ModifyDNResponse.errormessage == NULL) + || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) && + !W_ERROR_EQUAL(err, WERR_DS_NAMING_VIOLATION)) + || (rep->r.ModifyDNResponse.resultcode != LDAP_NAMING_VIOLATION)) { + return false; + } + + printf(" Try another wrong rename\n"); + + msg->type = LDAP_TAG_ModifyDNRequest; + msg->r.ModifyDNRequest.dn = basedn; + msg->r.ModifyDNRequest.newrdn = ""; + msg->r.ModifyDNRequest.deleteolddn = true; + msg->r.ModifyDNRequest.newsuperior = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap rename request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyDNResponse.resultcode == 0) + || (rep->r.ModifyDNResponse.errormessage == NULL) + || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) && + !W_ERROR_EQUAL(err, WERR_DS_PROTOCOL_ERROR)) + || (rep->r.ModifyDNResponse.resultcode != LDAP_PROTOCOL_ERROR)) { + return false; + } + + printf(" Try another wrong rename\n"); + + msg->type = LDAP_TAG_ModifyDNRequest; + msg->r.ModifyDNRequest.dn = ""; + msg->r.ModifyDNRequest.newrdn = "cn=temp"; + msg->r.ModifyDNRequest.deleteolddn = true; + msg->r.ModifyDNRequest.newsuperior = NULL; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap rename request - %s\n", nt_errstr(status)); + return false; + } + + if ((rep->r.ModifyDNResponse.resultcode == 0) + || (rep->r.ModifyDNResponse.errormessage == NULL) + || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0) + || (*endptr != ':')) { + printf("Invalid error message!\n"); + return false; + } + + err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr); + err_code_str = win_errstr(err); + printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr); + if ((!W_ERROR_EQUAL(err, WERR_DS_OBJ_NOT_FOUND) && + !W_ERROR_EQUAL(err, WERR_DS_NO_SUCH_OBJECT)) + || (rep->r.ModifyDNResponse.resultcode != LDAP_NO_SUCH_OBJECT)) { + return false; + } + + return true; +} + +static bool test_referrals(struct torture_context *tctx, TALLOC_CTX *mem_ctx, + const char *url, const char *basedn, const char **partitions) +{ + struct ldb_context *ldb; + struct ldb_result *res; + const char * const *attrs = { NULL }; + struct ldb_dn *dn1, *dn2; + int ret; + int i, j, k; + char *tempstr; + bool found, l_found; + + printf("Testing referrals\n"); + + if (partitions[0] == NULL) { + printf("Partitions list empty!\n"); + return false; + } + + if (strcmp(partitions[0], basedn) != 0) { + printf("The first (root) partition DN should be the base DN!\n"); + return false; + } + + ldb = ldb_wrap_connect(mem_ctx, tctx->ev, tctx->lp_ctx, url, + NULL, samba_cmdline_get_creds(), 0); + + /* "partitions[i]" are the partitions for which we search the parents */ + for (i = 1; partitions[i] != NULL; i++) { + dn1 = ldb_dn_new(mem_ctx, ldb, partitions[i]); + if (dn1 == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + /* search using base scope */ + /* "partitions[j]" are the parent candidates */ + for (j = str_list_length(partitions) - 1; j >= 0; --j) { + dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]); + if (dn2 == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + ret = ldb_search(ldb, mem_ctx, &res, dn2, + LDB_SCOPE_BASE, attrs, + "(foo=bar)"); + if (ret != LDB_SUCCESS) { + printf("%s", ldb_errstring(ldb)); + talloc_free(ldb); + return false; + } + + if (res->refs != NULL) { + printf("There shouldn't be generated any referrals in the base scope!\n"); + talloc_free(ldb); + return false; + } + + talloc_free(res); + talloc_free(dn2); + } + + /* search using onelevel scope */ + found = false; + /* "partitions[j]" are the parent candidates */ + for (j = str_list_length(partitions) - 1; j >= 0; --j) { + dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]); + if (dn2 == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + ret = ldb_search(ldb, mem_ctx, &res, dn2, + LDB_SCOPE_ONELEVEL, attrs, + "(foo=bar)"); + if (ret != LDB_SUCCESS) { + printf("%s", ldb_errstring(ldb)); + talloc_free(ldb); + return false; + } + + tempstr = talloc_asprintf(mem_ctx, "/%s??base", + partitions[i]); + if (tempstr == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + /* Try to find or find not a matching referral */ + l_found = false; + for (k = 0; (!l_found) && (res->refs != NULL) + && (res->refs[k] != NULL); k++) { + if (strstr(res->refs[k], tempstr) != NULL) { + l_found = true; + } + } + + talloc_free(tempstr); + + if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0) + && (ldb_dn_compare(dn2, dn1) != 0)) { + /* This is a referral candidate */ + if (!l_found) { + printf("A required referral hasn't been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]); + talloc_free(ldb); + return false; + } + found = true; + } else { + /* This isn't a referral candidate */ + if (l_found) { + printf("A unrequired referral has been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]); + talloc_free(ldb); + return false; + } + } + + talloc_free(res); + talloc_free(dn2); + } + + /* search using subtree scope */ + found = false; + /* "partitions[j]" are the parent candidates */ + for (j = str_list_length(partitions) - 1; j >= 0; --j) { + dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]); + if (dn2 == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + ret = ldb_search(ldb, mem_ctx, &res, dn2, + LDB_SCOPE_SUBTREE, attrs, + "(foo=bar)"); + if (ret != LDB_SUCCESS) { + printf("%s", ldb_errstring(ldb)); + talloc_free(ldb); + return false; + } + + tempstr = talloc_asprintf(mem_ctx, "/%s", + partitions[i]); + if (tempstr == NULL) { + printf("Out of memory\n"); + talloc_free(ldb); + return false; + } + + /* Try to find or find not a matching referral */ + l_found = false; + for (k = 0; (!l_found) && (res->refs != NULL) + && (res->refs[k] != NULL); k++) { + if (strstr(res->refs[k], tempstr) != NULL) { + l_found = true; + } + } + + talloc_free(tempstr); + + if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0) + && (ldb_dn_compare(dn2, dn1) != 0)) { + /* This is a referral candidate */ + if (!l_found) { + printf("A required referral hasn't been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]); + talloc_free(ldb); + return false; + } + found = true; + } else { + /* This isn't a referral candidate */ + if (l_found) { + printf("A unrequired referral has been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]); + talloc_free(ldb); + return false; + } + } + + talloc_free(res); + talloc_free(dn2); + } + + talloc_free(dn1); + } + + talloc_free(ldb); + + return true; +} + +static bool test_abandon_request(struct torture_context *tctx, + struct ldap_connection *conn, const char *basedn) +{ + struct ldap_message *msg; + struct ldap_request *req; + NTSTATUS status; + + printf("Testing the AbandonRequest with an old message id!\n"); + + if (!basedn) { + return false; + } + + msg = new_ldap_message(conn); + if (!msg) { + return false; + } + + printf(" Try a AbandonRequest for an old message id\n"); + + msg->type = LDAP_TAG_AbandonRequest; + msg->r.AbandonRequest.messageid = 1; + + req = ldap_request_send(conn, msg); + if (!req) { + return false; + } + + status = ldap_request_wait(req); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap abandon request - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +bool torture_ldap_basic(struct torture_context *torture) +{ + NTSTATUS status; + struct ldap_connection *conn; + TALLOC_CTX *mem_ctx; + bool ret = true; + const char *host = torture_setting_string(torture, "host", NULL); + const char *userdn = torture_setting_string(torture, "ldap_userdn", NULL); + const char *secret = torture_setting_string(torture, "ldap_secret", NULL); + const char *url; + const char *basedn; + const char **partitions; + + mem_ctx = talloc_init("torture_ldap_basic"); + + url = talloc_asprintf(mem_ctx, "ldap://%s/", host); + + status = torture_ldap_connection(torture, &conn, url); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (!test_search_rootDSE(conn, &basedn, &partitions)) { + ret = false; + } + + if (!test_search_rootDSE_empty_substring(conn)) { + ret = false; + } + + /* other bind tests here */ + + if (!test_multibind(conn, userdn, secret)) { + ret = false; + } + + if (!test_bind_sasl(torture, conn, samba_cmdline_get_creds())) { + ret = false; + } + + if (!test_search_auth_empty_substring(conn, basedn)) { + ret = false; + } + + if (!test_compare_sasl(conn, basedn)) { + ret = false; + } + + /* error codes test here */ + + if (!test_error_codes(torture, conn, basedn)) { + ret = false; + } + + /* referrals test here */ + + if (!test_referrals(torture, mem_ctx, url, basedn, partitions)) { + ret = false; + } + + if (!test_abandon_request(torture, conn, basedn)) { + ret = false; + } + + /* if there are no more tests we are closing */ + torture_ldap_close(conn); + talloc_free(mem_ctx); + + torture_assert(torture, ret, "torture_ldap_basic failed"); + + return ret; +} + diff --git a/source4/torture/ldap/cldap.c b/source4/torture/ldap/cldap.c new file mode 100644 index 0000000..a021f4c --- /dev/null +++ b/source4/torture/ldap/cldap.c @@ -0,0 +1,179 @@ +/* + Unix SMB/CIFS Implementation. + + test CLDAP operations + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Matthias Dieter Wallnöfer 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "libcli/cldap/cldap.h" +#include "libcli/ldap/ldap_client.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "../lib/tsocket/tsocket.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status") + +#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value"); + +#define CHECK_STRING(v, correct) torture_assert_str_equal(tctx, v, correct, "incorrect value"); + +/* + convert a ldap result message to a ldb message. This allows us to + use the convenient ldif dump routines in ldb to print out cldap + search results +*/ +static struct ldb_message *ldap_msg_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct ldap_SearchResEntry *res) +{ + struct ldb_message *msg; + + msg = ldb_msg_new(mem_ctx); + msg->dn = ldb_dn_new(msg, ldb, res->dn); + msg->num_elements = res->num_attributes; + msg->elements = talloc_steal(msg, res->attributes); + return msg; +} + +/* + dump a set of cldap results +*/ +static void cldap_dump_results(struct cldap_search *search) +{ + struct ldb_ldif ldif; + struct ldb_context *ldb; + + if (!search || !(search->out.response)) { + return; + } + + /* we need a ldb context to use ldb_ldif_write_file() */ + ldb = ldb_init(NULL, NULL); + + ZERO_STRUCT(ldif); + ldif.msg = ldap_msg_to_ldb(ldb, ldb, search->out.response); + + ldb_ldif_write_file(ldb, stdout, &ldif); + + talloc_free(ldb); +} + +/* + test generic cldap operations +*/ +static bool test_cldap_generic(struct torture_context *tctx, const char *dest) +{ + struct cldap_socket *cldap; + NTSTATUS status; + struct cldap_search search; + const char *attrs1[] = { "currentTime", "highestCommittedUSN", NULL }; + const char *attrs2[] = { "currentTime", "highestCommittedUSN", "netlogon", NULL }; + const char *attrs3[] = { "netlogon", NULL }; + struct tsocket_address *dest_addr; + const char *ip; + struct nbt_name nbt_name; + int ret; + + make_nbt_name_server(&nbt_name, dest); + + status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, &nbt_name, tctx, &ip, tctx->ev); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx,"Failed to resolve %s: %s", + nbt_name.name, nt_errstr(status))); + + ret = tsocket_address_inet_from_strings(tctx, "ip", + ip, + lpcfg_cldap_port(tctx->lp_ctx), + &dest_addr); + CHECK_VAL(ret, 0); + + /* cldap_socket_init should now know about the dest. address */ + status = cldap_socket_init(tctx, NULL, dest_addr, &cldap); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(search); + search.in.dest_address = NULL; + search.in.dest_port = 0; + search.in.timeout = 10; + search.in.retries = 3; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("fetching whole rootDSE\n"); + search.in.filter = "(objectclass=*)"; + search.in.attributes = NULL; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + if (DEBUGLVL(3)) cldap_dump_results(&search); + + printf("fetching currentTime and USN\n"); + search.in.filter = "(objectclass=*)"; + search.in.attributes = attrs1; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + if (DEBUGLVL(3)) cldap_dump_results(&search); + + printf("Testing currentTime, USN and netlogon\n"); + search.in.filter = "(objectclass=*)"; + search.in.attributes = attrs2; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + if (DEBUGLVL(3)) cldap_dump_results(&search); + + printf("Testing objectClass=* and netlogon\n"); + search.in.filter = "(objectclass=*)"; + search.in.attributes = attrs3; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + if (DEBUGLVL(3)) cldap_dump_results(&search); + + printf("Testing a false expression\n"); + search.in.filter = "(&(objectclass=*)(highestCommittedUSN=2))"; + search.in.attributes = attrs1; + + status = cldap_search(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + if (DEBUGLVL(3)) cldap_dump_results(&search); + + return true; +} + +bool torture_cldap(struct torture_context *torture) +{ + bool ret = true; + const char *host = torture_setting_string(torture, "host", NULL); + + ret &= test_cldap_generic(torture, host); + + return ret; +} + diff --git a/source4/torture/ldap/cldapbench.c b/source4/torture/ldap/cldapbench.c new file mode 100644 index 0000000..9b6f7f2 --- /dev/null +++ b/source4/torture/ldap/cldapbench.c @@ -0,0 +1,233 @@ +/* + Unix SMB/CIFS implementation. + + CLDAP benchmark test + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/cldap/cldap.h" +#include "libcli/resolve/resolve.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/torture.h" +#include "torture/ldap/proto.h" +#include "param/param.h" +#include "../lib/tsocket/tsocket.h" + +#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value"); + +struct bench_state { + struct torture_context *tctx; + int pass_count, fail_count; +}; + +static void request_netlogon_handler(struct tevent_req *req) +{ + struct cldap_netlogon io; + struct bench_state *state = tevent_req_callback_data(req, struct bench_state); + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + io.in.version = 6; + status = cldap_netlogon_recv(req, tmp_ctx, &io); + talloc_free(req); + if (NT_STATUS_IS_OK(status)) { + state->pass_count++; + } else { + state->fail_count++; + } + talloc_free(tmp_ctx); +} + +/* + benchmark cldap netlogon calls +*/ +static bool bench_cldap_netlogon(struct torture_context *tctx, const char *address) +{ + struct cldap_socket *cldap; + int num_sent=0; + struct timeval tv = timeval_current(); + int timelimit = torture_setting_int(tctx, "timelimit", 10); + struct cldap_netlogon search; + struct bench_state *state; + NTSTATUS status; + struct tsocket_address *dest_addr; + int ret; + + ret = tsocket_address_inet_from_strings(tctx, "ip", + address, + lpcfg_cldap_port(tctx->lp_ctx), + &dest_addr); + CHECK_VAL(ret, 0); + + status = cldap_socket_init(tctx, NULL, dest_addr, &cldap); + torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init"); + + state = talloc_zero(tctx, struct bench_state); + state->tctx = tctx; + + ZERO_STRUCT(search); + search.in.dest_address = NULL; + search.in.dest_port = 0; + search.in.acct_control = -1; + search.in.version = 6; + + printf("Running CLDAP/netlogon for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + while (num_sent - (state->pass_count+state->fail_count) < 10) { + struct tevent_req *req; + req = cldap_netlogon_send(state, tctx->ev, + cldap, &search); + + tevent_req_set_callback(req, request_netlogon_handler, state); + + num_sent++; + if (num_sent % 50 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + printf("%.1f queries per second (%d failures) \r", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + fflush(stdout); + } + } + } + + tevent_loop_once(tctx->ev); + } + + while (num_sent != (state->pass_count + state->fail_count)) { + tevent_loop_once(tctx->ev); + } + + printf("%.1f queries per second (%d failures) \n", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + + talloc_free(cldap); + return true; +} + +static void request_rootdse_handler(struct tevent_req *req) +{ + struct cldap_search io; + struct bench_state *state = tevent_req_callback_data(req, struct bench_state); + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + status = cldap_search_recv(req, tmp_ctx, &io); + talloc_free(req); + if (NT_STATUS_IS_OK(status)) { + state->pass_count++; + } else { + state->fail_count++; + } + talloc_free(tmp_ctx); +} + +/* + benchmark cldap netlogon calls +*/ +static bool bench_cldap_rootdse(struct torture_context *tctx, const char *address) +{ + struct cldap_socket *cldap; + int num_sent=0; + struct timeval tv = timeval_current(); + int timelimit = torture_setting_int(tctx, "timelimit", 10); + struct cldap_search search; + struct bench_state *state; + NTSTATUS status; + struct tsocket_address *dest_addr; + int ret; + + ret = tsocket_address_inet_from_strings(tctx, "ip", + address, + lpcfg_cldap_port(tctx->lp_ctx), + &dest_addr); + CHECK_VAL(ret, 0); + + /* cldap_socket_init should now know about the dest. address */ + status = cldap_socket_init(tctx, NULL, dest_addr, &cldap); + torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init"); + + state = talloc_zero(tctx, struct bench_state); + + ZERO_STRUCT(search); + search.in.dest_address = NULL; + search.in.dest_port = 0; + search.in.filter = "(objectClass=*)"; + search.in.timeout = 2; + search.in.retries = 1; + + printf("Running CLDAP/rootdse for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + while (num_sent - (state->pass_count+state->fail_count) < 10) { + struct tevent_req *req; + req = cldap_search_send(state, tctx->ev, cldap, &search); + + tevent_req_set_callback(req, request_rootdse_handler, state); + + num_sent++; + if (num_sent % 50 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + printf("%.1f queries per second (%d failures) \r", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + fflush(stdout); + } + } + } + + tevent_loop_once(tctx->ev); + } + + while (num_sent != (state->pass_count + state->fail_count)) { + tevent_loop_once(tctx->ev); + } + + printf("%.1f queries per second (%d failures) \n", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + + talloc_free(cldap); + return true; +} + +/* + benchmark how fast a CLDAP server can respond to a series of parallel + requests +*/ +bool torture_bench_cldap(struct torture_context *torture) +{ + const char *address; + struct nbt_name name; + NTSTATUS status; + bool ret = true; + + make_nbt_name_server(&name, torture_setting_string(torture, "host", NULL)); + + /* do an initial name resolution to find its IP */ + status = resolve_name_ex(lpcfg_resolve_context(torture->lp_ctx), + 0, 0, &name, torture, &address, torture->ev); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to resolve %s - %s\n", + name.name, nt_errstr(status)); + return false; + } + + ret &= bench_cldap_netlogon(torture, address); + ret &= bench_cldap_rootdse(torture, address); + + return ret; +} diff --git a/source4/torture/ldap/common.c b/source4/torture/ldap/common.c new file mode 100644 index 0000000..c33fda7 --- /dev/null +++ b/source4/torture/ldap/common.c @@ -0,0 +1,135 @@ +/* + Unix SMB/CIFS Implementation. + LDAP protocol helper functions for SAMBA + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Simo Sorce 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/smbtorture.h" +#include "torture/ldap/proto.h" + +NTSTATUS torture_ldap_bind(struct ldap_connection *conn, const char *userdn, const char *password) +{ + NTSTATUS status; + + status = ldap_bind_simple(conn, userdn, password); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to bind with provided credentials - %s\n", + nt_errstr(status)); + } + + return status; +} + +NTSTATUS torture_ldap_bind_sasl(struct ldap_connection *conn, + struct cli_credentials *creds, + struct loadparm_context *lp_ctx) +{ + NTSTATUS status; + + status = ldap_bind_sasl(conn, creds, lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed sasl bind with provided credentials - %s\n", + nt_errstr(status)); + } + + return status; +} + +/* open a ldap connection to a server */ +NTSTATUS torture_ldap_connection(struct torture_context *tctx, + struct ldap_connection **conn, + const char *url) +{ + NTSTATUS status; + + if (!url) { + printf("You must specify a url string\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + *conn = ldap4_new_connection(tctx, tctx->lp_ctx, tctx->ev); + + status = ldap_connect(*conn, url); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to ldap server '%s' - %s\n", + url, nt_errstr(status)); + } + + return status; +} + +/* close an ldap connection to a server */ +NTSTATUS torture_ldap_close(struct ldap_connection *conn) +{ + struct ldap_message *msg; + struct ldap_request *req; + NTSTATUS status; + + printf("Closing the connection...\n"); + + msg = new_ldap_message(conn); + if (!msg) { + talloc_free(conn); + return NT_STATUS_NO_MEMORY; + } + + printf(" Try a UnbindRequest\n"); + + msg->type = LDAP_TAG_UnbindRequest; + + req = ldap_request_send(conn, msg); + if (!req) { + talloc_free(conn); + return NT_STATUS_NO_MEMORY; + } + + status = ldap_request_wait(req); + if (!NT_STATUS_IS_OK(status)) { + printf("error in ldap unbind request - %s\n", nt_errstr(status)); + talloc_free(conn); + return status; + } + + talloc_free(conn); + return NT_STATUS_OK; +} + +NTSTATUS torture_ldap_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ldap"); + torture_suite_add_simple_test(suite, "bench-cldap", torture_bench_cldap); + torture_suite_add_simple_test(suite, "basic", torture_ldap_basic); + torture_suite_add_simple_test(suite, "sort", torture_ldap_sort); + torture_suite_add_simple_test(suite, "cldap", torture_cldap); + torture_suite_add_simple_test(suite, "netlogon-udp", torture_netlogon_udp); + torture_suite_add_simple_test(suite, "netlogon-tcp", torture_netlogon_tcp); + torture_suite_add_simple_test(suite, "schema", torture_ldap_schema); + torture_suite_add_simple_test(suite, "uptodatevector", torture_ldap_uptodatevector); + torture_suite_add_simple_test(suite, "nested-search", test_ldap_nested_search); + torture_suite_add_simple_test( + suite, "session-expiry", torture_ldap_session_expiry); + + suite->description = talloc_strdup(suite, "LDAP and CLDAP tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/ldap/ldap_sort.c b/source4/torture/ldap/ldap_sort.c new file mode 100644 index 0000000..b28bd95 --- /dev/null +++ b/source4/torture/ldap/ldap_sort.c @@ -0,0 +1,158 @@ +/* + Unix SMB/CIFS implementation. + + Test LDB attribute functions + + Copyright (C) Andrew Bartlet 2008-2009 + Copyright (C) Matthieu Patou 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include +#include +#include "ldb_wrap.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/smbtorture.h" +#include "torture/ldap/proto.h" +#include + +bool torture_ldap_sort(struct torture_context *torture) +{ + struct ldb_context *ldb; + + bool ret = false; + const char *host = torture_setting_string(torture, "host", NULL); + char *url; + codepoint_t j; + struct ldb_message_element *elem; + struct ldb_message *msg; + + struct ldb_server_sort_control **control; + struct ldb_request *req; + struct ldb_result *ctx; + struct ldb_val *prev = NULL; + const char *prev_txt = NULL; + int prev_len = 0; + struct ldb_val *cur = NULL; + const char *cur_txt = NULL; + int cur_len = 0; + struct ldb_dn *dn; + + + /* TALLOC_CTX* ctx;*/ + + url = talloc_asprintf(torture, "ldap://%s/", host); + + ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + torture_assert(torture, ldb, "Failed to make LDB connection to target"); + + ctx = talloc_zero(ldb, struct ldb_result); + + control = talloc_array(ctx, struct ldb_server_sort_control *, 2); + control[0] = talloc(control, struct ldb_server_sort_control); + control[0]->attributeName = talloc_strdup(control, "cn"); + control[0]->orderingRule = NULL; + control[0]->reverse = 0; + control[1] = NULL; + + dn = ldb_get_default_basedn(ldb); + ldb_dn_add_child_fmt(dn, "cn=users"); + ret = ldb_build_search_req(&req, ldb, ctx, + dn, + LDB_SCOPE_SUBTREE, + "(objectClass=*)", NULL, + NULL, + ctx, ldb_search_default_callback, NULL); + torture_assert(torture, ret == LDB_SUCCESS, "Failed to build search request"); + + ret = ldb_request_add_control(req, LDB_CONTROL_SERVER_SORT_OID, true, control); + torture_assert(torture, ret == LDB_SUCCESS, "Failed to add control to search request"); + + ret = ldb_request(ldb, req); + torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb)); + + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb)); + + ret = true; + if (ctx->count > 1) { + unsigned int i; + for (i=0;icount;i++) { + msg = ctx->msgs[i]; + elem = ldb_msg_find_element(msg,"cn"); + torture_assert_not_null(torture, elem, "msg lacks CN"); + cur = elem->values; + torture_comment(torture, "cn: %s\n",cur->data); + if (prev != NULL) + { + /* Do only the ascii case right now ... */ + cur_txt = (const char *) cur->data; + cur_len = cur->length; + prev_txt = (const char *) prev->data; + prev_len = prev->length; + /* Remove leading whitespace as the sort function do so ... */ + while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;} + while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;} + while( *(cur_txt) && *(prev_txt) && cur_len && prev_len ) { + j = toupper_m(*(prev_txt))-toupper_m(*(cur_txt)); + if ( j > 0 ) { + /* Just check that is not due to trailing white space in prev_txt + * That is to say *cur_txt = 0 and prev_txt = 20 */ + /* Remove trailing whitespace */ + while ( *prev_txt == ' ' ) { prev_txt++; prev_len--;} + while ( *cur_txt == ' ' ) { cur_txt++; cur_len--;} + /* Now that potential whitespace are removed if we are at the end + * of the cur_txt then it means that in fact strings were identical + */ + torture_assert(torture, *cur_txt && *prev_txt, "Data wrongly sorted"); + break; + } + else + { + if ( j == 0 ) + { + if ( *(cur_txt) == ' ') { + while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;} + while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;} + } + cur_txt++; + prev_txt++; + prev_len--; + cur_len--; + } + else + { + break; + } + } + } + if ( ret != 1 ) { + break; + } + } + prev = cur; + } + + } + + return ret; +} diff --git a/source4/torture/ldap/nested_search.c b/source4/torture/ldap/nested_search.c new file mode 100644 index 0000000..8f4d71b --- /dev/null +++ b/source4/torture/ldap/nested_search.c @@ -0,0 +1,206 @@ +/* + Unix SMB/CIFS implementation. + + BRIEF FILE DESCRIPTION + + Copyright (C) Kamen Mazdrashki 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "ldb.h" +#include "ldb_wrap.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#define torture_assert_res(torture_ctx,expr,cmt,_res) \ + if (!(expr)) { \ + torture_result(torture_ctx, TORTURE_FAIL, __location__": Expression `%s' failed: %s", __STRING(expr), cmt); \ + return _res; \ + } + + +struct nested_search_context { + struct torture_context *tctx; + struct ldb_dn *root_dn; + struct ldb_context *ldb; + struct ldb_result *ldb_res; +}; + +/* + * ldb_search handler - used to executed a nested + * ldap search request during LDB_REPLY_ENTRY handling + */ +static int nested_search_callback(struct ldb_request *req, + struct ldb_reply *ares) +{ + unsigned int i; + int res; + struct nested_search_context *sctx; + struct ldb_result *ldb_res; + struct ldb_message *ldb_msg; + static const char *attrs[] = { + "rootDomainNamingContext", + "configurationNamingContext", + "schemaNamingContext", + "defaultNamingContext", + NULL + }; + enum ldb_reply_type type; + + sctx = talloc_get_type(req->context, struct nested_search_context); + + type = ares->type; + /* sanity check */ + switch (type) { + case LDB_REPLY_ENTRY: + torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_ENTRY\n"); + ldb_msg = ares->message; + torture_assert_res(sctx->tctx, ldb_msg, "ares->message is NULL!", LDB_ERR_OPERATIONS_ERROR); + torture_assert_res(sctx->tctx, ldb_msg->num_elements, "No elements returned!", LDB_ERR_OPERATIONS_ERROR); + torture_assert_res(sctx->tctx, ldb_msg->elements, "elements member is NULL!", LDB_ERR_OPERATIONS_ERROR); + break; + case LDB_REPLY_DONE: + torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_DONE\n"); + break; + case LDB_REPLY_REFERRAL: + torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_REFERRAL\n"); + break; + } + + /* switch context and let default handler do its job */ + req->context = sctx->ldb_res; + res = ldb_search_default_callback(req, ares); + req->context = sctx; + if (res != LDB_SUCCESS) { + return res; + } + + /* not a search reply, then get out */ + if (type != LDB_REPLY_ENTRY) { + return res; + } + + + res = ldb_search(sctx->ldb, sctx, &ldb_res, sctx->root_dn, LDB_SCOPE_BASE, attrs, "(objectClass=*)"); + if (res != LDB_SUCCESS) { + torture_warning(sctx->tctx, + "Search on RootDSE failed in search_entry handler: %s", + ldb_errstring(sctx->ldb)); + return LDB_SUCCESS; + } + + torture_assert_res(sctx->tctx, ldb_res->count == 1, "One message expected here", LDB_ERR_OPERATIONS_ERROR); + + ldb_msg = ldb_res->msgs[0]; + torture_assert_res(sctx->tctx, ldb_msg->num_elements == (ARRAY_SIZE(attrs)-1), + "Search returned different number of elts than requested", LDB_ERR_OPERATIONS_ERROR); + for (i = 0; i < ldb_msg->num_elements; i++) { + const char *msg; + struct ldb_message_element *elt1; + struct ldb_message_element *elt2; + + elt2 = &ldb_msg->elements[i]; + msg = talloc_asprintf(sctx, "Processing element: %s", elt2->name); + elt1 = ldb_msg_find_element(sctx->ldb_res->msgs[0], elt2->name); + torture_assert_res(sctx->tctx, elt1, msg, LDB_ERR_OPERATIONS_ERROR); + + /* compare elements */ + torture_assert_res(sctx->tctx, elt2->flags == elt1->flags, "", LDB_ERR_OPERATIONS_ERROR); + torture_assert_res(sctx->tctx, elt2->num_values == elt1->num_values, "", LDB_ERR_OPERATIONS_ERROR); + } + /* TODO: check returned result */ + + return LDB_SUCCESS; +} + +/** + * Test nested search execution against RootDSE + * on remote LDAP server. + */ +bool test_ldap_nested_search(struct torture_context *tctx) +{ + int ret; + char *url; + const char *host = torture_setting_string(tctx, "host", NULL); + struct ldb_request *req; + struct nested_search_context *sctx; + static const char *attrs[] = { +/* + "rootDomainNamingContext", + "configurationNamingContext", + "schemaNamingContext", + "defaultNamingContext", +*/ + "*", + NULL + }; + + sctx = talloc_zero(tctx, struct nested_search_context); + torture_assert(tctx, sctx, "Not enough memory"); + sctx->tctx = tctx; + + url = talloc_asprintf(sctx, "ldap://%s/", host); + if (!url) { + torture_assert(tctx, url, "Not enough memory"); + } + + torture_comment(tctx, "Connecting to: %s\n", url); + sctx->ldb = ldb_wrap_connect(sctx, tctx->ev, tctx->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + torture_assert(tctx, sctx->ldb, "Failed to create ldb connection"); + + /* prepare context for searching */ + sctx->root_dn = ldb_dn_new(sctx, sctx->ldb, NULL); + sctx->ldb_res = talloc_zero(sctx, struct ldb_result); + + /* build search request */ + ret = ldb_build_search_req(&req, + sctx->ldb, + sctx, + sctx->root_dn, LDB_SCOPE_BASE, + "(objectClass=*)", attrs, NULL, + sctx, nested_search_callback, + NULL); + if (ret != LDB_SUCCESS) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Allocating request failed: %s", ldb_errstring(sctx->ldb)); + return false; + } + + ret = ldb_request(sctx->ldb, req); + if (ret != LDB_SUCCESS) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Search failed: %s", ldb_errstring(sctx->ldb)); + return false; + } + + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (ret != LDB_SUCCESS) { + torture_result(tctx, TORTURE_FAIL, + __location__ ": Search error: %s", ldb_errstring(sctx->ldb)); + return false; + } + + /* TODO: check returned result */ + + talloc_free(sctx); + return true; +} + diff --git a/source4/torture/ldap/netlogon.c b/source4/torture/ldap/netlogon.c new file mode 100644 index 0000000..0bddb3e --- /dev/null +++ b/source4/torture/ldap/netlogon.c @@ -0,0 +1,668 @@ +/* + Unix SMB/CIFS Implementation. + + test CLDAP/LDAP netlogon operations + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Matthias Dieter Wallnöfer 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "libcli/cldap/cldap.h" +#include "libcli/ldap/ldap_client.h" +#include "libcli/ldap/ldap_ndr.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/netlogon.h" +#include "param/param.h" +#include "../lib/tsocket/tsocket.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#undef strcasecmp + +#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status") + +#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value"); + +#define CHECK_STRING(v, correct) torture_assert_str_equal(tctx, v, correct, "incorrect value"); + +typedef NTSTATUS (*request_netlogon_t)(void *con, + TALLOC_CTX *mem_ctx, + struct cldap_netlogon *io); + +typedef NTSTATUS (*request_rootdse_t)(void *con, + TALLOC_CTX *mem_ctx, + struct cldap_search *io); + +/* + test netlogon operations +*/ +static bool test_ldap_netlogon(struct torture_context *tctx, + request_netlogon_t request_netlogon, + void *cldap, + const char *dest) +{ + NTSTATUS status; + struct cldap_netlogon search, empty_search; + struct netlogon_samlogon_response n1; + struct GUID guid; + int i; + + ZERO_STRUCT(search); + search.in.dest_address = NULL; + search.in.dest_port = 0; + search.in.acct_control = -1; + search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; + search.in.map_response = true; + + empty_search = search; + + printf("Trying without any attributes\n"); + search = empty_search; + status = request_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + n1 = search.out.netlogon; + + search.in.user = "Administrator"; + search.in.realm = n1.data.nt5_ex.dns_domain; + search.in.host = "__cldap_torture__"; + + printf("Scanning for netlogon levels\n"); + for (i=0;i<256;i++) { + search.in.version = i; + printf("Trying netlogon level %d\n", i); + status = request_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + } + + printf("Scanning for netlogon level bits\n"); + for (i=0;i<31;i++) { + search.in.version = (1<type = LDAP_TAG_SearchRequest; + msg->r.SearchRequest.basedn = ""; + msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE; + msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; + msg->r.SearchRequest.timelimit = 0; + msg->r.SearchRequest.sizelimit = 0; + msg->r.SearchRequest.attributesonly = false; + msg->r.SearchRequest.tree = ldb_parse_tree(msg, io->in.filter); + msg->r.SearchRequest.num_attributes = str_list_length(io->in.attributes); + msg->r.SearchRequest.attributes = io->in.attributes; + + req = ldap_request_send(conn, msg); + if (req == NULL) { + printf("Could not setup ldap search\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + ZERO_STRUCT(io->out); + for (i = 0; i < 2; ++i) { + status = ldap_result_n(req, i, &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + switch (result->type) { + case LDAP_TAG_SearchResultEntry: + if (i != 0) { + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + io->out.response = &result->r.SearchResultEntry; + break; + case LDAP_TAG_SearchResultDone: + io->out.result = &result->r.SearchResultDone; + if (io->out.result->resultcode != LDAP_SUCCESS) { + return NT_STATUS_LDAP(io->out.result->resultcode); + } + + return NT_STATUS_OK; + default: + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS tcp_ldap_netlogon(void *conn, + TALLOC_CTX *mem_ctx, + struct cldap_netlogon *io) +{ + struct cldap_search search; + struct ldap_SearchResEntry *res; + NTSTATUS status; + DATA_BLOB *blob; + + ZERO_STRUCT(search); + search.in.attributes = (const char *[]) { "netlogon", NULL }; + search.in.filter = cldap_netlogon_create_filter(mem_ctx, io); + if (search.in.filter == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = tcp_ldap_rootdse(conn, mem_ctx, &search); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + res = search.out.response; + if (res == NULL) { + return NT_STATUS_NOT_FOUND; + } + + if (res->num_attributes != 1 || + strcasecmp(res->attributes[0].name, "netlogon") != 0 || + res->attributes[0].num_values != 1 || + res->attributes[0].values->length < 2) { + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + + blob = res->attributes[0].values; + status = pull_netlogon_samlogon_response(blob, mem_ctx, + &io->out.netlogon); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (io->in.map_response) { + map_netlogon_samlogon_response(&io->out.netlogon); + } + + return NT_STATUS_OK; +} + +static NTSTATUS udp_ldap_rootdse(void *data, TALLOC_CTX *mem_ctx, + struct cldap_search *io) +{ + struct cldap_socket *cldap = talloc_get_type(data, + struct cldap_socket); + + return cldap_search(cldap, mem_ctx, io); +} + +static bool test_netlogon_extra_attrs(struct torture_context *tctx, + request_rootdse_t request_rootdse, + void *conn) +{ + struct cldap_search io; + NTSTATUS status; + const char *attrs[] = { + "netlogon", + "supportedCapabilities", + NULL + }; + const char *attrs2[] = { "netlogon", "*", NULL }; + struct ldb_message ldbmsg = { NULL, 0, NULL }; + + ZERO_STRUCT(io); + io.in.dest_address = NULL; + io.in.dest_port = 0; + io.in.timeout = 2; + io.in.retries = 2; + /* Additional attributes may be requested next to netlogon */ + torture_comment(tctx, "Requesting netlogon with additional attribute\n"); + io.in.filter = + talloc_asprintf(tctx, "(&" + "(NtVer=%s)(AAC=%s)" + /* Query for LDAP_CAP_ACTIVE_DIRECTORY_OID */ + "(supportedCapabilities=1.2.840.113556.1.4.800)" + ")", + ldap_encode_ndr_uint32(tctx, + NETLOGON_NT_VERSION_5EX), + ldap_encode_ndr_uint32(tctx, 0)); + torture_assert(tctx, io.in.filter != NULL, "OOM"); + io.in.attributes = attrs; + status = request_rootdse(conn, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert(tctx, io.out.response != NULL, "No Entries found."); + CHECK_VAL(io.out.response->num_attributes, 2); + + /* netlogon + '*' attr return zero results */ + torture_comment(tctx, "Requesting netlogon and '*' attributes\n"); + io.in.attributes = attrs2; + status = request_rootdse(conn, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert(tctx, io.out.response != NULL, "No Entries found."); + ldbmsg.num_elements = io.out.response->num_attributes; + ldbmsg.elements = io.out.response->attributes; + torture_assert(tctx, ldb_msg_find_element(&ldbmsg, "netlogon") != NULL, + "Attribute netlogon not found in Result Entry\n"); + + /* Wildcards are not allowed in filters when netlogon is requested. */ + torture_comment(tctx, "Requesting netlogon with invalid attr filter\n"); + io.in.filter = + talloc_asprintf(tctx, + "(&(NtVer=%s)(AAC=%s)(supportedCapabilities=*))", + ldap_encode_ndr_uint32(tctx, + NETLOGON_NT_VERSION_5EX), + ldap_encode_ndr_uint32(tctx, 0)); + torture_assert(tctx, io.in.filter != NULL, "OOM"); + io.in.attributes = attrs; + status = request_rootdse(conn, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert(tctx, io.out.response == NULL, + "A wildcard filter should return no entries."); + + return true; +} + +/* + Bug #11392: Huawei Unified Storage System S5500 V3 sends no NtVer + [MS-ADTS] Section 7.3.3.2 "Domain Controller Response to an LDAP Ping" +*/ +static bool test_netlogon_huawei(struct torture_context *tctx, + request_rootdse_t request_rootdse, + void *conn) +{ + struct cldap_search io; + struct netlogon_samlogon_response n1; + NTSTATUS status; + const char *attrs[] = { + "netlogon", + NULL + }; + struct ldb_message ldbmsg = { NULL, 0, NULL }; + + ZERO_STRUCT(io); + io.in.dest_address = NULL; + io.in.dest_port = 0; + io.in.timeout = 2; + io.in.retries = 2; + + torture_comment(tctx, "Requesting netlogon without NtVer filter\n"); + io.in.filter = talloc_asprintf(tctx, "(&(DnsDomain=%s))", + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, io.in.filter != NULL, "OOM"); + io.in.attributes = attrs; + status = request_rootdse(conn, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert(tctx, io.out.response != NULL, "No Entries found."); + CHECK_VAL(io.out.response->num_attributes, 1); + + ldbmsg.num_elements = io.out.response->num_attributes; + ldbmsg.elements = io.out.response->attributes; + torture_assert(tctx, ldb_msg_find_element(&ldbmsg, "netlogon") != NULL, + "Attribute netlogon not found in Result Entry\n"); + + status = pull_netlogon_samlogon_response( + io.out.response->attributes[0].values, + tctx, + &n1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(n1.ntver, NETLOGON_NT_VERSION_5); + + return true; +} + +bool torture_netlogon_tcp(struct torture_context *tctx) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + bool ret = true; + NTSTATUS status; + struct ldap_connection *conn; + TALLOC_CTX *mem_ctx; + const char *url; + + mem_ctx = talloc_init("torture_ldap_netlogon"); + + url = talloc_asprintf(mem_ctx, "ldap://%s/", host); + + status = torture_ldap_connection(tctx, &conn, url); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_ldap_netlogon(tctx, tcp_ldap_netlogon, conn, host); + ret &= test_ldap_netlogon_flags(tctx, tcp_ldap_netlogon, conn, host); + ret &= test_netlogon_extra_attrs(tctx, tcp_ldap_rootdse, conn); + + return ret; +} + +static NTSTATUS udp_ldap_netlogon(void *data, + TALLOC_CTX *mem_ctx, + struct cldap_netlogon *io) +{ + struct cldap_socket *cldap = talloc_get_type(data, + struct cldap_socket); + + return cldap_netlogon(cldap, mem_ctx, io); +} + +bool torture_netlogon_udp(struct torture_context *tctx) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *ip; + struct nbt_name nbt_name; + bool ret = true; + int r; + struct cldap_socket *cldap; + NTSTATUS status; + struct tsocket_address *dest_addr; + + make_nbt_name_server(&nbt_name, host); + + status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, &nbt_name, tctx, &ip, tctx->ev); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx,"Failed to resolve %s: %s", + nbt_name.name, nt_errstr(status))); + + r = tsocket_address_inet_from_strings(tctx, "ip", + ip, + lpcfg_cldap_port(tctx->lp_ctx), + &dest_addr); + CHECK_VAL(r, 0); + + /* cldap_socket_init should now know about the dest. address */ + status = cldap_socket_init(tctx, NULL, dest_addr, &cldap); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= test_ldap_netlogon(tctx, udp_ldap_netlogon, cldap, host); + ret &= test_ldap_netlogon_flags(tctx, udp_ldap_netlogon, cldap, host); + ret &= test_netlogon_extra_attrs(tctx, udp_ldap_rootdse, cldap); + ret &= test_netlogon_huawei(tctx, udp_ldap_rootdse, cldap); + + return ret; +} diff --git a/source4/torture/ldap/schema.c b/source4/torture/ldap/schema.c new file mode 100644 index 0000000..06313bc --- /dev/null +++ b/source4/torture/ldap/schema.c @@ -0,0 +1,408 @@ +/* + Unix SMB/CIFS Implementation. + LDAP schema tests + + Copyright (C) Stefan Metzmacher 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "libcli/ldap/ldap_client.h" +#include "lib/cmdline/cmdline.h" +#include "ldb_wrap.h" +#include "dsdb/samdb/samdb.h" +#include "../lib/util/dlinklist.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + + +struct test_rootDSE { + const char *defaultdn; + const char *rootdn; + const char *configdn; + const char *schemadn; +}; + +struct test_schema_ctx { + struct ldb_context *ldb; + + struct ldb_paged_control *ctrl; + uint32_t count; + bool pending; + + int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *); + void *private_data; +}; + +static bool test_search_rootDSE(struct ldb_context *ldb, struct test_rootDSE *root) +{ + int ret; + struct ldb_message *msg; + struct ldb_result *r; + + d_printf("Testing RootDSE Search\n"); + + ret = ldb_search(ldb, ldb, &r, ldb_dn_new(ldb, ldb, NULL), + LDB_SCOPE_BASE, NULL, NULL); + if (ret != LDB_SUCCESS) { + return false; + } else if (r->count != 1) { + talloc_free(r); + return false; + } + + msg = r->msgs[0]; + + root->defaultdn = ldb_msg_find_attr_as_string(msg, "defaultNamingContext", NULL); + talloc_steal(ldb, root->defaultdn); + root->rootdn = ldb_msg_find_attr_as_string(msg, "rootDomainNamingContext", NULL); + talloc_steal(ldb, root->rootdn); + root->configdn = ldb_msg_find_attr_as_string(msg, "configurationNamingContext", NULL); + talloc_steal(ldb, root->configdn); + root->schemadn = ldb_msg_find_attr_as_string(msg, "schemaNamingContext", NULL); + talloc_steal(ldb, root->schemadn); + + talloc_free(r); + + return true; +} + +static int test_schema_search_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct test_schema_ctx *actx; + int ret = LDB_SUCCESS; + + actx = talloc_get_type(req->context, struct test_schema_ctx); + + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_request_done(req, ares->error); + } + + switch (ares->type) { + case LDB_REPLY_ENTRY: + actx->count++; + ret = actx->callback(actx->private_data, actx->ldb, ares->message); + break; + + case LDB_REPLY_REFERRAL: + break; + + case LDB_REPLY_DONE: + if (ares->controls) { + struct ldb_paged_control *ctrl = NULL; + int i; + + for (i=0; ares->controls[i]; i++) { + if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[i]->oid) == 0) { + ctrl = talloc_get_type(ares->controls[i]->data, struct ldb_paged_control); + break; + } + } + + if (!ctrl) break; + + talloc_free(actx->ctrl->cookie); + actx->ctrl->cookie = talloc_steal(actx->ctrl->cookie, ctrl->cookie); + actx->ctrl->cookie_len = ctrl->cookie_len; + + if (actx->ctrl->cookie_len > 0) { + actx->pending = true; + } + } + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); + + default: + d_printf("%s: unknown Reply Type %u\n", __location__, ares->type); + return ldb_request_done(req, LDB_ERR_OTHER); + } + + if (talloc_free(ares) == -1) { + d_printf("talloc_free failed\n"); + actx->pending = 0; + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + if (ret) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + return LDB_SUCCESS; +} + +static bool test_create_schema_type(struct ldb_context *ldb, struct test_rootDSE *root, + const char *filter, + int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *), + void *private_data) +{ + struct ldb_control **ctrl; + struct ldb_paged_control *control; + struct ldb_request *req; + int ret; + struct test_schema_ctx *actx; + + actx = talloc(ldb, struct test_schema_ctx); + actx->ldb = ldb; + actx->private_data = private_data; + actx->callback= callback; + + ctrl = talloc_array(actx, struct ldb_control *, 2); + ctrl[0] = talloc(ctrl, struct ldb_control); + ctrl[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID; + ctrl[0]->critical = true; + control = talloc(ctrl[0], struct ldb_paged_control); + control->size = 1000; + control->cookie = NULL; + control->cookie_len = 0; + ctrl[0]->data = control; + ctrl[1] = NULL; + + ret = ldb_build_search_req(&req, ldb, actx, + ldb_dn_new(actx, ldb, root->schemadn), + LDB_SCOPE_SUBTREE, + filter, NULL, + ctrl, + actx, test_schema_search_callback, + NULL); + + actx->ctrl = control; + actx->count = 0; +again: + actx->pending = false; + + ret = ldb_request(ldb, req); + if (ret != LDB_SUCCESS) { + d_printf("search failed - %s\n", ldb_errstring(ldb)); + talloc_free(actx); + return false; + } + + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (ret != LDB_SUCCESS) { + d_printf("search error - %s\n", ldb_errstring(ldb)); + talloc_free(actx); + return false; + } + + if (actx->pending) + goto again; + + d_printf("filter[%s] count[%u]\n", filter, actx->count); + talloc_free(actx); + return true; +} + +static int test_add_attribute(void *ptr, struct ldb_context *ldb, struct ldb_message *msg) +{ + struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema); + WERROR status; + + status = dsdb_set_attribute_from_ldb(ldb, schema, msg); + if (!W_ERROR_IS_OK(status)) { + goto failed; + } + + return LDB_SUCCESS; +failed: + return LDB_ERR_OTHER; +} + +static int test_add_class(void *ptr, struct ldb_context *ldb, struct ldb_message *msg) +{ + struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema); + WERROR status; + + status = dsdb_set_class_from_ldb(schema, msg); + if (!W_ERROR_IS_OK(status)) { + goto failed; + } + + return LDB_SUCCESS; +failed: + return LDB_ERR_OTHER; +} + +static bool test_create_schema(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema **_schema) +{ + bool ret = true; + struct dsdb_schema *schema; + + schema = talloc_zero(ldb, struct dsdb_schema); + + d_printf("Fetching attributes...\n"); + ret &= test_create_schema_type(ldb, root, "(objectClass=attributeSchema)", + test_add_attribute, schema); + d_printf("Fetching objectClasses...\n"); + ret &= test_create_schema_type(ldb, root, "(objectClass=classSchema)", + test_add_class, schema); + + if (ret == true) { + *_schema = schema; + } + return ret; +} + +static bool test_dump_not_replicated(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema) +{ + struct dsdb_attribute *a; + uint32_t a_i = 1; + + d_printf("Dumping not replicated attributes\n"); + + for (a=schema->attributes; a; a = a->next) { + if (!(a->systemFlags & 0x00000001)) continue; + d_printf("attr[%4u]: '%s'\n", a_i++, + a->lDAPDisplayName); + } + + return true; +} + +static bool test_dump_partial(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema) +{ + struct dsdb_attribute *a; + uint32_t a_i = 1; + + d_printf("Dumping attributes which are provided by the global catalog\n"); + + for (a=schema->attributes; a; a = a->next) { + if (!(a->systemFlags & 0x00000002) && !a->isMemberOfPartialAttributeSet) continue; + d_printf("attr[%4u]: %u %u '%s'\n", a_i++, + a->systemFlags & 0x00000002, a->isMemberOfPartialAttributeSet, + a->lDAPDisplayName); + } + + return true; +} + +static bool test_dump_contructed(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema) +{ + struct dsdb_attribute *a; + uint32_t a_i = 1; + + d_printf("Dumping constructed attributes\n"); + + for (a=schema->attributes; a; a = a->next) { + if (!(a->systemFlags & 0x00000004)) continue; + d_printf("attr[%4u]: '%s'\n", a_i++, + a->lDAPDisplayName); + } + + return true; +} + +static bool test_dump_sorted_syntax(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema) +{ + struct dsdb_attribute *a; + uint32_t a_i = 1; + uint32_t i; + const char *syntaxes[] = { + "2.5.5.0", + "2.5.5.1", + "2.5.5.2", + "2.5.5.3", + "2.5.5.4", + "2.5.5.5", + "2.5.5.6", + "2.5.5.7", + "2.5.5.8", + "2.5.5.9", + "2.5.5.10", + "2.5.5.11", + "2.5.5.12", + "2.5.5.13", + "2.5.5.14", + "2.5.5.15", + "2.5.5.16", + "2.5.5.17" + }; + + d_printf("Dumping attribute syntaxes\n"); + + for (i=0; i < ARRAY_SIZE(syntaxes); i++) { + for (a=schema->attributes; a; a = a->next) { + char *om_hex; + + if (strcmp(syntaxes[i], a->attributeSyntax_oid) != 0) continue; + + om_hex = data_blob_hex_string_upper(ldb, &a->oMObjectClass); + if (!om_hex) { + return false; + } + + d_printf("attr[%4u]: %s %u '%s' '%s'\n", a_i++, + a->attributeSyntax_oid, a->oMSyntax, + om_hex, a->lDAPDisplayName); + talloc_free(om_hex); + } + } + + return true; +} + +static bool test_dump_not_in_filtered_replica(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema) +{ + struct dsdb_attribute *a; + uint32_t a_i = 1; + + d_printf("Dumping attributes not in filtered replica\n"); + + for (a=schema->attributes; a; a = a->next) { + if (!dsdb_attribute_is_attr_in_filtered_replica(a)) { + d_printf("attr[%4u]: '%s'\n", a_i++, + a->lDAPDisplayName); + } + } + return true; +} + +bool torture_ldap_schema(struct torture_context *torture) +{ + struct ldb_context *ldb; + bool ret = true; + const char *host = torture_setting_string(torture, "host", NULL); + char *url; + struct test_rootDSE rootDSE; + struct dsdb_schema *schema = NULL; + + ZERO_STRUCT(rootDSE); + + url = talloc_asprintf(torture, "ldap://%s/", host); + + ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + if (!ldb) goto failed; + + ret &= test_search_rootDSE(ldb, &rootDSE); + if (!ret) goto failed; + ret &= test_create_schema(ldb, &rootDSE, &schema); + if (!ret) goto failed; + + ret &= test_dump_not_replicated(ldb, &rootDSE, schema); + ret &= test_dump_partial(ldb, &rootDSE, schema); + ret &= test_dump_contructed(ldb, &rootDSE, schema); + ret &= test_dump_sorted_syntax(ldb, &rootDSE, schema); + ret &= test_dump_not_in_filtered_replica(ldb, &rootDSE, schema); + +failed: + return ret; +} diff --git a/source4/torture/ldap/session_expiry.c b/source4/torture/ldap/session_expiry.c new file mode 100644 index 0000000..e910662 --- /dev/null +++ b/source4/torture/ldap/session_expiry.c @@ -0,0 +1,122 @@ +/* + * Unix SMB/CIFS implementation. + * + * Test LDB attribute functions + * + * Copyright (C) Andrew Bartlet 2008-2009 + * Copyright (C) Matthieu Patou 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "lib/events/events.h" +#include +#include +#include "ldb_wrap.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "libcli/ldap/ldap_client.h" +#include "torture/smbtorture.h" +#include "torture/ldap/proto.h" + +bool torture_ldap_session_expiry(struct torture_context *torture) +{ + const char *host = torture_setting_string(torture, "host", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct ldb_context *ldb = NULL; + const char *url = NULL; + bool ret = false; + bool ok; + struct ldb_dn *rootdn = NULL; + struct ldb_result *result = NULL; + int rc = LDB_SUCCESS; + + /* + * Further down we request a ticket lifetime of 4 + * seconds. Give the server 10 seconds for this to kick in + */ + const struct timeval endtime = timeval_current_ofs(10, 0); + + url = talloc_asprintf(torture, "ldap://%s/", host); + torture_assert_goto( + torture, url!=NULL, ret, fail, "talloc_asprintf failed"); + + cli_credentials_set_kerberos_state(credentials, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + + ok = lpcfg_set_option( + torture->lp_ctx, "gensec_gssapi:requested_life_time=4"); + torture_assert_goto( + torture, ok, ret, fail, "lpcfg_set_option failed"); + + ldb = ldb_wrap_connect( + torture, + torture->ev, + torture->lp_ctx, + url, + NULL, + credentials, + 0); + torture_assert_goto( + torture, ldb!=NULL, ret, fail, "ldb_wrap_connect failed"); + + rootdn = ldb_dn_new(ldb, ldb, NULL); + torture_assert_goto( + torture, rootdn!=NULL, ret, fail, "ldb_dn_new failed"); + + rc = ldb_search( + ldb, /* ldb */ + ldb, /* mem_ctx */ + &result, /* result */ + rootdn, /* base */ + LDB_SCOPE_BASE, /* scope */ + NULL, /* attrs */ + "(objectclass=*)"); /* exp_fmt */ + torture_assert_goto( + torture, rc==LDB_SUCCESS, ret, fail, "1st ldb_search failed"); + + do { + smb_msleep(1000); + + rc = ldb_search( + ldb, /* ldb */ + ldb, /* mem_ctx */ + &result, /* result */ + rootdn, /* base */ + LDB_SCOPE_BASE, /* scope */ + NULL, /* attrs */ + "(objectclass=*)"); /* exp_fmt */ + printf("ldb_search returned %s\n", ldb_strerror(rc)); + TALLOC_FREE(result); + + if (rc != LDB_SUCCESS) { + break; + } + } while (!timeval_expired(&endtime)); + + torture_assert_goto( + torture, + rc==LDB_ERR_PROTOCOL_ERROR, + ret, + fail, + "expected LDB_ERR_PROTOCOL_ERROR after 4 seconds"); + + ret = true; +fail: + TALLOC_FREE(ldb); + return ret; +} diff --git a/source4/torture/ldap/uptodatevector.c b/source4/torture/ldap/uptodatevector.c new file mode 100644 index 0000000..01c85ab --- /dev/null +++ b/source4/torture/ldap/uptodatevector.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS Implementation. + LDAP replUpToDateVector tests + + Copyright (C) Stefan Metzmacher 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "includes.h" +#include "libcli/ldap/ldap_client.h" +#include "lib/cmdline/cmdline.h" +#include "ldb_wrap.h" +#include "dsdb/samdb/samdb.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#include "librpc/gen_ndr/ndr_drsblobs.h" + +#include "param/param.h" + +static bool test_check_uptodatevector(struct torture_context *torture, + struct ldb_context *ldb, + struct ldb_dn *partition_dn) +{ + bool ok = true; + uint32_t i; + int ret; + enum ndr_err_code ndr_err; + struct ldb_result *r; + const struct ldb_val *utdv_val1; + struct replUpToDateVectorBlob utdv1; + static const char *attrs[] = { + "uSNChanged", + "replUpToDateVector", + "description", + NULL + }; + + torture_comment(torture, "Check replUpToDateVector on partition[%s]\n", + ldb_dn_get_linearized(partition_dn)); + + ret = ldb_search(ldb, torture, &r, partition_dn, LDB_SCOPE_BASE, attrs, + "(objectClass=*)"); + if (ret != LDB_SUCCESS) { + return false; + } else if (r->count != 1) { + talloc_free(r); + return false; + } + + ZERO_STRUCT(utdv1); + utdv_val1 = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector"); + if (utdv_val1) { + ndr_err = ndr_pull_struct_blob_all(utdv_val1, torture, + &utdv1, + (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + } + + for (i=0; i < 2; i++) { + const struct ldb_val *utdv_val; + struct replUpToDateVectorBlob utdv; + struct ldb_message *msg; + char *description; + uint32_t j; + bool no_match = false; + + /* make a 'modify' msg, and only for serverReference */ + msg = ldb_msg_new(torture); + if (!msg) return false; + msg->dn = partition_dn; + + description = talloc_asprintf(msg, "torture replUpToDateVector[%u]", i); + if (!description) return false; + + ret = ldb_msg_add_string(msg, "description", description); + if (ret != 0) return false; + + for (j=0;jnum_elements;j++) { + msg->elements[j].flags = LDB_FLAG_MOD_REPLACE; + } + + ret = ldb_modify(ldb, msg); + if (ret != LDB_SUCCESS) return false; + + ret = ldb_search(ldb, msg, &r, partition_dn, LDB_SCOPE_BASE, + attrs, "(objectClass=*)"); + if (ret != LDB_SUCCESS) { + return false; + } else if (r->count != 1) { + talloc_free(r); + return false; + } + + ZERO_STRUCT(utdv); + utdv_val = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector"); + if (utdv_val) { + ndr_err = ndr_pull_struct_blob_all(utdv_val, torture, + &utdv, + (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + } + + if (!utdv_val1 && utdv_val) { + no_match = true; + } else if (utdv_val1 && !utdv_val) { + no_match = true; + } else if (!utdv_val1 && !utdv_val) { + } else if (utdv_val1->length != utdv_val->length) { + no_match = true; + } else if (utdv_val1->length && memcmp(utdv_val1->data, utdv_val->data, utdv_val->length) != 0) { + no_match = true; + } + + torture_comment(torture, "[%u]: uSNChanged[%llu] description[%s] replUpToDateVector[%s]\n", i, + (unsigned long long)ldb_msg_find_attr_as_uint64(r->msgs[0], "uSNChanged", 0), + ldb_msg_find_attr_as_string(r->msgs[0], "description", NULL), + (no_match ? "changed!: not ok" : "not changed: ok")); + + if (no_match) { + NDR_PRINT_DEBUG(replUpToDateVectorBlob, &utdv1); + NDR_PRINT_DEBUG(replUpToDateVectorBlob, &utdv); + ok = false; + } + + talloc_free(msg); + } + + return ok; +} + +bool torture_ldap_uptodatevector(struct torture_context *torture) +{ + struct ldb_context *ldb; + bool ret = true; + const char *host = torture_setting_string(torture, "host", NULL); + char *url; + + url = talloc_asprintf(torture, "ldap://%s/", host); + if (!url) goto failed; + + ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + if (!ldb) goto failed; + + ret &= test_check_uptodatevector(torture, ldb, ldb_get_default_basedn(ldb)); + ret &= test_check_uptodatevector(torture, ldb, ldb_get_config_basedn(ldb)); + ret &= test_check_uptodatevector(torture, ldb, ldb_get_schema_basedn(ldb)); + + return ret; +failed: + return false; +} diff --git a/source4/torture/ldb/ldb.c b/source4/torture/ldb/ldb.c new file mode 100644 index 0000000..69bd57e --- /dev/null +++ b/source4/torture/ldb/ldb.c @@ -0,0 +1,1794 @@ +/* + Unix SMB/CIFS implementation. + + Test LDB attribute functions + + Copyright (C) Andrew Bartlet 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include +#include +#include +#include +#include "lib/ldb-samba/ldif_handlers.h" +#include "ldb_wrap.h" +#include "dsdb/samdb/samdb.h" +#include "param/param.h" +#include "torture/smbtorture.h" +#include "torture/local/proto.h" +#include + +static const char *sid = "S-1-5-21-4177067393-1453636373-93818737"; +static const char *hex_sid = "01040000000000051500000081fdf8f815bba456718f9705"; +static const char *guid = "975ac5fa-35d9-431d-b86a-845bcd34fff9"; +static const char *guid2 = "{975ac5fa-35d9-431d-b86a-845bcd34fff9}"; +static const char *hex_guid = "fac55a97d9351d43b86a845bcd34fff9"; + +static const char *prefix_map_newline = "2:1.2.840.113556.1.2\n5:2.16.840.1.101.2.2.3"; +static const char *prefix_map_semi = "2:1.2.840.113556.1.2;5:2.16.840.1.101.2.2.3"; + +/** + * This is the hex code derived from the tdbdump for + * "st/ad_dc/private/sam.ldb.d/DC=ADDC,DC=SAMBA,DC=EXAMPLE,DC=COM.ldb" + * key "DN=CN=DDA1D01D-4BD7-4C49-A184-46F9241B560E,CN=OPERATIONS,CN=DOMAINUPDATES,CN=SYSTEM,DC=ADDC,DC=SAMBA,DC=EXAMPLE,DC=COM\00" + * -- adrianc + */ + +static const uint8_t dda1d01d_bin_v1[] = { + 0x67, 0x19, 0x01, 0x26, 0x0d, 0x00, 0x00, 0x00, 0x43, 0x4e, 0x3d, 0x64, 0x64, 0x61, 0x31, 0x64, + 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, 0x63, 0x34, 0x39, 0x2d, 0x61, 0x31, + 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, 0x62, 0x35, 0x36, 0x30, 0x65, 0x2c, + 0x43, 0x4e, 0x3d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x43, 0x4e, + 0x3d, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2c, 0x43, + 0x4e, 0x3d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x64, 0x63, + 0x2c, 0x44, 0x43, 0x3d, 0x73, 0x61, 0x6d, 0x62, 0x61, 0x2c, 0x44, 0x43, 0x3d, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x44, 0x43, 0x3d, 0x63, 0x6f, 0x6d, 0x00, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x74, 0x6f, 0x70, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x00, 0x63, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x64, 0x64, 0x61, + 0x31, 0x64, 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, 0x63, 0x34, 0x39, 0x2d, + 0x61, 0x31, 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, 0x62, 0x35, 0x36, 0x30, + 0x65, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x32, 0x30, 0x31, + 0x35, 0x30, 0x37, 0x30, 0x38, 0x32, 0x32, 0x34, 0x33, 0x31, 0x30, 0x2e, 0x30, 0x5a, 0x00, 0x77, + 0x68, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x32, 0x30, 0x31, 0x35, 0x30, 0x37, 0x30, 0x38, 0x32, 0x32, 0x34, 0x33, 0x31, + 0x30, 0x2e, 0x30, 0x5a, 0x00, 0x75, 0x53, 0x4e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x33, 0x34, 0x36, 0x37, 0x00, 0x75, 0x53, 0x4e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x33, 0x34, 0x36, 0x37, 0x00, 0x73, 0x68, 0x6f, 0x77, 0x49, 0x6e, 0x41, 0x64, 0x76, 0x61, 0x6e, + 0x63, 0x65, 0x64, 0x56, 0x69, 0x65, 0x77, 0x4f, 0x6e, 0x6c, 0x79, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x54, 0x52, 0x55, 0x45, 0x00, 0x6e, 0x54, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x18, 0x05, 0x00, 0x00, 0x01, 0x00, 0x17, 0x8c, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, 0x5e, + 0x1a, 0x4b, 0x00, 0x02, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, + 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, 0x5e, 0x1a, 0x4b, 0x00, 0x02, + 0x00, 0x00, 0x04, 0x00, 0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x5a, 0x38, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xbe, 0x3b, 0x0e, 0xf3, 0xf0, 0x9f, 0xd1, 0x11, 0xb6, 0x03, + 0x00, 0x00, 0xf8, 0x03, 0x67, 0xc1, 0xa5, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, + 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x5a, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xbf, 0x3b, + 0x0e, 0xf3, 0xf0, 0x9f, 0xd1, 0x11, 0xb6, 0x03, 0x00, 0x00, 0xf8, 0x03, 0x67, 0xc1, 0xa5, 0x7a, + 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x54, 0x04, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0xff, 0x01, 0x0f, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, 0x5e, + 0x1a, 0x4b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0xff, 0x01, 0x0f, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x94, 0x00, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x1a, + 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x16, 0x4c, 0xc0, 0x20, + 0xd0, 0x11, 0xa7, 0x68, 0x00, 0xaa, 0x00, 0x6e, 0x05, 0x29, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, + 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x16, 0x4c, 0xc0, 0x20, 0xd0, 0x11, 0xa7, 0x68, + 0x00, 0xaa, 0x00, 0x6e, 0x05, 0x29, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, + 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, + 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x10, 0x20, 0x20, 0x5f, 0xa5, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, + 0xd4, 0xcf, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, + 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, + 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x20, + 0x20, 0x5f, 0xa5, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0xba, 0x7a, + 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, + 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0xc2, 0x0a, 0xbc, 0xa9, 0x79, + 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, + 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0xc2, 0x0a, 0xbc, 0xa9, 0x79, 0xd0, 0x11, 0x90, 0x20, + 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, + 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, + 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x42, 0x2f, 0xba, 0x59, 0xa2, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, + 0xd3, 0xcf, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, + 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, + 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x42, 0x2f, + 0xba, 0x59, 0xa2, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd3, 0xcf, 0xba, 0x7a, + 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, + 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x88, 0x70, 0x03, 0xe1, 0x0a, + 0xd2, 0x11, 0xb4, 0x22, 0x00, 0xa0, 0xc9, 0x68, 0xf9, 0x39, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, + 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x88, 0x70, 0x03, 0xe1, 0x0a, 0xd2, 0x11, 0xb4, 0x22, + 0x00, 0xa0, 0xc9, 0x68, 0xf9, 0x39, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, + 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, + 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x6d, 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, 0xd2, 0x11, 0x85, 0x4e, 0x00, 0xa0, 0xc9, 0x83, + 0xf6, 0x08, 0x86, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, + 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x05, 0x1a, + 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6d, 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, + 0xd2, 0x11, 0x85, 0x4e, 0x00, 0xa0, 0xc9, 0x83, 0xf6, 0x08, 0x9c, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, + 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x05, 0x1a, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x6d, 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, 0xd2, 0x11, 0x85, 0x4e, 0x00, 0xa0, 0xc9, 0x83, + 0xf6, 0x08, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, + 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x05, 0x1a, + 0x2c, 0x00, 0x94, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, + 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x2c, 0x00, 0x94, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, + 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, + 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x2c, 0x00, 0x94, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x00, 0x00, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, + 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, + 0x00, 0x00, 0x05, 0x12, 0x28, 0x00, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xde, 0x47, + 0xe6, 0x91, 0x6f, 0xd9, 0x70, 0x4b, 0x95, 0x57, 0xd6, 0x3f, 0xf4, 0xf3, 0xcc, 0xd8, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x12, 0x24, 0x00, 0xff, 0x01, + 0x0f, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, + 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, 0x5e, 0x1a, 0x4b, 0x07, 0x02, 0x00, 0x00, 0x00, 0x12, + 0x18, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, + 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00, 0xbd, 0x01, 0x0f, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x6e, + 0x61, 0x6d, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x64, 0x64, 0x61, 0x31, + 0x64, 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, 0x63, 0x34, 0x39, 0x2d, 0x61, + 0x31, 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, 0x62, 0x35, 0x36, 0x30, 0x65, + 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x55, 0x49, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x57, 0x93, 0x1e, 0x29, 0x25, 0x49, 0xe5, 0x40, 0x9d, 0x98, 0x36, 0x07, + 0x11, 0x9e, 0xbd, 0xe5, 0x00, 0x72, 0x65, 0x70, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x79, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xa9, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x03, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, + 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, + 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x00, 0x01, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x55, 0x49, 0x44, 0x3d, + 0x35, 0x32, 0x34, 0x32, 0x39, 0x30, 0x33, 0x38, 0x2d, 0x65, 0x34, 0x33, 0x35, 0x2d, 0x34, 0x66, + 0x65, 0x33, 0x2d, 0x39, 0x36, 0x34, 0x65, 0x2d, 0x38, 0x30, 0x64, 0x61, 0x31, 0x35, 0x34, 0x39, + 0x39, 0x63, 0x39, 0x63, 0x3e, 0x3b, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2c, 0x43, 0x4e, 0x3d, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43, + 0x3d, 0x61, 0x64, 0x64, 0x63, 0x2c, 0x44, 0x43, 0x3d, 0x73, 0x61, 0x6d, 0x62, 0x61, 0x2c, 0x44, + 0x43, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x44, 0x43, 0x3d, 0x63, 0x6f, 0x6d, + 0x00 +}; + +static const uint8_t dda1d01d_bin_v2[] = { + 0x68, 0x19, 0x01, 0x26, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x43, 0x4e, 0x3d, 0x64, + 0x64, 0x61, 0x31, 0x64, 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, 0x63, 0x34, + 0x39, 0x2d, 0x61, 0x31, 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, 0x62, 0x35, + 0x36, 0x30, 0x65, 0x2c, 0x43, 0x4e, 0x3d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x44, 0x43, 0x3d, + 0x61, 0x64, 0x64, 0x63, 0x2c, 0x44, 0x43, 0x3d, 0x73, 0x61, 0x6d, 0x62, 0x61, 0x2c, 0x44, 0x43, + 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x44, 0x43, 0x3d, 0x63, 0x6f, 0x6d, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x61, 0x64, 0x64, 0x63, 0x2e, 0x73, 0x61, 0x6d, 0x62, 0x61, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x2f, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x64, 0x64, 0x61, 0x31, 0x64, + 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, 0x63, 0x34, 0x39, 0x2d, 0x61, 0x31, + 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, 0x62, 0x35, 0x36, 0x30, 0x65, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x03, 0x09, 0x02, 0x00, 0x00, 0x00, 0x63, + 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x24, 0x0c, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x0b, + 0x00, 0x00, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x75, + 0x53, 0x4e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, + 0x0a, 0x00, 0x00, 0x00, 0x75, 0x53, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x04, 0x16, 0x00, 0x00, 0x00, 0x73, 0x68, 0x6f, 0x77, 0x49, 0x6e, 0x41, + 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x56, 0x69, 0x65, 0x77, 0x4f, 0x6e, 0x6c, 0x79, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x14, 0x00, 0x00, 0x00, 0x6e, 0x54, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x18, 0x05, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x24, 0x0a, 0x00, 0x00, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, + 0x55, 0x49, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x14, 0x00, 0x00, 0x00, 0x72, 0x65, + 0x70, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, + 0x74, 0x61, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x90, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x76, 0x74, 0x6f, 0x70, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x00, 0x64, 0x64, 0x61, 0x31, 0x64, 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, 0x37, 0x2d, 0x34, + 0x63, 0x34, 0x39, 0x2d, 0x61, 0x31, 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, 0x32, 0x34, 0x31, + 0x62, 0x35, 0x36, 0x30, 0x65, 0x00, 0x34, 0x00, 0x32, 0x30, 0x31, 0x35, 0x30, 0x37, 0x30, 0x38, + 0x32, 0x32, 0x34, 0x33, 0x31, 0x30, 0x2e, 0x30, 0x5a, 0x00, 0x32, 0x30, 0x31, 0x35, 0x30, 0x37, + 0x30, 0x38, 0x32, 0x32, 0x34, 0x33, 0x31, 0x30, 0x2e, 0x30, 0x5a, 0x00, 0x33, 0x34, 0x36, 0x37, + 0x00, 0x33, 0x34, 0x36, 0x37, 0x00, 0x54, 0x52, 0x55, 0x45, 0x00, 0x01, 0x00, 0x14, 0x8c, 0x14, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x01, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, + 0xe0, 0x11, 0x3c, 0x6e, 0x5e, 0x1a, 0x4b, 0x00, 0x02, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, + 0x5e, 0x1a, 0x4b, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, + 0x5a, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xbe, 0x3b, 0x0e, 0xf3, 0xf0, + 0x9f, 0xd1, 0x11, 0xb6, 0x03, 0x00, 0x00, 0xf8, 0x03, 0x67, 0xc1, 0xa5, 0x7a, 0x96, 0xbf, 0xe6, + 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5a, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0xbf, 0x3b, 0x0e, 0xf3, 0xf0, 0x9f, 0xd1, 0x11, 0xb6, 0x03, 0x00, 0x00, 0xf8, + 0x03, 0x67, 0xc1, 0xa5, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, + 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x54, 0x04, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0xff, 0x01, 0x0f, 0x00, 0x01, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, + 0xe0, 0x11, 0x3c, 0x6e, 0x5e, 0x1a, 0x4b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0xff, + 0x01, 0x0f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x94, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0b, + 0x00, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x16, 0x4c, 0xc0, 0x20, 0xd0, 0x11, 0xa7, 0x68, 0x00, 0xaa, 0x00, 0x6e, 0x05, 0x29, 0x14, + 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, + 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x16, 0x4c, 0xc0, + 0x20, 0xd0, 0x11, 0xa7, 0x68, 0x00, 0xaa, 0x00, 0x6e, 0x05, 0x29, 0xba, 0x7a, 0x96, 0xbf, 0xe6, + 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x20, 0x20, 0x5f, 0xa5, 0x79, 0xd0, 0x11, 0x90, + 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, + 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, + 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x10, 0x20, 0x20, 0x5f, 0xa5, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, + 0xc2, 0xd4, 0xcf, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, + 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, + 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, + 0xc2, 0x0a, 0xbc, 0xa9, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0x14, + 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, + 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0xc2, 0x0a, 0xbc, 0xa9, + 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd4, 0xcf, 0xba, 0x7a, 0x96, 0xbf, 0xe6, + 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x42, 0x2f, 0xba, 0x59, 0xa2, 0x79, 0xd0, 0x11, 0x90, + 0x20, 0x00, 0xc0, 0x4f, 0xc2, 0xd3, 0xcf, 0x14, 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, + 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, + 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x42, 0x2f, 0xba, 0x59, 0xa2, 0x79, 0xd0, 0x11, 0x90, 0x20, 0x00, 0xc0, 0x4f, + 0xc2, 0xd3, 0xcf, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, + 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, + 0x02, 0x00, 0x00, 0x05, 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, + 0x88, 0x70, 0x03, 0xe1, 0x0a, 0xd2, 0x11, 0xb4, 0x22, 0x00, 0xa0, 0xc9, 0x68, 0xf9, 0x39, 0x14, + 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, + 0x1a, 0x3c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x88, 0x70, 0x03, 0xe1, + 0x0a, 0xd2, 0x11, 0xb4, 0x22, 0x00, 0xa0, 0xc9, 0x68, 0xf9, 0x39, 0xba, 0x7a, 0x96, 0xbf, 0xe6, + 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x38, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6d, 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, 0xd2, 0x11, 0x85, + 0x4e, 0x00, 0xa0, 0xc9, 0x83, 0xf6, 0x08, 0x86, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, + 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, + 0x00, 0x00, 0x00, 0x05, 0x1a, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6d, + 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, 0xd2, 0x11, 0x85, 0x4e, 0x00, 0xa0, 0xc9, 0x83, 0xf6, 0x08, 0x9c, + 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x05, 0x1a, 0x38, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6d, 0x9e, 0xc6, 0xb7, 0xc7, 0x2c, 0xd2, 0x11, 0x85, + 0x4e, 0x00, 0xa0, 0xc9, 0x83, 0xf6, 0x08, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, + 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, + 0x00, 0x00, 0x00, 0x05, 0x1a, 0x2c, 0x00, 0x94, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0xcc, 0x28, 0x48, 0x37, 0x14, 0xbc, 0x45, 0x9b, 0x07, 0xad, 0x6f, 0x01, 0x5e, 0x5f, 0x28, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, + 0x1a, 0x2c, 0x00, 0x94, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x7a, 0x96, 0xbf, 0xe6, + 0x0d, 0xd0, 0x11, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x1a, 0x2c, 0x00, 0x94, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0xba, 0x7a, 0x96, 0xbf, 0xe6, 0x0d, 0xd0, 0x11, 0xa2, + 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, + 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x05, 0x12, 0x28, 0x00, 0x30, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xde, 0x47, 0xe6, 0x91, 0x6f, 0xd9, 0x70, 0x4b, 0x95, 0x57, 0xd6, 0x3f, 0xf4, + 0xf3, 0xcc, 0xd8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x24, 0x00, 0xff, 0x01, 0x0f, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, + 0x00, 0x00, 0x00, 0x9a, 0xbd, 0x91, 0x7d, 0xd5, 0xe0, 0x11, 0x3c, 0x6e, 0x5e, 0x1a, 0x4b, 0x07, + 0x02, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00, 0xbd, + 0x01, 0x0f, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x02, 0x00, 0x00, 0x00, 0x64, 0x64, 0x61, 0x31, 0x64, 0x30, 0x31, 0x64, 0x2d, 0x34, 0x62, 0x64, + 0x37, 0x2d, 0x34, 0x63, 0x34, 0x39, 0x2d, 0x61, 0x31, 0x38, 0x34, 0x2d, 0x34, 0x36, 0x66, 0x39, + 0x32, 0x34, 0x31, 0x62, 0x35, 0x36, 0x30, 0x65, 0x00, 0x57, 0x93, 0x1e, 0x29, 0x25, 0x49, 0xe5, + 0x40, 0x9d, 0x98, 0x36, 0x07, 0x11, 0x9e, 0xbd, 0xe5, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x01, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x03, 0x09, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7e, 0x38, 0xae, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x9d, 0xcd, 0xcd, 0x57, 0xee, 0x58, + 0x6e, 0x4e, 0x96, 0x99, 0xcc, 0x7d, 0xe1, 0x96, 0xf1, 0x05, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x55, 0x49, 0x44, + 0x3d, 0x35, 0x32, 0x34, 0x32, 0x39, 0x30, 0x33, 0x38, 0x2d, 0x65, 0x34, 0x33, 0x35, 0x2d, 0x34, + 0x66, 0x65, 0x33, 0x2d, 0x39, 0x36, 0x34, 0x65, 0x2d, 0x38, 0x30, 0x64, 0x61, 0x31, 0x35, 0x34, + 0x39, 0x39, 0x63, 0x39, 0x63, 0x3e, 0x3b, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2c, 0x43, 0x4e, + 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, + 0x43, 0x3d, 0x61, 0x64, 0x64, 0x63, 0x2c, 0x44, 0x43, 0x3d, 0x73, 0x61, 0x6d, 0x62, 0x61, 0x2c, + 0x44, 0x43, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x44, 0x43, 0x3d, 0x63, 0x6f, + 0x6d, 0x00 +}; + +static const char dda1d01d_ldif[] = "" +"dn: CN=dda1d01d-4bd7-4c49-a184-46f9241b560e,CN=Operations,CN=DomainUpdates,CN=System,DC=addc,DC=samba,DC=example,DC=com\n" +"objectClass: top\n" +"objectClass: container\n" +"cn: dda1d01d-4bd7-4c49-a184-46f9241b560e\n" +"instanceType: 4\n" +"whenCreated: 20150708224310.0Z\n" +"whenChanged: 20150708224310.0Z\n" +"uSNCreated: 3467\n" +"uSNChanged: 3467\n" +"showInAdvancedViewOnly: TRUE\n" +"nTSecurityDescriptor: O:S-1-5-21-2106703258-1007804629-1260019310-512G:S-1-5-2\n" +" 1-2106703258-1007804629-1260019310-512D:AI(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-\n" +" 1-5-21-2106703258-1007804629-1260019310-512)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;\n" +" SY)(A;;LCRPLORC;;;AU)(OA;CIIOID;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828c\n" +" c14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;4c164200-20c0-11d0-a768-00aa\n" +" 006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;5f202010-79a5-\n" +" 11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;\n" +" 5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)\n" +" (OA;CIIOID;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad\n" +" 6f015e5f28;RU)(OA;CIIOID;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de\n" +" 6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3c\n" +" f;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;59ba2f42-79a2-11d0-90\n" +" 20-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;037088f\n" +" 8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CII\n" +" OID;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa00304\n" +" 9e2;RU)(OA;CIIOID;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-\n" +" a285-00aa003049e2;ED)(OA;CIIOID;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967\n" +" a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIOID;RP;b7c69e6d-2cc7-11d2-854e-00a0\n" +" c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIOID;LCRPLORC;;4828cc1\n" +" 4-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;LCRPLORC;;bf967a9c-0de6-11d0-a285\n" +" -00aa003049e2;RU)(OA;CIIOID;LCRPLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU\n" +" )(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(A;CIID;CCDCLCSWRPW\n" +" PDTLOCRSDRCWDWO;;;S-1-5-21-2106703258-1007804629-1260019310-519)(A;CIID;LC;;;\n" +" RU)(A;CIID;CCLCSWRPWPLOCRSDRCWDWO;;;BA)S:AI(OU;CIIOIDSA;WP;f30e3bbe-9ff0-11d1\n" +" -b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIOIDSA;WP;f3\n" +" 0e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)\n" +"name: dda1d01d-4bd7-4c49-a184-46f9241b560e\n" +"objectGUID: 291e9357-4925-40e5-9d98-3607119ebde5\n" +"replPropertyMetaData:: AQAAAAAAAAAIAAAAAAAAAAAAAAABAAAAfjiuCwMAAACdzc1X7lhuTpa\n" +" ZzH3hlvEFiw0AAAAAAACLDQAAAAAAAAEAAgABAAAAfjiuCwMAAACdzc1X7lhuTpaZzH3hlvEFiw0A\n" +" AAAAAACLDQAAAAAAAAIAAgABAAAAfjiuCwMAAACdzc1X7lhuTpaZzH3hlvEFiw0AAAAAAACLDQAAA\n" +" AAAAKkAAgABAAAAfjiuCwMAAACdzc1X7lhuTpaZzH3hlvEFiw0AAAAAAACLDQAAAAAAABkBAgABAA\n" +" AAfjiuCwMAAACdzc1X7lhuTpaZzH3hlvEFiw0AAAAAAACLDQAAAAAAAAEACQABAAAAfjiuCwMAAAC\n" +" dzc1X7lhuTpaZzH3hlvEFiw0AAAAAAACLDQAAAAAAAA4DCQABAAAAfjiuCwMAAACdzc1X7lhuTpaZ\n" +" zH3hlvEFiw0AAAAAAACLDQAAAAAAAAMAAAABAAAAfjiuCwMAAACdzc1X7lhuTpaZzH3hlvEFiw0AA\n" +" AAAAACLDQAAAAAAAA==\n" +"objectCategory: ;CN=Container,CN=Sc\n" +" hema,CN=Configuration,DC=addc,DC=samba,DC=example,DC=com\n\n"; + +static const char *dda1d01d_ldif_reduced = "" +"dn: CN=dda1d01d-4bd7-4c49-a184-46f9241b560e,CN=Operations,CN=DomainUpdates,CN=System,DC=addc,DC=samba,DC=example,DC=com\n" +"objectClass: top\n" +"objectClass: container\n" +"instanceType: 4\n" +"whenChanged: 20150708224310.0Z\n" +"uSNCreated: 3467\n" +"showInAdvancedViewOnly: TRUE\n" +"name: dda1d01d-4bd7-4c49-a184-46f9241b560e\n\n"; + +static bool torture_ldb_attrs(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + const struct ldb_schema_attribute *attr; + struct ldb_val string_sid_blob, binary_sid_blob; + struct ldb_val string_guid_blob, string_guid_blob2, binary_guid_blob; + struct ldb_val string_prefix_map_newline_blob, string_prefix_map_semi_blob, string_prefix_map_blob; + struct ldb_val prefix_map_blob; + + DATA_BLOB sid_blob = strhex_to_data_blob(mem_ctx, hex_sid); + DATA_BLOB guid_blob = strhex_to_data_blob(mem_ctx, hex_guid); + + torture_assert(torture, + ldb = ldb_init(mem_ctx, torture->ev), + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_register_samba_handlers(ldb), LDB_SUCCESS, + "Failed to register Samba handlers"); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + /* Test SID behaviour */ + torture_assert(torture, attr = ldb_schema_attribute_by_name(ldb, "objectSid"), + "Failed to get objectSid schema attribute"); + + string_sid_blob = data_blob_string_const(sid); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, + &string_sid_blob, &binary_sid_blob), 0, + "Failed to parse string SID"); + + torture_assert_data_blob_equal(torture, binary_sid_blob, sid_blob, + "Read SID into blob form failed"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, + &sid_blob, &binary_sid_blob), -1, + "Should have failed to parse binary SID"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_write_fn(ldb, mem_ctx, &binary_sid_blob, &string_sid_blob), 0, + "Failed to parse binary SID"); + + torture_assert_data_blob_equal(torture, + string_sid_blob, data_blob_string_const(sid), + "Write SID into string form failed"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &binary_sid_blob, &string_sid_blob), 0, + "Failed to compare binary and string SID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_sid_blob, &binary_sid_blob), 0, + "Failed to compare string and binary binary SID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_sid_blob, &string_sid_blob), 0, + "Failed to compare string and string SID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &binary_sid_blob, &binary_sid_blob), 0, + "Failed to compare binary and binary SID"); + + torture_assert(torture, attr->syntax->comparison_fn(ldb, mem_ctx, &guid_blob, &binary_sid_blob) != 0, + "Failed to distinguish binary GUID and binary SID"); + + + /* Test GUID behaviour */ + torture_assert(torture, attr = ldb_schema_attribute_by_name(ldb, "objectGUID"), + "Failed to get objectGUID schema attribute"); + + string_guid_blob = data_blob_string_const(guid); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, + &string_guid_blob, &binary_guid_blob), 0, + "Failed to parse string GUID"); + + torture_assert_data_blob_equal(torture, binary_guid_blob, guid_blob, + "Read GUID into blob form failed"); + + string_guid_blob2 = data_blob_string_const(guid2); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, + &string_guid_blob2, &binary_guid_blob), 0, + "Failed to parse string GUID"); + + torture_assert_data_blob_equal(torture, binary_guid_blob, guid_blob, + "Read GUID into blob form failed"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, + &guid_blob, &binary_guid_blob), 0, + "Failed to parse binary GUID"); + + torture_assert_data_blob_equal(torture, binary_guid_blob, guid_blob, + "Read GUID into blob form failed"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_write_fn(ldb, mem_ctx, &binary_guid_blob, &string_guid_blob), 0, + "Failed to print binary GUID as string"); + + torture_assert_data_blob_equal(torture, string_sid_blob, data_blob_string_const(sid), + "Write SID into string form failed"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &binary_guid_blob, &string_guid_blob), 0, + "Failed to compare binary and string GUID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_guid_blob, &binary_guid_blob), 0, + "Failed to compare string and binary binary GUID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_guid_blob, &string_guid_blob), 0, + "Failed to compare string and string GUID"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &binary_guid_blob, &binary_guid_blob), 0, + "Failed to compare binary and binary GUID"); + + string_prefix_map_newline_blob = data_blob_string_const(prefix_map_newline); + + string_prefix_map_semi_blob = data_blob_string_const(prefix_map_semi); + + /* Test prefixMap behaviour */ + torture_assert(torture, attr = ldb_schema_attribute_by_name(ldb, "prefixMap"), + "Failed to get prefixMap schema attribute"); + + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_prefix_map_newline_blob, &string_prefix_map_semi_blob), 0, + "Failed to compare prefixMap with newlines and prefixMap with semicolons"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_read_fn(ldb, mem_ctx, &string_prefix_map_newline_blob, &prefix_map_blob), 0, + "Failed to read prefixMap with newlines"); + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_prefix_map_newline_blob, &prefix_map_blob), 0, + "Failed to compare prefixMap with newlines and prefixMap binary"); + + torture_assert_int_equal(torture, + attr->syntax->ldif_write_fn(ldb, mem_ctx, &prefix_map_blob, &string_prefix_map_blob), 0, + "Failed to write prefixMap"); + torture_assert_int_equal(torture, + attr->syntax->comparison_fn(ldb, mem_ctx, &string_prefix_map_blob, &prefix_map_blob), 0, + "Failed to compare prefixMap ldif write and prefixMap binary"); + + torture_assert_data_blob_equal(torture, string_prefix_map_blob, string_prefix_map_semi_blob, + "Failed to compare prefixMap ldif write and prefixMap binary"); + + + + talloc_free(mem_ctx); + return true; +} + +static bool torture_ldb_dn_attrs(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + const struct ldb_dn_extended_syntax *attr; + struct ldb_val string_sid_blob, binary_sid_blob; + struct ldb_val string_guid_blob, binary_guid_blob; + struct ldb_val hex_sid_blob, hex_guid_blob; + + DATA_BLOB sid_blob = strhex_to_data_blob(mem_ctx, hex_sid); + DATA_BLOB guid_blob = strhex_to_data_blob(mem_ctx, hex_guid); + + torture_assert(torture, + ldb = ldb_init(mem_ctx, torture->ev), + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_register_samba_handlers(ldb), LDB_SUCCESS, + "Failed to register Samba handlers"); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + /* Test SID behaviour */ + torture_assert(torture, attr = ldb_dn_extended_syntax_by_name(ldb, "SID"), + "Failed to get SID DN syntax"); + + string_sid_blob = data_blob_string_const(sid); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &string_sid_blob, &binary_sid_blob), 0, + "Failed to parse string SID"); + + torture_assert_data_blob_equal(torture, binary_sid_blob, sid_blob, + "Read SID into blob form failed"); + + hex_sid_blob = data_blob_string_const(hex_sid); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &hex_sid_blob, &binary_sid_blob), 0, + "Failed to parse HEX SID"); + + torture_assert_data_blob_equal(torture, binary_sid_blob, sid_blob, + "Read SID into blob form failed"); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &sid_blob, &binary_sid_blob), -1, + "Should have failed to parse binary SID"); + + torture_assert_int_equal(torture, + attr->write_hex_fn(ldb, mem_ctx, &sid_blob, &hex_sid_blob), 0, + "Failed to parse binary SID"); + + torture_assert_data_blob_equal(torture, + hex_sid_blob, data_blob_string_const(hex_sid), + "Write SID into HEX string form failed"); + + torture_assert_int_equal(torture, + attr->write_clear_fn(ldb, mem_ctx, &sid_blob, &string_sid_blob), 0, + "Failed to parse binary SID"); + + torture_assert_data_blob_equal(torture, + string_sid_blob, data_blob_string_const(sid), + "Write SID into clear string form failed"); + + + /* Test GUID behaviour */ + torture_assert(torture, attr = ldb_dn_extended_syntax_by_name(ldb, "GUID"), + "Failed to get GUID DN syntax"); + + string_guid_blob = data_blob_string_const(guid); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &string_guid_blob, &binary_guid_blob), 0, + "Failed to parse string GUID"); + + torture_assert_data_blob_equal(torture, binary_guid_blob, guid_blob, + "Read GUID into blob form failed"); + + hex_guid_blob = data_blob_string_const(hex_guid); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &hex_guid_blob, &binary_guid_blob), 0, + "Failed to parse HEX GUID"); + + torture_assert_data_blob_equal(torture, binary_guid_blob, guid_blob, + "Read GUID into blob form failed"); + + torture_assert_int_equal(torture, + attr->read_fn(ldb, mem_ctx, + &guid_blob, &binary_guid_blob), -1, + "Should have failed to parse binary GUID"); + + torture_assert_int_equal(torture, + attr->write_hex_fn(ldb, mem_ctx, &guid_blob, &hex_guid_blob), 0, + "Failed to parse binary GUID"); + + torture_assert_data_blob_equal(torture, + hex_guid_blob, data_blob_string_const(hex_guid), + "Write GUID into HEX string form failed"); + + torture_assert_int_equal(torture, + attr->write_clear_fn(ldb, mem_ctx, &guid_blob, &string_guid_blob), 0, + "Failed to parse binary GUID"); + + torture_assert_data_blob_equal(torture, + string_guid_blob, data_blob_string_const(guid), + "Write GUID into clear string form failed"); + + + + talloc_free(mem_ctx); + return true; +} + +static bool torture_ldb_dn_extended(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_dn *dn, *dn2; + + DATA_BLOB sid_blob = strhex_to_data_blob(mem_ctx, hex_sid); + DATA_BLOB guid_blob = strhex_to_data_blob(mem_ctx, hex_guid); + + const char *dn_str = "cn=admin,cn=users,dc=samba,dc=org"; + + torture_assert(torture, + ldb = ldb_init(mem_ctx, torture->ev), + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_register_samba_handlers(ldb), LDB_SUCCESS, + "Failed to register Samba handlers"); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + /* Check behaviour of a normal DN */ + torture_assert(torture, + dn = ldb_dn_new(mem_ctx, ldb, dn_str), + "Failed to create a 'normal' DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'normal' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == false, + "Should not find plain DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") == NULL, + "Should not find an SID on plain DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") == NULL, + "Should not find an GUID on plain DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "WKGUID") == NULL, + "Should not find an WKGUID on plain DN"); + + /* Now make an extended DN */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";;%s", + guid, sid, dn_str), + "Failed to create an 'extended' DN"); + + torture_assert(torture, + dn2 = ldb_dn_copy(mem_ctx, dn), + "Failed to copy the 'extended' DN"); + talloc_free(dn); + dn = dn2; + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'extended' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == true, + "Should find extended DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") != NULL, + "Should find an SID on extended DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") != NULL, + "Should find an GUID on extended DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "SID"), sid_blob, + "Extended DN SID incorrect"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob, + "Extended DN GUID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), dn_str, + "linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_casefold(dn), strupper_talloc(mem_ctx, dn_str), + "casefolded DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_component_name(dn, 0), "cn", + "component zero incorrect"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_component_val(dn, 0), data_blob_string_const("admin"), + "component zero incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 1), + talloc_asprintf(mem_ctx, ";;%s", + guid, sid, dn_str), + "Clear extended linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0), + talloc_asprintf(mem_ctx, ";;%s", + hex_guid, hex_sid, dn_str), + "HEX extended linearized DN incorrect"); + + torture_assert(torture, ldb_dn_remove_child_components(dn, 1) == true, + "Failed to remove DN child"); + + torture_assert(torture, ldb_dn_has_extended(dn) == false, + "Extended DN flag should be cleared after child element removal"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") == NULL, + "Should not find an SID on DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") == NULL, + "Should not find an GUID on DN"); + + + /* TODO: test setting these in the other order, and ensure it still comes out 'GUID first' */ + torture_assert_int_equal(torture, ldb_dn_set_extended_component(dn, "GUID", &guid_blob), 0, + "Failed to set a GUID on DN"); + + torture_assert_int_equal(torture, ldb_dn_set_extended_component(dn, "SID", &sid_blob), 0, + "Failed to set a SID on DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "SID"), sid_blob, + "Extended DN SID incorrect"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob, + "Extended DN GUID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "cn=users,dc=samba,dc=org", + "linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 1), + talloc_asprintf(mem_ctx, ";;%s", + guid, sid, "cn=users,dc=samba,dc=org"), + "Clear extended linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0), + talloc_asprintf(mem_ctx, ";;%s", + hex_guid, hex_sid, "cn=users,dc=samba,dc=org"), + "HEX extended linearized DN incorrect"); + + /* Now check a 'just GUID' DN (clear format) */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, "", + guid), + "Failed to create an 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'extended' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == true, + "Should find extended DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") == NULL, + "Should not find an SID on this DN"); + + torture_assert_int_equal(torture, ldb_dn_get_comp_num(dn), 0, + "Should not find an 'normal' component on this DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") != NULL, + "Should find an GUID on this DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob, + "Extended DN GUID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "", + "linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 1), + talloc_asprintf(mem_ctx, "", + guid), + "Clear extended linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0), + talloc_asprintf(mem_ctx, "", + hex_guid), + "HEX extended linearized DN incorrect"); + + /* Now check a 'just GUID' DN (HEX format) */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, "", + hex_guid), + "Failed to create an 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'extended' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == true, + "Should find extended DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") == NULL, + "Should not find an SID on this DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") != NULL, + "Should find an GUID on this DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob, + "Extended DN GUID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "", + "linearized DN incorrect"); + + /* Now check a 'just SID' DN (clear format) */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, "", + sid), + "Failed to create an 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'extended' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == true, + "Should find extended DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") == NULL, + "Should not find an SID on this DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") != NULL, + "Should find an SID on this DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "SID"), sid_blob, + "Extended DN SID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "", + "linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 1), + talloc_asprintf(mem_ctx, "", + sid), + "Clear extended linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0), + talloc_asprintf(mem_ctx, "", + hex_sid), + "HEX extended linearized DN incorrect"); + + /* Now check a 'just SID' DN (HEX format) */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, "", + hex_sid), + "Failed to create an 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate 'extended' DN"); + + torture_assert(torture, ldb_dn_has_extended(dn) == true, + "Should find extended DN to be 'extended'"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "GUID") == NULL, + "Should not find an SID on this DN"); + + torture_assert(torture, ldb_dn_get_extended_component(dn, "SID") != NULL, + "Should find an SID on this DN"); + + torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "SID"), sid_blob, + "Extended DN SID incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "", + "linearized DN incorrect"); + + talloc_free(mem_ctx); + return true; +} + + +static bool torture_ldb_dn(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_dn *dn; + struct ldb_dn *child_dn; + struct ldb_dn *typo_dn; + struct ldb_dn *special_dn; + struct ldb_val val; + + torture_assert(torture, + ldb = ldb_init(mem_ctx, torture->ev), + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_register_samba_handlers(ldb), LDB_SUCCESS, + "Failed to register Samba handlers"); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + /* Check behaviour of a normal DN */ + torture_assert(torture, + dn = ldb_dn_new(mem_ctx, ldb, NULL), + "Failed to create a NULL DN"); + + torture_assert(torture, + ldb_dn_validate(dn), + "Failed to validate NULL DN"); + + torture_assert(torture, + ldb_dn_add_base_fmt(dn, "dc=org"), + "Failed to add base DN"); + + torture_assert(torture, + ldb_dn_add_child_fmt(dn, "dc=samba"), + "Failed to add base DN"); + + torture_assert_str_equal(torture, ldb_dn_get_linearized(dn), "dc=samba,dc=org", + "linearized DN incorrect"); + + torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0), "dc=samba,dc=org", + "extended linearized DN incorrect"); + + /* Check child DN comparisons */ + torture_assert(torture, + child_dn = ldb_dn_new(mem_ctx, ldb, "CN=users,DC=SAMBA,DC=org"), + "Failed to create child DN"); + + torture_assert(torture, + ldb_dn_compare(dn, child_dn) != 0, + "Comparison on dc=samba,dc=org and CN=users,DC=SAMBA,DC=org should != 0"); + + torture_assert(torture, + ldb_dn_compare_base(child_dn, dn) != 0, + "Base Comparison of CN=users,DC=SAMBA,DC=org and dc=samba,dc=org should != 0"); + + torture_assert(torture, + ldb_dn_compare_base(dn, child_dn) == 0, + "Base Comparison on dc=samba,dc=org and CN=users,DC=SAMBA,DC=org should == 0"); + + /* Check comparisons with a truncated DN */ + torture_assert(torture, + typo_dn = ldb_dn_new(mem_ctx, ldb, "c=samba,dc=org"), + "Failed to create 'typo' DN"); + + torture_assert(torture, + ldb_dn_compare(dn, typo_dn) != 0, + "Comparison on dc=samba,dc=org and c=samba,dc=org should != 0"); + + torture_assert(torture, + ldb_dn_compare_base(typo_dn, dn) != 0, + "Base Comparison of c=samba,dc=org and dc=samba,dc=org should != 0"); + + torture_assert(torture, + ldb_dn_compare_base(dn, typo_dn) != 0, + "Base Comparison on dc=samba,dc=org and c=samba,dc=org should != 0"); + + /* Check comparisons with a special DN */ + torture_assert(torture, + special_dn = ldb_dn_new(mem_ctx, ldb, "@special_dn"), + "Failed to create 'special' DN"); + + torture_assert(torture, + ldb_dn_compare(dn, special_dn) != 0, + "Comparison on dc=samba,dc=org and @special_dn should != 0"); + + torture_assert(torture, + ldb_dn_compare_base(special_dn, dn) > 0, + "Base Comparison of @special_dn and dc=samba,dc=org should > 0"); + + torture_assert(torture, + ldb_dn_compare_base(dn, special_dn) < 0, + "Base Comparison on dc=samba,dc=org and @special_dn should < 0"); + + /* Check DN based on MS-ADTS:3.1.1.5.1.2 Naming Constraints*/ + torture_assert(torture, + dn = ldb_dn_new(mem_ctx, ldb, "CN=New\nLine,DC=SAMBA,DC=org"), + "Failed to create a DN with 0xA in it"); + + /* this is a warning until we work out how the DEL: CNs work */ + if (ldb_dn_validate(dn) != false) { + torture_warning(torture, + "should have failed to validate a DN with 0xA in it"); + } + + /* Escaped comma */ + torture_assert(torture, + dn = ldb_dn_new(mem_ctx, ldb, "CN=A\\,comma,DC=SAMBA,DC=org"), + "Failed to create a DN with an escaped comma in it"); + + + val = data_blob_const("CN=Zer\0,DC=SAMBA,DC=org", 23); + torture_assert(torture, + NULL == ldb_dn_from_ldb_val(mem_ctx, ldb, &val), + "should fail to create a DN with 0x0 in it"); + + talloc_free(mem_ctx); + return true; +} + +static bool torture_ldb_dn_invalid_extended(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_dn *dn; + + const char *dn_str = "cn=admin,cn=users,dc=samba,dc=org"; + + torture_assert(torture, + ldb = ldb_init(mem_ctx, torture->ev), + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_register_samba_handlers(ldb), LDB_SUCCESS, + "Failed to register Samba handlers"); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + /* Check behaviour of a normal DN */ + torture_assert(torture, + dn = ldb_dn_new(mem_ctx, ldb, "samba,dc=org"), + "Failed to create a 'normal' invalid DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'normal' invalid DN"); + + /* Now make an extended DN */ + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";%s", + sid, dn_str), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, "%s", + sid, dn_str), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";", + sid), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";", + hex_sid), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";", + hex_guid), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ";", + guid), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + torture_assert(torture, + dn = ldb_dn_new_fmt(mem_ctx, ldb, ""), + "Failed to create an invalid 'extended' DN"); + + torture_assert(torture, + ldb_dn_validate(dn) == false, + "should have failed to validate 'extended' DN"); + + return true; +} + +static bool helper_ldb_message_compare(struct torture_context *torture, + struct ldb_message *a, + struct ldb_message *b) +{ + int i; + + if (a->num_elements != b->num_elements) { + return false; + } + + for (i = 0; i < a->num_elements; i++) { + int j; + struct ldb_message_element x = a->elements[i]; + struct ldb_message_element y = b->elements[i]; + + torture_comment(torture, "#%s\n", x.name); + torture_assert_int_equal(torture, x.flags, y.flags, + "Flags do not match"); + torture_assert_str_equal(torture, x.name, y.name, + "Names do not match in field"); + torture_assert_int_equal(torture, x.num_values, y.num_values, + "Number of values do not match"); + + /* + * Records cannot round trip via the SDDL string with a + * nTSecurityDescriptor field. + * + * Parsing from SDDL and diffing the NDR dump output gives the + * following: + * + * in: struct decode_security_descriptor + * sd: struct security_descriptor + * revision : SECURITY_DESCRIPTOR_REVISION_1 (1) + *- type : 0x8c14 (35860) + *- 0: SEC_DESC_OWNER_DEFAULTED + *- 0: SEC_DESC_GROUP_DEFAULTED + *+ type : 0x8c17 (35863) + *+ 1: SEC_DESC_OWNER_DEFAULTED + *+ 1: SEC_DESC_GROUP_DEFAULTED + * 1: SEC_DESC_DACL_PRESENT + * 0: SEC_DESC_DACL_DEFAULTED + * 1: SEC_DESC_SACL_PRESENT + */ + if (strcmp(x.name, "nTSecurityDescriptor") == 0) { + continue; + } + for (j = 0; j < x.num_values; j++) { + torture_assert_int_equal(torture, x.values[j].length, + y.values[j].length, + "Does not match in length"); + torture_assert_mem_equal(torture, + x.values[j].data, + y.values[j].data, + x.values[j].length, + "Does not match in data"); + } + } + return true; +} + +static bool torture_ldb_unpack(struct torture_context *torture, + const void *data_p) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_val data = *discard_const_p(struct ldb_val, data_p); + struct ldb_message *msg = ldb_msg_new(mem_ctx); + const char *ldif_text = dda1d01d_ldif; + struct ldb_ldif ldif; + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL, NULL, NULL); + torture_assert(torture, + ldb != NULL, + "Failed to init ldb"); + + torture_assert_int_equal(torture, ldb_unpack_data(ldb, &data, msg), 0, + "ldb_unpack_data failed"); + + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = msg; + ldif_text = ldb_ldif_write_string(ldb, mem_ctx, &ldif); + + torture_assert_int_equal(torture, + strcmp(ldif_text, dda1d01d_ldif), 0, + "ldif form differs from binary form"); + return true; +} + +static bool torture_ldb_unpack_flags(struct torture_context *torture, + const void *data_p) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_val data = *discard_const_p(struct ldb_val, data_p); + struct ldb_message *msg = ldb_msg_new(mem_ctx); + const char *ldif_text = dda1d01d_ldif; + struct ldb_ldif ldif; + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL, NULL, NULL); + torture_assert(torture, + ldb != NULL, + "Failed to init ldb"); + + torture_assert_int_equal(torture, + ldb_unpack_data_flags(ldb, &data, msg, + LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC), + 0, + "ldb_unpack_data failed"); + + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = msg; + ldif_text = ldb_ldif_write_string(ldb, mem_ctx, &ldif); + + torture_assert_int_equal(torture, + strcmp(ldif_text, dda1d01d_ldif), 0, + "ldif form differs from binary form"); + + torture_assert_int_equal(torture, + ldb_unpack_data_flags(ldb, &data, msg, + LDB_UNPACK_DATA_FLAG_NO_DN), + 0, + "ldb_unpack_data failed"); + + torture_assert(torture, + msg->dn == NULL, + "msg->dn should be NULL"); + + return true; +} + +static bool torture_ldb_unpack_data_corrupt(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + + uint8_t bin[] = {0x68, 0x19, 0x01, 0x26, /* version */ + 1, 0, 0, 0, /* num elements */ + 4, 0, 0, 0, /* dn length */ + 'D', 'N', '=', 'A', 0, /* dn with null term */ + 2, 0, 0, 0, /* canonicalized dn length */ + '/', 'A', 0, /* canonicalized dn with null term */ + 18, 0, 0, 0, /* size of name and sizes section + 4 (this field) */ + 3, 0, 0, 0, /* el name length */ + 'a', 'b', 'c', 0, /* name with null term */ + 1, 0, 0, 0, 1, /* num values and length width */ + 1, /* value lengths */ + '1', 0}; /* values for abc */ + + struct ldb_val binary = data_blob_const(bin, sizeof(bin)); + struct ldb_val bin_copy; + struct ldb_message *msg; + + int i, j, current, expect_rcode, ret; + const char *comment; + + /* + * List of corruptible byte ranges. First 12 bytes are corruptible, + * next 4 bytes are not, next 5 bytes are corruptible, etc. + */ + uint8_t corrupt_bytes[] = {12, 4, 5, 2, 9, 3, 7, 2}; + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL,NULL,NULL); + torture_assert(torture, ldb != NULL, "Failed to init ldb"); + + current = 0; + for (i=0; iev, NULL,NULL,NULL); + torture_assert(torture, ldb != NULL, "Failed to init ldb"); + + msg.dn = ldb_dn_new(NULL, ldb, "DN=A"); + + torture_assert_int_equal(torture, + ldb_pack_data(ldb, &msg, &binary, + LDB_PACKING_FORMAT_V2), + 0, "ldb_pack_data failed"); + + torture_assert_int_equal(torture, expect_bin_ldb.length, + binary.length, + "packed data length not as expected"); + + torture_assert_mem_equal(torture, + expect_bin_ldb.data, + binary.data, + binary.length, + "packed data not as expected"); + talloc_free(expect_bin); + TALLOC_FREE(msg.dn); + + return true; +} + +static bool torture_ldb_pack_data_v2_special(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_val binary; + + uint8_t bin[] = {0x68, 0x19, 0x01, 0x26, /* version */ + 1, 0, 0, 0, /* num elements */ + 2, 0, 0, 0, /* dn length */ + '@', 'A', 0, /* dn with null term */ + 0, 0, 0, 0, /* canonicalized dn length */ + 0, /* no canonicalized dn, just null term */ + 18, 0, 0, 0, /* distance from here to values section */ + 3, 0, 0, 0, /* el name length */ + 'a', 'b', 'c', 0, /* name with null term */ + 1, 0, 0, 0, 1, /* num values and length width */ + 1, /* value lengths */ + '1', 0}; /* values for abc */ + + struct ldb_val vals[1] = {{.data=discard_const_p(uint8_t, "1"), + .length=1}}; + struct ldb_message_element els[1] = {{.name=discard_const_p(char, + "abc"), + .num_values=1, .values=vals}}; + struct ldb_message msg = {.num_elements=1, .elements=els}; + + struct ldb_val expect_bin_ldb; + expect_bin_ldb = data_blob_const(bin, sizeof(bin)); + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL,NULL,NULL); + torture_assert(torture, ldb != NULL, "Failed to init ldb"); + + msg.dn = ldb_dn_new(NULL, ldb, "@A"); + + torture_assert_int_equal(torture, + ldb_pack_data(ldb, &msg, &binary, + LDB_PACKING_FORMAT_V2), + 0, "ldb_pack_data failed"); + + torture_assert_int_equal(torture, expect_bin_ldb.length, + binary.length, + "packed data length not as expected"); + + torture_assert_mem_equal(torture, + expect_bin_ldb.data, + binary.data, + binary.length, + "packed data not as expected"); + + TALLOC_FREE(msg.dn); + + return true; +} + +static bool torture_ldb_parse_ldif(struct torture_context *torture, + const void *data_p) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + const char *ldif_text = dda1d01d_ldif; + struct ldb_context *ldb; + struct ldb_ldif *ldif; + struct ldb_val binary; + struct ldb_val data = *discard_const_p(struct ldb_val, data_p); + struct ldb_message *msg = ldb_msg_new(mem_ctx); + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL,NULL,NULL); + torture_assert(torture, + ldb != NULL, + "Failed to init ldb"); + + ldif = ldb_ldif_read_string(ldb, &ldif_text); + torture_assert(torture, + ldif != NULL, + "ldb_ldif_read_string failed"); + torture_assert_int_equal(torture, ldif->changetype, LDB_CHANGETYPE_NONE, + "changetype is incorrect"); + torture_assert_int_equal(torture, + ldb_pack_data(ldb, ldif->msg, &binary, + LDB_PACKING_FORMAT_V2), + 0, "ldb_pack_data failed"); + + torture_assert_int_equal(torture, ldb_unpack_data(ldb, &data, msg), 0, + "ldb_unpack_data failed"); + + torture_assert(torture, + helper_ldb_message_compare(torture, ldif->msg, msg), + "Forms differ in memory"); + + return true; +} + +static bool torture_ldb_pack_format_perf(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + char *unp_ldif_text = talloc_strndup(mem_ctx, dda1d01d_ldif, + sizeof(dda1d01d_ldif)-1); + const char *ldif_text; + struct ldb_context *ldb; + struct ldb_ldif *ldif; + struct ldb_val binary; + struct ldb_message *msg = ldb_msg_new(mem_ctx); + int ret, i; + clock_t start, diff; + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL,NULL,NULL); + torture_assert(torture, + ldb != NULL, + "Failed to init ldb"); + + unp_ldif_text[sizeof(dda1d01d_ldif)-2] = '\0'; + ldif_text = unp_ldif_text; + + for (i=0; i<10000; i++) { + ldif_text = talloc_asprintf_append( + discard_const_p(char, ldif_text), + "member: fillerfillerfillerfillerfillerfiller" + "fillerfillerfillerfillerfillerfillerfiller" + "fillerfillerfillerfillerfiller-group%d\n", i); + } + + ldif = ldb_ldif_read_string(ldb, &ldif_text); + torture_assert(torture, + ldif != NULL, + "ldb_ldif_read_string failed"); + torture_assert_int_equal(torture, ldif->changetype, LDB_CHANGETYPE_NONE, + "changetype is incorrect"); + torture_assert_int_equal(torture, + ldb_pack_data(ldb, ldif->msg, &binary, + LDB_PACKING_FORMAT_V2), + 0, "ldb_pack_data failed"); + + ret = ldb_unpack_data(ldb, &binary, msg); + torture_assert_int_equal(torture, ret, 0, + "ldb_unpack_data failed"); + + torture_assert(torture, + helper_ldb_message_compare(torture, ldif->msg, msg), + "Forms differ in memory"); + + i = 0; + start = clock(); + while (true) { + ldb_pack_data(ldb, ldif->msg, &binary, LDB_PACKING_FORMAT_V2); + i++; + + if (i >= 1000) { + break; + } + } + diff = (clock() - start) * 1000 / CLOCKS_PER_SEC; + printf("\n%d pack runs took: %ldms\n", i, (long)diff); + + i = 0; + start = clock(); + while (true) { + ldb_unpack_data(ldb, &binary, msg); + i++; + + if (i >= 1000) { + break; + } + } + diff = (clock() - start) * 1000 / CLOCKS_PER_SEC; + printf("%d unpack runs took: %ldms\n", i, (long)diff); + + return true; +} + +static bool torture_ldb_unpack_and_filter(struct torture_context *torture, + const void *data_p) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + struct ldb_context *ldb; + struct ldb_val data = *discard_const_p(struct ldb_val, data_p); + struct ldb_message *msg = ldb_msg_new(mem_ctx); + const char *lookup_names[] = {"instanceType", "nonexistent", + "whenChanged", "objectClass", + "uSNCreated", "showInAdvancedViewOnly", + "name", "cnNotHere", NULL}; + const char *ldif_text; + struct ldb_ldif ldif; + + ldb = samba_ldb_init(mem_ctx, torture->ev, NULL, NULL, NULL); + torture_assert(torture, + ldb != NULL, + "Failed to init samba"); + + torture_assert_int_equal(torture, + ldb_unpack_data(ldb, &data, msg), + 0, "ldb_unpack_data failed"); + + torture_assert_int_equal(torture, msg->num_elements, 13, + "Got wrong count of elements"); + + torture_assert_int_equal(torture, + ldb_filter_attrs_in_place(msg, lookup_names), + 0, "ldb_filter_attrs_in_place failed"); + + /* Compare data in binary form */ + torture_assert_int_equal(torture, msg->num_elements, 6, + "Got wrong number of parsed elements"); + + torture_assert_str_equal(torture, msg->elements[0].name, "objectClass", + "First element has wrong name"); + torture_assert_int_equal(torture, msg->elements[0].num_values, 2, + "First element has wrong count of values"); + torture_assert_int_equal(torture, + msg->elements[0].values[0].length, 3, + "First element's first value is of wrong length"); + torture_assert_mem_equal(torture, + msg->elements[0].values[0].data, "top", 3, + "First element's first value is incorrect"); + torture_assert_int_equal(torture, + msg->elements[0].values[1].length, strlen("container"), + "First element's second value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[0].values[1].data, + "container", strlen("container"), + "First element's second value is incorrect"); + + torture_assert_str_equal(torture, msg->elements[1].name, "instanceType", + "Second element has wrong name"); + torture_assert_int_equal(torture, msg->elements[1].num_values, 1, + "Second element has too many values"); + torture_assert_int_equal(torture, msg->elements[1].values[0].length, 1, + "Second element's value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[1].values[0].data, + "4", 1, + "Second element's value is incorrect"); + + torture_assert_str_equal(torture, msg->elements[2].name, "whenChanged", + "Third element has wrong name"); + torture_assert_int_equal(torture, msg->elements[2].num_values, 1, + "Third element has too many values"); + torture_assert_int_equal(torture, msg->elements[2].values[0].length, + strlen("20150708224310.0Z"), + "Third element's value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[2].values[0].data, + "20150708224310.0Z", strlen("20150708224310.0Z"), + "Third element's value is incorrect"); + + torture_assert_str_equal(torture, msg->elements[3].name, "uSNCreated", + "Fourth element has wrong name"); + torture_assert_int_equal(torture, msg->elements[3].num_values, 1, + "Fourth element has too many values"); + torture_assert_int_equal(torture, msg->elements[3].values[0].length, 4, + "Fourth element's value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[3].values[0].data, + "3467", 4, + "Fourth element's value is incorrect"); + + torture_assert_str_equal(torture, msg->elements[4].name, "showInAdvancedViewOnly", + "Fifth element has wrong name"); + torture_assert_int_equal(torture, msg->elements[4].num_values, 1, + "Fifth element has too many values"); + torture_assert_int_equal(torture, msg->elements[4].values[0].length, 4, + "Fifth element's value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[4].values[0].data, + "TRUE", 4, + "Fourth element's value is incorrect"); + + torture_assert_str_equal(torture, msg->elements[5].name, "name", + "Sixth element has wrong name"); + torture_assert_int_equal(torture, msg->elements[5].num_values, 1, + "Sixth element has too many values"); + torture_assert_int_equal(torture, msg->elements[5].values[0].length, + strlen("dda1d01d-4bd7-4c49-a184-46f9241b560e"), + "Sixth element's value is of wrong length"); + torture_assert_mem_equal(torture, msg->elements[5].values[0].data, + "dda1d01d-4bd7-4c49-a184-46f9241b560e", + strlen("dda1d01d-4bd7-4c49-a184-46f9241b560e"), + "Sixth element's value is incorrect"); + + /* Compare data in ldif form */ + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = msg; + ldif_text = ldb_ldif_write_string(ldb, mem_ctx, &ldif); + + torture_assert_str_equal(torture, ldif_text, dda1d01d_ldif_reduced, + "Expected fields did not match"); + + return true; +} + +struct torture_suite *torture_ldb(TALLOC_CTX *mem_ctx) +{ + int i; + struct ldb_val *bins = talloc_array(mem_ctx, struct ldb_val, 2); + struct torture_suite *suite = torture_suite_create(mem_ctx, "ldb"); + + if (suite == NULL) { + return NULL; + } + + bins[0] = data_blob_const(dda1d01d_bin_v1, sizeof(dda1d01d_bin_v1)); + bins[1] = data_blob_const(dda1d01d_bin_v2, sizeof(dda1d01d_bin_v2)); + + torture_suite_add_simple_test(suite, "attrs", torture_ldb_attrs); + torture_suite_add_simple_test(suite, "dn-attrs", torture_ldb_dn_attrs); + torture_suite_add_simple_test(suite, "dn-extended", + torture_ldb_dn_extended); + torture_suite_add_simple_test(suite, "dn-invalid-extended", + torture_ldb_dn_invalid_extended); + torture_suite_add_simple_test(suite, "dn", torture_ldb_dn); + torture_suite_add_simple_test(suite, "pack-format-perf", + torture_ldb_pack_format_perf); + torture_suite_add_simple_test(suite, "pack-data-v2", + torture_ldb_pack_data_v2); + torture_suite_add_simple_test(suite, "pack-data-special-v2", + torture_ldb_pack_data_v2_special); + torture_suite_add_simple_test(suite, "unpack-corrupt-v2", + torture_ldb_unpack_data_corrupt); + + for (i=0; i<2; i++) { + torture_suite_add_simple_tcase_const(suite, + talloc_asprintf(mem_ctx, "unpack-data-v%d", i+1), + torture_ldb_unpack, &bins[i]); + torture_suite_add_simple_tcase_const(suite, + talloc_asprintf(mem_ctx, "unpack-data-flags-v%d", i+1), + torture_ldb_unpack_flags, &bins[i]); + torture_suite_add_simple_tcase_const(suite, + talloc_asprintf(mem_ctx, "parse-ldif-v%d", i+1), + torture_ldb_parse_ldif, &bins[i]); + torture_suite_add_simple_tcase_const(suite, + talloc_asprintf(mem_ctx, + "unpack-data-and-filter-v%d", i+1), + torture_ldb_unpack_and_filter, &bins[i]); + } + + suite->description = talloc_strdup(suite, "LDB (samba-specific behaviour) tests"); + + return suite; +} diff --git a/source4/torture/libnet/domain.c b/source4/torture/libnet/domain.c new file mode 100644 index 0000000..c1cfc91 --- /dev/null +++ b/source4/torture/libnet/domain.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + +static bool test_domainopen(struct torture_context *tctx, + struct libnet_context *net_ctx, TALLOC_CTX *mem_ctx, + struct lsa_String *domname, + struct policy_handle *domain_handle) +{ + NTSTATUS status; + struct libnet_DomainOpen io; + + ZERO_STRUCT(io); + + torture_comment(tctx, "opening domain\n"); + + io.in.domain_name = talloc_strdup(mem_ctx, domname->string); + io.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(net_ctx, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Composite domain open failed for domain '%s' - %s\n", + domname->string, nt_errstr(status)); + return false; + } + + *domain_handle = io.out.domain_handle; + return true; +} + + +static bool test_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle) +{ + struct samr_Close r; + struct policy_handle handle; + + r.in.handle = domain_handle; + r.out.handle = &handle; + + torture_comment(tctx, "closing domain handle\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Close_r(b, mem_ctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close failed"); + + return true; +} + + +bool torture_domainopen(struct torture_context *torture) +{ + NTSTATUS status; + struct libnet_context *net_ctx; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + + mem_ctx = talloc_init("test_domain_open"); + + net_ctx = libnet_context_init(torture->ev, torture->lp_ctx); + + status = torture_rpc_connection(torture, + &net_ctx->samr.pipe, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domainopen(torture, net_ctx, mem_ctx, &name, &h)) { + ret = false; + goto done; + } + + if (!test_cleanup(torture, net_ctx->samr.pipe->binding_handle, mem_ctx, &h)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/groupinfo.c b/source4/torture/libnet/groupinfo.c new file mode 100644 index 0000000..a738ab3 --- /dev/null +++ b/source4/torture/libnet/groupinfo.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + +#define TEST_GROUPNAME "libnetgroupinfotest" + + +static bool test_groupinfo(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* group_name, + uint32_t *rid) +{ + const uint16_t level = 5; + NTSTATUS status; + struct libnet_rpc_groupinfo group; + struct dom_sid *group_sid; + + group_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.sid = dom_sid_string(mem_ctx, group_sid); + group.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing sync libnet_rpc_groupinfo (SID argument)\n"); + status = libnet_rpc_groupinfo(tctx->ev, p->binding_handle, mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_userinfo - %s\n", nt_errstr(status)); + return false; + } + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.sid = NULL; + group.in.groupname = TEST_GROUPNAME; + group.in.level = level; + + printf("Testing sync libnet_rpc_groupinfo (groupname argument)\n"); + status = libnet_rpc_groupinfo(tctx->ev, p->binding_handle, mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_groupinfo - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +bool torture_groupinfo(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + struct dom_sid2 sid; + uint32_t rid; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userinfo"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_group_create(torture, b, mem_ctx, &h, TEST_GROUPNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_groupinfo(torture, p, mem_ctx, &h, &sid, TEST_GROUPNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, b, mem_ctx, &h, TEST_GROUPNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/groupman.c b/source4/torture/libnet/groupman.c new file mode 100644 index 0000000..8cd49db --- /dev/null +++ b/source4/torture/libnet/groupman.c @@ -0,0 +1,97 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/grouptest.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +static bool test_groupadd(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + NTSTATUS status; + bool ret = true; + struct libnet_rpc_groupadd group; + + ZERO_STRUCT(group); + + group.in.domain_handle = *domain_handle; + group.in.groupname = name; + + printf("Testing libnet_rpc_groupadd\n"); + + status = libnet_rpc_groupadd(tctx->ev, p->binding_handle, + mem_ctx, &group); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to call sync libnet_rpc_groupadd - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +bool torture_groupadd(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + const char *name = TEST_GROUPNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_groupadd"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connection"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_groupadd(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/grouptest.h b/source4/torture/libnet/grouptest.h new file mode 100644 index 0000000..8b65e6e --- /dev/null +++ b/source4/torture/libnet/grouptest.h @@ -0,0 +1,20 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define TEST_GROUPNAME "libnetgrptest" diff --git a/source4/torture/libnet/libnet.c b/source4/torture/libnet/libnet.c new file mode 100644 index 0000000..faf7bca --- /dev/null +++ b/source4/torture/libnet/libnet.c @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "librpc/rpc/dcerpc.h" +#include "librpc/gen_ndr/lsa.h" +#include "libnet/libnet.h" +#include "torture/libnet/proto.h" + +NTSTATUS torture_net_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "net"); + + torture_suite_add_simple_test(suite, "userinfo", torture_userinfo); + torture_suite_add_simple_test(suite, "useradd", torture_useradd); + torture_suite_add_simple_test(suite, "userdel", torture_userdel); + torture_suite_add_simple_test(suite, "usermod", torture_usermod); + torture_suite_add_simple_test(suite, "domopen", torture_domainopen); + torture_suite_add_simple_test(suite, "groupinfo", torture_groupinfo); + torture_suite_add_simple_test(suite, "groupadd", torture_groupadd); + torture_suite_add_simple_test(suite, "api.lookup", torture_lookup); + torture_suite_add_simple_test(suite, "api.lookuphost", torture_lookup_host); + torture_suite_add_simple_test(suite, "api.lookuppdc", torture_lookup_pdc); + torture_suite_add_simple_test(suite, "api.lookupname", torture_lookup_sam_name); + torture_suite_add_simple_test(suite, "api.createuser", torture_createuser); + torture_suite_add_simple_test(suite, "api.deleteuser", torture_deleteuser); + torture_suite_add_simple_test(suite, "api.modifyuser", torture_modifyuser); + torture_suite_add_simple_test(suite, "api.userinfo", torture_userinfo_api); + torture_suite_add_simple_test(suite, "api.userlist", torture_userlist); + torture_suite_add_simple_test(suite, "api.groupinfo", torture_groupinfo_api); + torture_suite_add_simple_test(suite, "api.grouplist", torture_grouplist); + torture_suite_add_simple_test(suite, "api.creategroup", torture_creategroup); + torture_suite_add_simple_test(suite, "api.rpcconn.bind", torture_rpc_connect_binding); + torture_suite_add_simple_test(suite, "api.rpcconn.srv", torture_rpc_connect_srv); + torture_suite_add_simple_test(suite, "api.rpcconn.pdc", torture_rpc_connect_pdc); + torture_suite_add_simple_test(suite, "api.rpcconn.dc", torture_rpc_connect_dc); + torture_suite_add_simple_test(suite, "api.rpcconn.dcinfo", torture_rpc_connect_dc_info); + torture_suite_add_simple_test(suite, "api.listshares", torture_listshares); + torture_suite_add_simple_test(suite, "api.delshare", torture_delshare); + torture_suite_add_simple_test(suite, "api.domopenlsa", torture_domain_open_lsa); + torture_suite_add_simple_test(suite, "api.domcloselsa", torture_domain_close_lsa); + torture_suite_add_simple_test(suite, "api.domopensamr", torture_domain_open_samr); + torture_suite_add_simple_test(suite, "api.domclosesamr", torture_domain_close_samr); + torture_suite_add_simple_test(suite, "api.become.dc", torture_net_become_dc); + torture_suite_add_simple_test(suite, "api.domlist", torture_domain_list); + + suite->description = talloc_strdup(suite, "libnet convenience interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/libnet/libnet_BecomeDC.c b/source4/torture/libnet/libnet_BecomeDC.c new file mode 100644 index 0000000..45d386b --- /dev/null +++ b/source4/torture/libnet/libnet_BecomeDC.c @@ -0,0 +1,191 @@ +/* + Unix SMB/CIFS implementation. + + libnet_BecomeDC() tests + + Copyright (C) Stefan Metzmacher 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "dsdb/samdb/samdb.h" +#include "../lib/util/dlinklist.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "system/time.h" +#include "ldb_wrap.h" +#include "auth/auth.h" +#include "param/param.h" +#include "param/provision.h" +#include "libcli/resolve/resolve.h" +#include "torture/libnet/proto.h" + +bool torture_net_become_dc(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + struct libnet_BecomeDC b; + struct libnet_UnbecomeDC u; + struct libnet_vampire_cb_state *s; + struct ldb_message *msg; + int ldb_ret; + uint32_t i; + char *private_dir; + const char *address; + struct nbt_name name; + const char *netbios_name; + struct cli_credentials *machine_account; + struct test_join *tj; + struct loadparm_context *lp_ctx; + struct ldb_context *ldb; + struct libnet_context *ctx; + struct dsdb_schema *schema; + + char *location = NULL; + torture_assert_ntstatus_ok(torture, torture_temp_dir(torture, "libnet_BecomeDC", &location), + "torture_temp_dir should return NT_STATUS_OK" ); + + netbios_name = lpcfg_parm_string(torture->lp_ctx, NULL, "become dc", "smbtorture dc"); + if (!netbios_name || !netbios_name[0]) { + netbios_name = "smbtorturedc"; + } + + make_nbt_name_server(&name, torture_setting_string(torture, "host", NULL)); + + /* do an initial name resolution to find its IP */ + status = resolve_name_ex(lpcfg_resolve_context(torture->lp_ctx), + 0, 0, + &name, torture, &address, torture->ev); + torture_assert_ntstatus_ok(torture, status, talloc_asprintf(torture, + "Failed to resolve %s - %s\n", + name.name, nt_errstr(status))); + + + /* Join domain as a member server. */ + tj = torture_join_domain(torture, netbios_name, + ACB_WSTRUST, + &machine_account); + torture_assert(torture, tj, talloc_asprintf(torture, + "%s failed to join domain as workstation\n", + netbios_name)); + + s = libnet_vampire_cb_state_init(torture, torture->lp_ctx, torture->ev, + netbios_name, + torture_join_dom_netbios_name(tj), + torture_join_dom_dns_name(tj), + location); + torture_assert(torture, s, "libnet_vampire_cb_state_init"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + ZERO_STRUCT(b); + b.in.domain_dns_name = torture_join_dom_dns_name(tj); + b.in.domain_netbios_name = torture_join_dom_netbios_name(tj); + b.in.domain_sid = torture_join_sid(tj); + b.in.source_dsa_address = address; + b.in.dest_dsa_netbios_name = netbios_name; + + b.in.callbacks.private_data = s; + b.in.callbacks.check_options = libnet_vampire_cb_check_options; + b.in.callbacks.prepare_db = libnet_vampire_cb_prepare_db; + b.in.callbacks.schema_chunk = libnet_vampire_cb_schema_chunk; + b.in.callbacks.config_chunk = libnet_vampire_cb_store_chunk; + b.in.callbacks.domain_chunk = libnet_vampire_cb_store_chunk; + + status = libnet_BecomeDC(ctx, s, &b); + torture_assert_ntstatus_ok_goto(torture, status, ret, cleanup, talloc_asprintf(torture, + "libnet_BecomeDC() failed - %s %s\n", + nt_errstr(status), b.out.error_string)); + ldb = libnet_vampire_cb_ldb(s); + + msg = ldb_msg_new(s); + torture_assert_int_equal_goto(torture, (msg?1:0), 1, ret, cleanup, + "ldb_msg_new() failed\n"); + msg->dn = ldb_dn_new(msg, ldb, "@ROOTDSE"); + torture_assert_int_equal_goto(torture, (msg->dn?1:0), 1, ret, cleanup, + "ldb_msg_new(@ROOTDSE) failed\n"); + + ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE"); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_msg_add_string(msg, isSynchronized, TRUE) failed\n"); + + for (i=0; i < msg->num_elements; i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + torture_comment(torture, "mark ROOTDSE with isSynchronized=TRUE\n"); + ldb_ret = ldb_modify(libnet_vampire_cb_ldb(s), msg); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_modify() failed\n"); + + /* commit the transaction now we know the secrets were written + * out properly + */ + ldb_ret = ldb_transaction_commit(ldb); + torture_assert_int_equal_goto(torture, ldb_ret, LDB_SUCCESS, ret, cleanup, + "ldb_transaction_commit() failed\n"); + + /* reopen the ldb */ + talloc_unlink(s, ldb); + + lp_ctx = libnet_vampire_cb_lp_ctx(s); + private_dir = talloc_asprintf(s, "%s/%s", location, "private"); + lpcfg_set_cmdline(lp_ctx, "private dir", private_dir); + torture_comment(torture, "Reopen the SAM LDB with system credentials and all replicated data: %s\n", private_dir); + ldb = samdb_connect(s, + torture->ev, + lp_ctx, + system_session(lp_ctx), + NULL, + 0); + torture_assert_goto(torture, ldb != NULL, ret, cleanup, + talloc_asprintf(torture, + "Failed to open '%s/sam.ldb'\n", private_dir)); + + torture_assert_goto(torture, dsdb_uses_global_schema(ldb), ret, cleanup, + "Uses global schema"); + + schema = dsdb_get_schema(ldb, s); + torture_assert_goto(torture, schema != NULL, ret, cleanup, + "Failed to get loaded dsdb_schema\n"); + + /* Make sure we get this from the command line */ + if (lpcfg_parm_bool(torture->lp_ctx, NULL, "become dc", "do not unjoin", false)) { + talloc_free(s); + return ret; + } + +cleanup: + ZERO_STRUCT(u); + u.in.domain_dns_name = torture_join_dom_dns_name(tj); + u.in.domain_netbios_name = torture_join_dom_netbios_name(tj); + u.in.source_dsa_address = address; + u.in.dest_dsa_netbios_name = netbios_name; + + status = libnet_UnbecomeDC(ctx, s, &u); + torture_assert_ntstatus_ok(torture, status, talloc_asprintf(torture, + "libnet_UnbecomeDC() failed - %s %s\n", + nt_errstr(status), u.out.error_string)); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + talloc_free(s); + return ret; +} diff --git a/source4/torture/libnet/libnet_domain.c b/source4/torture/libnet/libnet_domain.c new file mode 100644 index 0000000..2444000 --- /dev/null +++ b/source4/torture/libnet/libnet_domain.c @@ -0,0 +1,440 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +static bool test_opendomain_samr(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct lsa_String *domname, + uint32_t *access_mask, struct dom_sid **sid_p) +{ + struct policy_handle h, domain_handle; + struct samr_Connect r1; + struct samr_LookupDomain r2; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain r3; + + torture_comment(tctx, "connecting\n"); + + *access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + r1.in.system_name = 0; + r1.in.access_mask = *access_mask; + r1.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect_r(b, mem_ctx, &r1), + "Connect failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "Connect failed"); + + r2.in.connect_handle = &h; + r2.in.domain_name = domname; + r2.out.sid = &sid; + + torture_comment(tctx, "domain lookup on %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupDomain_r(b, mem_ctx, &r2), + "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "LookupDomain failed"); + + r3.in.connect_handle = &h; + r3.in.access_mask = *access_mask; + r3.in.sid = *sid_p = *r2.out.sid; + r3.out.domain_handle = &domain_handle; + + torture_comment(tctx, "opening domain\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, mem_ctx, &r3), + "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "OpenDomain failed"); + + *handle = domain_handle; + + return true; +} + + +static bool test_opendomain_lsa(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct lsa_String *domname, + uint32_t *access_mask) +{ + struct lsa_OpenPolicy2 open; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + + *access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + ZERO_STRUCT(attr); + ZERO_STRUCT(qos); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.sec_qos = &qos; + + open.in.system_name = domname->string; + open.in.attr = &attr; + open.in.access_mask = *access_mask; + open.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_OpenPolicy2_r(b, mem_ctx, &open), + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, open.out.result, + "OpenPolicy2 failed"); + + return true; +} + +bool torture_domain_open_lsa(struct torture_context *torture) +{ + NTSTATUS status; + bool ret = true; + struct libnet_context *ctx; + struct libnet_DomainOpen r; + struct lsa_Close lsa_close; + struct policy_handle h; + const char *domain_name; + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + return false; + } + + ctx->cred = samba_cmdline_get_creds(); + + ZERO_STRUCT(r); + r.in.type = DOMAIN_LSA; + r.in.domain_name = domain_name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(ctx, torture, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to open domain on lsa service: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + ZERO_STRUCT(lsa_close); + lsa_close.in.handle = &ctx->lsa.handle; + lsa_close.out.handle = &h; + + torture_assert_ntstatus_ok(torture, + dcerpc_lsa_Close_r(ctx->lsa.pipe->binding_handle, ctx, &lsa_close), + "failed to close domain on lsa service"); + torture_assert_ntstatus_ok(torture, lsa_close.out.result, + "failed to close domain on lsa service"); + +done: + talloc_free(ctx); + return ret; +} + + +bool torture_domain_close_lsa(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx=NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct dcerpc_binding *binding; + uint32_t access_mask; + struct policy_handle h; + struct dcerpc_pipe *p; + struct libnet_DomainClose r; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_lsa"); + status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_lsarpc, + samba_cmdline_get_creds(), + torture->ev, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to connect to server: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + + if (!test_opendomain_lsa(torture, p->binding_handle, torture, &h, &domain_name, &access_mask)) { + torture_comment(torture, "failed to open domain on lsa service\n"); + ret = false; + goto done; + } + + ctx->lsa.pipe = p; + ctx->lsa.name = domain_name.string; + ctx->lsa.access_mask = access_mask; + ctx->lsa.handle = h; + + ZERO_STRUCT(r); + r.in.type = DOMAIN_LSA; + r.in.domain_name = domain_name.string; + + status = libnet_DomainClose(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} + + +bool torture_domain_open_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct libnet_context *ctx; + TALLOC_CTX *mem_ctx; + struct policy_handle domain_handle, handle; + struct libnet_DomainOpen io; + struct samr_Close r; + const char *domain_name; + bool ret = true; + + mem_ctx = talloc_init("test_domainopen_lsa"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + torture_comment(torture, "opening domain\n"); + + io.in.type = DOMAIN_SAMR; + io.in.domain_name = domain_name; + io.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + status = libnet_DomainOpen(ctx, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Composite domain open failed for domain '%s' - %s\n", + domain_name, nt_errstr(status)); + ret = false; + goto done; + } + + domain_handle = ctx->samr.handle; + + r.in.handle = &domain_handle; + r.out.handle = &handle; + + torture_comment(torture, "closing domain handle\n"); + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_Close_r(ctx->samr.pipe->binding_handle, mem_ctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(torture, r.out.result, + "Close failed"); + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + + return ret; +} + + +bool torture_domain_close_samr(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct dcerpc_binding *binding; + uint32_t access_mask; + struct policy_handle h; + struct dcerpc_pipe *p; + struct libnet_DomainClose r; + struct dom_sid *sid; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_samr"); + status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_samr, + ctx->cred, torture->ev, torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "failed to connect to server: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + domain_name.string = talloc_strdup(mem_ctx, lpcfg_workgroup(torture->lp_ctx)); + + if (!test_opendomain_samr(torture, p->binding_handle, torture, &h, &domain_name, &access_mask, &sid)) { + torture_comment(torture, "failed to open domain on samr service\n"); + ret = false; + goto done; + } + + ctx->samr.pipe = p; + ctx->samr.name = talloc_steal(ctx, domain_name.string); + ctx->samr.access_mask = access_mask; + ctx->samr.handle = h; + ctx->samr.sid = talloc_steal(ctx, sid); + + ZERO_STRUCT(r); + r.in.type = DOMAIN_SAMR; + r.in.domain_name = domain_name.string; + + status = libnet_DomainClose(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} + + +bool torture_domain_list(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct dcerpc_binding *binding; + struct libnet_context *ctx; + struct libnet_DomainList r; + int i; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (ctx == NULL) { + torture_comment(torture, "failed to create libnet context\n"); + ret = false; + goto done; + } + + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture_domain_close_samr"); + + /* + * querying the domain list using default buffer size + */ + + ZERO_STRUCT(r); + r.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + + status = libnet_DomainList(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + torture_comment(torture, "Received list or domains (everything in one piece):\n"); + + for (i = 0; i < r.out.count; i++) { + torture_comment(torture, "Name[%d]: %s\n", i, r.out.domains[i].name); + } + + /* + * querying the domain list using specified (much smaller) buffer size + */ + + ctx->samr.buf_size = 32; + + ZERO_STRUCT(r); + r.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + + status = libnet_DomainList(ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + torture_comment(torture, "Received list or domains (collected in more than one round):\n"); + + for (i = 0; i < r.out.count; i++) { + torture_comment(torture, "Name[%d]: %s\n", i, r.out.domains[i].name); + } + +done: + torture_comment(torture, "\nStatus: %s\n", nt_errstr(status)); + + talloc_free(mem_ctx); + talloc_free(ctx); + return ret; +} diff --git a/source4/torture/libnet/libnet_group.c b/source4/torture/libnet/libnet_group.c new file mode 100644 index 0000000..e3e2030 --- /dev/null +++ b/source4/torture/libnet/libnet_group.c @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +#define TEST_GROUPNAME "libnetgrouptest" + + +bool torture_groupinfo_api(struct torture_context *torture) +{ + const char *name = TEST_GROUPNAME; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL, *prep_mem_ctx; + struct libnet_context *ctx = NULL; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct libnet_GroupInfo req; + + prep_mem_ctx = talloc_init("prepare torture group info"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, p->binding_handle, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_group_create(torture, p->binding_handle, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + mem_ctx = talloc_init("torture group info"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + ZERO_STRUCT(req); + + req.in.domain_name = domain_name.string; + req.in.level = GROUP_INFO_BY_NAME; + req.in.data.group_name = name; + + status = libnet_GroupInfo(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_GroupInfo call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_grouplist(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct libnet_GroupList req; + int i; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + mem_ctx = talloc_init("torture group list"); + + ZERO_STRUCT(req); + + torture_comment(torture, "listing group accounts:\n"); + + do { + req.in.domain_name = domain_name.string; + req.in.page_size = 128; + req.in.resume_index = req.out.resume_index; + + status = libnet_GroupList(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; + + for (i = 0; i < req.out.count; i++) { + torture_comment(torture, "\tgroup: %s, sid=%s\n", + req.out.groups[i].groupname, req.out.groups[i].sid); + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (!(NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(torture, "libnet_GroupList call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + + if (!test_lsa_close_handle(torture, + ctx->lsa.pipe->binding_handle, mem_ctx, &ctx->lsa.handle)) { + torture_comment(torture, "lsa domain close failed\n"); + ret = false; + } + + talloc_free(ctx); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_creategroup(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct libnet_CreateGroup req; + + mem_ctx = talloc_init("test_creategroup"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + req.in.group_name = TEST_GROUPNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.out.error_string = NULL; + + status = libnet_CreateGroup(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_CreateGroup call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_group_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/libnet_lookup.c b/source4/torture/libnet/libnet_lookup.c new file mode 100644 index 0000000..e6e23dc --- /dev/null +++ b/source4/torture/libnet/libnet_lookup.c @@ -0,0 +1,191 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "libcli/libcli.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +bool torture_lookup(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_Lookup lookup; + struct dcerpc_binding *binding; + + mem_ctx = talloc_init("test_lookup"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + lookup.in.hostname = torture_setting_string(torture, "host", NULL); + if (lookup.in.hostname == NULL) { + status = torture_rpc_binding(torture, &binding); + if (NT_STATUS_IS_OK(status)) { + lookup.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + } + } + + lookup.in.type = NBT_NAME_CLIENT; + lookup.in.resolve_ctx = NULL; + lookup.out.address = NULL; + + status = libnet_Lookup(ctx, mem_ctx, &lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup name %s: %s\n", lookup.in.hostname, nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "Name [%s] found at address: %s.\n", lookup.in.hostname, *lookup.out.address); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_host(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_Lookup lookup; + struct dcerpc_binding *binding; + + mem_ctx = talloc_init("test_lookup_host"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + lookup.in.hostname = torture_setting_string(torture, "host", NULL); + if (lookup.in.hostname == NULL) { + status = torture_rpc_binding(torture, &binding); + if (NT_STATUS_IS_OK(status)) { + lookup.in.hostname = dcerpc_binding_get_string_option(binding, "host"); + } + } + + lookup.in.resolve_ctx = NULL; + lookup.out.address = NULL; + + status = libnet_LookupHost(ctx, mem_ctx, &lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup host %s: %s\n", lookup.in.hostname, nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "Host [%s] found at address: %s.\n", lookup.in.hostname, *lookup.out.address); + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_pdc(struct torture_context *torture) +{ + bool ret; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_LookupDCs *lookup; + int i; + + mem_ctx = talloc_init("test_lookup_pdc"); + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + talloc_steal(ctx, mem_ctx); + + lookup = talloc(mem_ctx, struct libnet_LookupDCs); + if (!lookup) { + ret = false; + goto done; + } + + lookup->in.domain_name = lpcfg_workgroup(torture->lp_ctx); + lookup->in.name_type = NBT_NAME_PDC; + + status = libnet_LookupDCs(ctx, mem_ctx, lookup); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Couldn't lookup pdc %s: %s\n", lookup->in.domain_name, + nt_errstr(status)); + ret = false; + goto done; + } + + ret = true; + + torture_comment(torture, "DCs of domain [%s] found.\n", lookup->in.domain_name); + for (i = 0; i < lookup->out.num_dcs; i++) { + torture_comment(torture, "\tDC[%d]: name=%s, address=%s\n", i, lookup->out.dcs[i].name, + lookup->out.dcs[i].address); + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_lookup_sam_name(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx; + struct libnet_LookupName r; + bool ret = true; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + mem_ctx = talloc_init("torture lookup sam name"); + if (mem_ctx == NULL) return false; + + r.in.name = "Administrator"; + r.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + + status = libnet_LookupName(ctx, mem_ctx, &r); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "libnet_LookupName: failed"); + +done: + talloc_free(mem_ctx); + talloc_free(ctx); + + return ret; +} diff --git a/source4/torture/libnet/libnet_rpc.c b/source4/torture/libnet/libnet_rpc.c new file mode 100644 index 0000000..9820432 --- /dev/null +++ b/source4/torture/libnet/libnet_rpc.c @@ -0,0 +1,230 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_samr.h" +#include "librpc/gen_ndr/ndr_srvsvc.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + +static bool test_connect_service(struct torture_context *tctx, + struct libnet_context *ctx, + const struct ndr_interface_table *iface, + const char *binding_string, + const char *hostname, + const enum libnet_RpcConnect_level level, + bool badcreds, NTSTATUS expected_status) +{ + NTSTATUS status; + struct libnet_RpcConnect connect_r; + ZERO_STRUCT(connect_r); + + connect_r.level = level; + connect_r.in.binding = binding_string; + connect_r.in.name = hostname; + connect_r.in.dcerpc_iface = iface; + + /* if bad credentials are needed, set baduser%badpassword instead + of default commandline-passed credentials */ + if (badcreds) { + cli_credentials_set_username(ctx->cred, "baduser", CRED_SPECIFIED); + cli_credentials_set_password(ctx->cred, "badpassword", CRED_SPECIFIED); + } + + status = libnet_RpcConnect(ctx, ctx, &connect_r); + + if (!NT_STATUS_EQUAL(status, expected_status)) { + torture_comment(tctx, "Connecting to rpc service %s on %s.\n\tFAILED. Expected: %s." + "Received: %s\n", + connect_r.in.dcerpc_iface->name, connect_r.in.binding, nt_errstr(expected_status), + nt_errstr(status)); + + return false; + } + + torture_comment(tctx, "PASSED. Expected: %s, received: %s\n", nt_errstr(expected_status), + nt_errstr(status)); + + if (connect_r.level == LIBNET_RPC_CONNECT_DC_INFO && NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Domain Controller Info:\n"); + torture_comment(tctx, "\tDomain Name:\t %s\n", connect_r.out.domain_name); + torture_comment(tctx, "\tDomain SID:\t %s\n", dom_sid_string(ctx, connect_r.out.domain_sid)); + torture_comment(tctx, "\tRealm:\t\t %s\n", connect_r.out.realm); + torture_comment(tctx, "\tGUID:\t\t %s\n", GUID_string(ctx, connect_r.out.guid)); + + } else if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Error string: %s\n", connect_r.out.error_string); + } + + return true; +} + + +static bool torture_rpc_connect(struct torture_context *torture, + const enum libnet_RpcConnect_level level, + const char *bindstr, const char *hostname) +{ + struct libnet_context *ctx; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + torture_comment(torture, "Testing connection to LSA interface\n"); + + if (!test_connect_service(torture, ctx, &ndr_table_lsarpc, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect LSA interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SAMR interface\n"); + if (!test_connect_service(torture, ctx, &ndr_table_samr, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect SAMR interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SRVSVC interface\n"); + if (!test_connect_service(torture, ctx, &ndr_table_srvsvc, bindstr, + hostname, level, false, NT_STATUS_OK)) { + torture_comment(torture, "failed to connect SRVSVC interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to LSA interface with wrong credentials\n"); + if (!test_connect_service(torture, ctx, &ndr_table_lsarpc, bindstr, + hostname, level, true, NT_STATUS_LOGON_FAILURE)) { + torture_comment(torture, "failed to test wrong credentials on LSA interface\n"); + return false; + } + + torture_comment(torture, "Testing connection to SAMR interface with wrong credentials\n"); + if (!test_connect_service(torture, ctx, &ndr_table_samr, bindstr, + hostname, level, true, NT_STATUS_LOGON_FAILURE)) { + torture_comment(torture, "failed to test wrong credentials on SAMR interface\n"); + return false; + } + + talloc_free(ctx); + + return true; +} + + +bool torture_rpc_connect_srv(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_SERVER; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *host; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + host = dcerpc_binding_get_string_option(binding, "host"); + + return torture_rpc_connect(torture, level, NULL, host); +} + + +bool torture_rpc_connect_pdc(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_PDC; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_dc(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_DC; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_dc_info(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_DC_INFO; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *domain_name; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* we're accessing domain controller so the domain name should be + passed (it's going to be resolved to dc name and address) instead + of specific server name. */ + domain_name = lpcfg_workgroup(torture->lp_ctx); + return torture_rpc_connect(torture, level, NULL, domain_name); +} + + +bool torture_rpc_connect_binding(struct torture_context *torture) +{ + const enum libnet_RpcConnect_level level = LIBNET_RPC_CONNECT_BINDING; + NTSTATUS status; + struct dcerpc_binding *binding; + const char *bindstr; + + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + bindstr = dcerpc_binding_string(torture, binding); + + return torture_rpc_connect(torture, level, bindstr, NULL); +} diff --git a/source4/torture/libnet/libnet_share.c b/source4/torture/libnet/libnet_share.c new file mode 100644 index 0000000..da74b99 --- /dev/null +++ b/source4/torture/libnet/libnet_share.c @@ -0,0 +1,285 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Gregory LEOCADIE 2005 + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/libnet/proto.h" + + +#define TEST_SHARENAME "libnetsharetest" + + +static void test_displayshares(struct torture_context *tctx, + struct libnet_ListShares s) +{ + int i, j; + + struct share_type { + enum srvsvc_ShareType type; + const char *desc; + } share_types[] = { + { STYPE_DISKTREE, "STYPE_DISKTREE" }, + { STYPE_DISKTREE_TEMPORARY, "STYPE_DISKTREE_TEMPORARY" }, + { STYPE_DISKTREE_HIDDEN, "STYPE_DISKTREE_HIDDEN" }, + { STYPE_PRINTQ, "STYPE_PRINTQ" }, + { STYPE_PRINTQ_TEMPORARY, "STYPE_PRINTQ_TEMPORARY" }, + { STYPE_PRINTQ_HIDDEN, "STYPE_PRINTQ_HIDDEN" }, + { STYPE_DEVICE, "STYPE_DEVICE" }, + { STYPE_DEVICE_TEMPORARY, "STYPE_DEVICE_TEMPORARY" }, + { STYPE_DEVICE_HIDDEN, "STYPE_DEVICE_HIDDEN" }, + { STYPE_IPC, "STYPE_IPC" }, + { STYPE_IPC_TEMPORARY, "STYPE_IPC_TEMPORARY" }, + { STYPE_IPC_HIDDEN, "STYPE_IPC_HIDDEN" } + }; + + switch (s.in.level) { + case 0: + for (i = 0; i < s.out.ctr.ctr0->count; i++) { + struct srvsvc_NetShareInfo0 *info = &s.out.ctr.ctr0->array[i]; + torture_comment(tctx, "\t[%d] %s\n", i, info->name); + } + break; + + case 1: + for (i = 0; i < s.out.ctr.ctr1->count; i++) { + struct srvsvc_NetShareInfo1 *info = &s.out.ctr.ctr1->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s (%s)\t%s\n", + i, + info->name, + info->comment, + share_types[j].desc); + break; + } + } + } + break; + + case 2: + for (i = 0; i < s.out.ctr.ctr2->count; i++) { + struct srvsvc_NetShareInfo2 *info = &s.out.ctr.ctr2->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s\t%s\n" + "\t %s\n" + "\t [perms=0x%08x, " + "max_usr=%d, " + "cur_usr=%d, " + "path=%s, " + "pass=%s]\n", + i, + info->name, + share_types[j].desc, + info->comment, + info->permissions, + info->max_users, + info->current_users, + info->path, + info->password); + break; + } + } + } + break; + + case 501: + for (i = 0; i < s.out.ctr.ctr501->count; i++) { + struct srvsvc_NetShareInfo501 *info = &s.out.ctr.ctr501->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s" + "\t%s " + "[csc_policy=0x%08x]\n" + "\t %s\n", + i, + info->name, + share_types[j].desc, + info->csc_policy, + info->comment); + break; + } + } + } + break; + + case 502: + for (i = 0; i < s.out.ctr.ctr502->count; i++) { + struct srvsvc_NetShareInfo502 *info = &s.out.ctr.ctr502->array[i]; + for (j = 0; j < ARRAY_SIZE(share_types); j++) { + if (share_types[j].type == info->type) { + torture_comment(tctx, + "\t[%d] %s\t%s\n" + "\t %s\n" + "\t [perms=0x%08x, " + "max_usr=%d, " + "cur_usr=%d, " + "path=%s, pass=%s]\n", + i, + info->name, + share_types[j].desc, + info->comment, + info->permissions, + info->max_users, + info->current_users, + info->path, + info->password); + break; + } + } + } + break; + } +} + + +bool torture_listshares(struct torture_context *torture) +{ + struct libnet_ListShares share; + NTSTATUS status; + uint32_t levels[] = { 0, 1, 2, 501, 502 }; + int i; + bool ret = true; + struct libnet_context* libnetctx; + struct dcerpc_binding *binding; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("test_listshares"); + status = torture_rpc_binding(torture, &binding); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + libnetctx = libnet_context_init(torture->ev, torture->lp_ctx); + if (!libnetctx) { + torture_comment(torture, "Couldn't allocate libnet context\n"); + ret = false; + goto done; + } + + libnetctx->cred = samba_cmdline_get_creds(); + + torture_comment(torture, "Testing libnet_ListShare\n"); + + share.in.server_name = dcerpc_binding_get_string_option(binding, "host"); + + for (i = 0; i < ARRAY_SIZE(levels); i++) { + share.in.level = levels[i]; + torture_comment(torture, "Testing libnet_ListShare level %u\n", share.in.level); + + status = libnet_ListShares(libnetctx, mem_ctx, &share); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_ListShare level %u failed - %s\n", share.in.level, share.out.error_string); + ret = false; + goto done; + } + + torture_comment(torture, "listing shares:\n"); + test_displayshares(torture, share); + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +static bool test_addshare(struct torture_context *tctx, + struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, const char *host, + const char* share) +{ + NTSTATUS status; + struct srvsvc_NetShareAdd add; + union srvsvc_NetShareInfo info; + struct srvsvc_NetShareInfo2 i; + + ZERO_STRUCT(i); + ZERO_STRUCT(info); + ZERO_STRUCT(add); + + i.name = share; + i.type = STYPE_DISKTREE; + i.path = "C:\\WINDOWS\\TEMP"; + i.max_users = 5; + i.comment = "Comment to the test share"; + i.password = NULL; + i.permissions = 0x0; + + info.info2 = &i; + + add.in.server_unc = host; + add.in.level = 2; + add.in.info = &info; + add.in.parm_error = NULL; + + status = dcerpc_srvsvc_NetShareAdd_r(b, mem_ctx, &add); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to add a new share\n"); + return false; + } + + torture_comment(tctx, "share added\n"); + return true; +} + + +bool torture_delshare(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding *binding; + struct libnet_context* libnetctx; + const char *host; + NTSTATUS status; + bool ret = true; + struct libnet_DelShare share; + + host = torture_setting_string(torture, "host", NULL); + status = torture_rpc_binding(torture, &binding); + torture_assert_ntstatus_ok(torture, status, "Failed to get binding"); + + libnetctx = libnet_context_init(torture->ev, torture->lp_ctx); + libnetctx->cred = samba_cmdline_get_creds(); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_srvsvc); + + torture_assert_ntstatus_ok(torture, status, "Failed to get rpc connection"); + + if (!test_addshare(torture, p->binding_handle, torture, host, TEST_SHARENAME)) { + return false; + } + + share.in.server_name = dcerpc_binding_get_string_option(binding, "host"); + share.in.share_name = TEST_SHARENAME; + + status = libnet_DelShare(libnetctx, torture, &share); + torture_assert_ntstatus_ok(torture, status, "Failed to delete share"); + + return ret; +} diff --git a/source4/torture/libnet/libnet_user.c b/source4/torture/libnet/libnet_user.c new file mode 100644 index 0000000..9029827 --- /dev/null +++ b/source4/torture/libnet/libnet_user.c @@ -0,0 +1,520 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "lib/cmdline/cmdline.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/usertest.h" +#include "torture/libnet/proto.h" +#include "param/param.h" + + + +bool torture_createuser(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_context *ctx = NULL; + struct libnet_CreateUser req; + bool ret = true; + + mem_ctx = talloc_init("test_createuser"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + req.in.user_name = TEST_USERNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.out.error_string = NULL; + + status = libnet_CreateUser(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_CreateUser call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_deleteuser(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + struct policy_handle h; + struct lsa_String domain_name; + const char *name = TEST_USERNAME; + struct libnet_context *ctx = NULL; + struct libnet_DeleteUser req; + bool ret = false; + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "torture_rpc_connection() failed"); + + mem_ctx = talloc_init("torture_deleteuser"); + + /* + * Pre-create a user to be deleted later + */ + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + ret = test_domain_open(torture, p->binding_handle, &domain_name, mem_ctx, &h, NULL); + torture_assert_goto(torture, ret, ret, done, "test_domain_open() failed"); + + ret = test_user_create(torture, p->binding_handle, mem_ctx, &h, name, NULL); + torture_assert_goto(torture, ret, ret, done, "test_user_create() failed"); + + /* + * Delete the user using libnet layer + */ + ret = test_libnet_context_init(torture, true, &ctx); + torture_assert_goto(torture, ret, ret, done, "test_libnet_context_init() failed"); + + req.in.user_name = TEST_USERNAME; + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + + status = libnet_DeleteUser(ctx, mem_ctx, &req); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, "libnet_DeleteUser() failed"); + + /* mark test as successful */ + ret = true; + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +/* + Generate testing set of random changes +*/ + +static void set_test_changes(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct libnet_ModifyUser *r, + int num_changes, char **user_name, enum test_fields req_change) +{ + const char* logon_scripts[] = { "start_login.cmd", "login.bat", "start.cmd" }; + const char* home_dirs[] = { "\\\\srv\\home", "\\\\homesrv\\home\\user", "\\\\pdcsrv\\domain" }; + const char* home_drives[] = { "H:", "z:", "I:", "J:", "n:" }; + const uint32_t flags[] = { (ACB_DISABLED | ACB_NORMAL | ACB_PW_EXPIRED), + (ACB_NORMAL | ACB_PWNOEXP), + (ACB_NORMAL | ACB_PW_EXPIRED) }; + const char *homedir, *homedrive, *logonscript; + struct timeval now; + int i, testfld; + + torture_comment(tctx, "Fields to change: ["); + + for (i = 0; i < num_changes && i <= USER_FIELD_LAST; i++) { + const char *fldname; + + testfld = (req_change == none) ? (random() % USER_FIELD_LAST) + 1 : req_change; + + /* get one in case we hit time field this time */ + gettimeofday(&now, NULL); + + switch (testfld) { + case acct_name: + continue_if_field_set(r->in.account_name); + r->in.account_name = talloc_asprintf(mem_ctx, TEST_CHG_ACCOUNTNAME, + (int)(random() % 100)); + fldname = "account_name"; + + /* update the test's user name in case it's about to change */ + *user_name = talloc_strdup(mem_ctx, r->in.account_name); + break; + + case acct_full_name: + continue_if_field_set(r->in.full_name); + r->in.full_name = talloc_asprintf(mem_ctx, TEST_CHG_FULLNAME, + (unsigned int)random(), (unsigned int)random()); + fldname = "full_name"; + break; + + case acct_description: + continue_if_field_set(r->in.description); + r->in.description = talloc_asprintf(mem_ctx, TEST_CHG_DESCRIPTION, + (long)random()); + fldname = "description"; + break; + + case acct_home_directory: + continue_if_field_set(r->in.home_directory); + homedir = home_dirs[random() % ARRAY_SIZE(home_dirs)]; + r->in.home_directory = talloc_strdup(mem_ctx, homedir); + fldname = "home_dir"; + break; + + case acct_home_drive: + continue_if_field_set(r->in.home_drive); + homedrive = home_drives[random() % ARRAY_SIZE(home_drives)]; + r->in.home_drive = talloc_strdup(mem_ctx, homedrive); + fldname = "home_drive"; + break; + + case acct_comment: + continue_if_field_set(r->in.comment); + r->in.comment = talloc_asprintf(mem_ctx, TEST_CHG_COMMENT, + (unsigned long)random(), (unsigned long)random()); + fldname = "comment"; + break; + + case acct_logon_script: + continue_if_field_set(r->in.logon_script); + logonscript = logon_scripts[random() % ARRAY_SIZE(logon_scripts)]; + r->in.logon_script = talloc_strdup(mem_ctx, logonscript); + fldname = "logon_script"; + break; + + case acct_profile_path: + continue_if_field_set(r->in.profile_path); + r->in.profile_path = talloc_asprintf(mem_ctx, TEST_CHG_PROFILEPATH, + (unsigned long)random(), (unsigned int)random()); + fldname = "profile_path"; + break; + + case acct_expiry: + continue_if_field_set(r->in.acct_expiry); + now = timeval_add(&now, (random() % (31*24*60*60)), 0); + r->in.acct_expiry = (struct timeval *)talloc_memdup(mem_ctx, &now, sizeof(now)); + fldname = "acct_expiry"; + break; + + case acct_flags: + continue_if_field_set(r->in.acct_flags); + r->in.acct_flags = flags[random() % ARRAY_SIZE(flags)]; + fldname = "acct_flags"; + break; + + default: + fldname = "unknown_field"; + } + + torture_comment(tctx, ((i < num_changes - 1) ? "%s," : "%s"), fldname); + + /* disable requested field (it's supposed to be the only one used) */ + if (req_change != none) req_change = none; + } + + torture_comment(tctx, "]\n"); +} + + +#define TEST_STR_FLD(fld) \ + if (!strequal(req.in.fld, user_req.out.fld)) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + +#define TEST_TIME_FLD(fld) \ + if (timeval_compare(req.in.fld, user_req.out.fld)) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + +#define TEST_NUM_FLD(fld) \ + if (req.in.fld != user_req.out.fld) { \ + torture_comment(torture, "failed to change '%s'\n", #fld); \ + ret = false; \ + goto cleanup; \ + } + + +bool torture_modifyuser(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *prep_mem_ctx; + struct policy_handle h; + struct lsa_String domain_name; + char *name; + struct libnet_context *ctx = NULL; + struct libnet_ModifyUser req; + struct libnet_UserInfo user_req; + int fld; + bool ret = true; + struct dcerpc_binding_handle *b; + + prep_mem_ctx = talloc_init("prepare test_deleteuser"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + b = p->binding_handle; + + name = talloc_strdup(prep_mem_ctx, TEST_USERNAME); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + torture_comment(torture, "Testing change of all fields - each single one in turn\n"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + for (fld = USER_FIELD_FIRST; fld <= USER_FIELD_LAST; fld++) { + ZERO_STRUCT(req); + req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + req.in.user_name = name; + + set_test_changes(torture, torture, &req, 1, &name, fld); + + status = libnet_ModifyUser(ctx, torture, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_ModifyUser call failed: %s\n", nt_errstr(status)); + ret = false; + continue; + } + + ZERO_STRUCT(user_req); + user_req.in.domain_name = lpcfg_workgroup(torture->lp_ctx); + user_req.in.data.user_name = name; + user_req.in.level = USER_INFO_BY_NAME; + + status = libnet_UserInfo(ctx, torture, &user_req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_UserInfo call failed: %s\n", nt_errstr(status)); + ret = false; + continue; + } + + switch (fld) { + case acct_name: TEST_STR_FLD(account_name); + break; + case acct_full_name: TEST_STR_FLD(full_name); + break; + case acct_comment: TEST_STR_FLD(comment); + break; + case acct_description: TEST_STR_FLD(description); + break; + case acct_home_directory: TEST_STR_FLD(home_directory); + break; + case acct_home_drive: TEST_STR_FLD(home_drive); + break; + case acct_logon_script: TEST_STR_FLD(logon_script); + break; + case acct_profile_path: TEST_STR_FLD(profile_path); + break; + case acct_expiry: TEST_TIME_FLD(acct_expiry); + break; + case acct_flags: TEST_NUM_FLD(acct_flags); + break; + default: + break; + } + } + +cleanup: + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + torture, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, torture, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(prep_mem_ctx); + return ret; +} + + +bool torture_userinfo_api(struct torture_context *torture) +{ + const char *name = TEST_USERNAME; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL, *prep_mem_ctx; + struct libnet_context *ctx = NULL; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct libnet_UserInfo req; + struct dcerpc_binding_handle *b; + + prep_mem_ctx = talloc_init("prepare torture user info"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, prep_mem_ctx, &h, NULL)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, prep_mem_ctx, &h, name, NULL)) { + ret = false; + goto done; + } + + mem_ctx = talloc_init("torture user info"); + + if (!test_libnet_context_init(torture, true, &ctx)) { + return false; + } + + ZERO_STRUCT(req); + + req.in.domain_name = domain_name.string; + req.in.data.user_name = name; + req.in.level = USER_INFO_BY_NAME; + + status = libnet_UserInfo(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "libnet_UserInfo call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, ctx->samr.pipe->binding_handle, + mem_ctx, &ctx->samr.handle, TEST_USERNAME)) { + torture_comment(torture, "cleanup failed\n"); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "domain close failed\n"); + ret = false; + } + +done: + talloc_free(ctx); + talloc_free(mem_ctx); + return ret; +} + + +bool torture_userlist(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct libnet_context *ctx; + struct lsa_String domain_name; + struct libnet_UserList req; + int i; + + ctx = libnet_context_init(torture->ev, torture->lp_ctx); + ctx->cred = samba_cmdline_get_creds(); + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + mem_ctx = talloc_init("torture user list"); + + ZERO_STRUCT(req); + + torture_comment(torture, "listing user accounts:\n"); + + do { + + req.in.domain_name = domain_name.string; + req.in.page_size = 128; + req.in.resume_index = req.out.resume_index; + + status = libnet_UserList(ctx, mem_ctx, &req); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; + + for (i = 0; i < req.out.count; i++) { + torture_comment(torture, "\tuser: %s, sid=%s\n", + req.out.users[i].username, req.out.users[i].sid); + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (!(NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(torture, "libnet_UserList call failed: %s\n", nt_errstr(status)); + ret = false; + goto done; + } + + if (!test_samr_close_handle(torture, + ctx->samr.pipe->binding_handle, mem_ctx, &ctx->samr.handle)) { + torture_comment(torture, "samr domain close failed\n"); + ret = false; + goto done; + } + + if (!test_lsa_close_handle(torture, + ctx->lsa.pipe->binding_handle, mem_ctx, &ctx->lsa.handle)) { + torture_comment(torture, "lsa domain close failed\n"); + ret = false; + } + + talloc_free(ctx); + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/python/samr-test.py b/source4/torture/libnet/python/samr-test.py new file mode 100644 index 0000000..4181e56 --- /dev/null +++ b/source4/torture/libnet/python/samr-test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright (C) Kamen Mazdrashki 2009 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Usage: +# export ACCOUNT_NAME=kamen +# export NEW_PASS=test +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$samba4srcdir/torture/libnet/python" $SUBUNITRUN samr-test -Ukma-exch.devel/Administrator%333 +# + +import os + +from samba import net +import samba.tests + +if "ACCOUNT_NAME" not in os.environ.keys(): + raise Exception("Please supply ACCOUNT_NAME in environment") + +if "NEW_PASS" not in os.environ.keys(): + raise Exception("Please supply NEW_PASS in environment") + +account_name = os.environ["ACCOUNT_NAME"] +new_pass = os.environ["NEW_PASS"] + +# +# Tests start here +# + + +class Libnet_SetPwdTest(samba.tests.TestCase): + + ######################################################################################## + + def test_SetPassword(self): + creds = self.get_credentials() + net.SetPassword(account_name=account_name, + domain_name=creds.get_domain(), + newpassword=new_pass, + credentials=creds) + + ######################################################################################## diff --git a/source4/torture/libnet/userinfo.c b/source4/torture/libnet/userinfo.c new file mode 100644 index 0000000..897273b --- /dev/null +++ b/source4/torture/libnet/userinfo.c @@ -0,0 +1,192 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include "torture/libnet/proto.h" + + +#define TEST_USERNAME "libnetuserinfotest" + +static bool test_userinfo(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* user_name, + uint32_t *rid) +{ + const uint16_t level = 5; + NTSTATUS status; + struct libnet_rpc_userinfo user; + struct dom_sid *user_sid; + + user_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = dom_sid_string(mem_ctx, user_sid); + user.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing sync libnet_rpc_userinfo (SID argument)\n"); + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling sync libnet_rpc_userinfo() failed"); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = NULL; + user.in.username = user_name; + user.in.level = level; + + torture_comment(tctx, "Testing sync libnet_rpc_userinfo (username argument)\n"); + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling sync libnet_rpc_userinfo failed"); + + return true; +} + + +static bool test_userinfo_async(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + struct dom_sid2 *domain_sid, const char* user_name, + uint32_t *rid) +{ + const uint16_t level = 10; + NTSTATUS status; + struct composite_context *c; + struct libnet_rpc_userinfo user; + struct dom_sid *user_sid; + + user_sid = dom_sid_add_rid(mem_ctx, domain_sid, *rid); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = dom_sid_string(mem_ctx, user_sid); + user.in.level = level; /* this should be extended */ + + torture_comment(tctx, "Testing async libnet_rpc_userinfo (SID argument)\n"); + + c = libnet_rpc_userinfo_send(mem_ctx, tctx->ev, p->binding_handle, &user, msg_handler); + torture_assert(tctx, c != NULL, "Failed to call async libnet_rpc_userinfo_send"); + + status = libnet_rpc_userinfo_recv(c, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling async libnet_rpc_userinfo_recv failed"); + + ZERO_STRUCT(user); + + user.in.domain_handle = *domain_handle; + user.in.sid = NULL; + user.in.username = user_name; + user.in.level = level; + + torture_comment(tctx, "Testing async libnet_rpc_userinfo (username argument)\n"); + + c = libnet_rpc_userinfo_send(mem_ctx, tctx->ev, p->binding_handle, &user, msg_handler); + torture_assert(tctx, c != NULL, "Failed to call async libnet_rpc_userinfo_send"); + + status = libnet_rpc_userinfo_recv(c, mem_ctx, &user); + torture_assert_ntstatus_ok(tctx, status, "Calling async libnet_rpc_userinfo_recv failed"); + + return true; +} + + +bool torture_userinfo(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct policy_handle h; + struct lsa_String name; + struct dom_sid2 sid; + uint32_t rid; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userinfo"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + name.string = lpcfg_workgroup(torture->lp_ctx); + + /* + * Testing synchronous version + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_userinfo(torture, p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + + /* + * Testing asynchronous version and monitor messages + */ + if (!test_domain_open(torture, b, &name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_userinfo_async(torture, p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/libnet/userman.c b/source4/torture/libnet/userman.c new file mode 100644 index 0000000..8c49bb6 --- /dev/null +++ b/source4/torture/libnet/userman.c @@ -0,0 +1,473 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/libnet/usertest.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" + +#include "torture/libnet/proto.h" + + +static bool test_useradd(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + NTSTATUS status; + bool ret = true; + struct libnet_rpc_useradd user; + + user.in.domain_handle = *domain_handle; + user.in.username = name; + + torture_comment(tctx, "Testing libnet_rpc_useradd\n"); + + status = libnet_rpc_useradd(tctx->ev, p->binding_handle, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call libnet_rpc_useradd - %s\n", nt_errstr(status)); + return false; + } + + return ret; +} + + +static bool test_useradd_async(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char* username) +{ + NTSTATUS status; + struct composite_context *c; + struct libnet_rpc_useradd user; + + user.in.domain_handle = *handle; + user.in.username = username; + + torture_comment(tctx, "Testing async libnet_rpc_useradd\n"); + + c = libnet_rpc_useradd_send(mem_ctx, tctx->ev, p->binding_handle, + &user, msg_handler); + if (!c) { + torture_comment(tctx, "Failed to call async libnet_rpc_useradd\n"); + return false; + } + + status = libnet_rpc_useradd_recv(c, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Calling async libnet_rpc_useradd failed - %s\n", nt_errstr(status)); + return false; + } + + return true; + +} + +static bool test_usermod(struct torture_context *tctx, struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, int num_changes, + struct libnet_rpc_usermod *mod, char **username) +{ + const char* logon_scripts[] = { "start_login.cmd", "login.bat", "start.cmd" }; + const char* home_dirs[] = { "\\\\srv\\home", "\\\\homesrv\\home\\user", "\\\\pdcsrv\\domain" }; + const char* home_drives[] = { "H:", "z:", "I:", "J:", "n:" }; + const char *homedir, *homedrive, *logonscript; + const uint32_t flags[] = { (ACB_DISABLED | ACB_NORMAL | ACB_PW_EXPIRED), + (ACB_NORMAL | ACB_PWNOEXP), + (ACB_NORMAL | ACB_PW_EXPIRED) }; + + NTSTATUS status; + struct timeval now; + enum test_fields testfld; + int i; + + ZERO_STRUCT(*mod); + srandom((unsigned)time(NULL)); + + mod->in.username = talloc_strdup(mem_ctx, *username); + mod->in.domain_handle = *handle; + + torture_comment(tctx, "modifying user (%d simultaneous change(s))\n", + num_changes); + + torture_comment(tctx, "fields to change: ["); + + for (i = 0; i < num_changes && i <= USER_FIELD_LAST; i++) { + const char *fldname; + + testfld = (random() % USER_FIELD_LAST) + 1; + + GetTimeOfDay(&now); + + switch (testfld) { + case acct_name: + continue_if_field_set(mod->in.change.account_name); + mod->in.change.account_name = talloc_asprintf(mem_ctx, TEST_CHG_ACCOUNTNAME, + (int)(random() % 100)); + mod->in.change.fields |= USERMOD_FIELD_ACCOUNT_NAME; + fldname = "account_name"; + *username = talloc_strdup(mem_ctx, mod->in.change.account_name); + break; + + case acct_full_name: + continue_if_field_set(mod->in.change.full_name); + mod->in.change.full_name = talloc_asprintf(mem_ctx, TEST_CHG_FULLNAME, + (int)random(), (int)random()); + mod->in.change.fields |= USERMOD_FIELD_FULL_NAME; + fldname = "full_name"; + break; + + case acct_description: + continue_if_field_set(mod->in.change.description); + mod->in.change.description = talloc_asprintf(mem_ctx, TEST_CHG_DESCRIPTION, + random()); + mod->in.change.fields |= USERMOD_FIELD_DESCRIPTION; + fldname = "description"; + break; + + case acct_home_directory: + continue_if_field_set(mod->in.change.home_directory); + homedir = home_dirs[random() % (sizeof(home_dirs)/sizeof(char*))]; + mod->in.change.home_directory = talloc_strdup(mem_ctx, homedir); + mod->in.change.fields |= USERMOD_FIELD_HOME_DIRECTORY; + fldname = "home_directory"; + break; + + case acct_home_drive: + continue_if_field_set(mod->in.change.home_drive); + homedrive = home_drives[random() % (sizeof(home_drives)/sizeof(char*))]; + mod->in.change.home_drive = talloc_strdup(mem_ctx, homedrive); + mod->in.change.fields |= USERMOD_FIELD_HOME_DRIVE; + fldname = "home_drive"; + break; + + case acct_comment: + continue_if_field_set(mod->in.change.comment); + mod->in.change.comment = talloc_asprintf(mem_ctx, TEST_CHG_COMMENT, + random(), random()); + mod->in.change.fields |= USERMOD_FIELD_COMMENT; + fldname = "comment"; + break; + + case acct_logon_script: + continue_if_field_set(mod->in.change.logon_script); + logonscript = logon_scripts[random() % (sizeof(logon_scripts)/sizeof(char*))]; + mod->in.change.logon_script = talloc_strdup(mem_ctx, logonscript); + mod->in.change.fields |= USERMOD_FIELD_LOGON_SCRIPT; + fldname = "logon_script"; + break; + + case acct_profile_path: + continue_if_field_set(mod->in.change.profile_path); + mod->in.change.profile_path = talloc_asprintf(mem_ctx, TEST_CHG_PROFILEPATH, + (long int)random(), (unsigned int)random()); + mod->in.change.fields |= USERMOD_FIELD_PROFILE_PATH; + fldname = "profile_path"; + break; + + case acct_expiry: + continue_if_field_set(mod->in.change.acct_expiry); + now = timeval_add(&now, (random() % (31*24*60*60)), 0); + mod->in.change.acct_expiry = (struct timeval *)talloc_memdup(mem_ctx, &now, sizeof(now)); + mod->in.change.fields |= USERMOD_FIELD_ACCT_EXPIRY; + fldname = "acct_expiry"; + break; + + case acct_flags: + continue_if_field_set(mod->in.change.acct_flags); + mod->in.change.acct_flags = flags[random() % ARRAY_SIZE(flags)]; + mod->in.change.fields |= USERMOD_FIELD_ACCT_FLAGS; + fldname = "acct_flags"; + break; + + default: + fldname = talloc_asprintf(mem_ctx, "unknown_field (%d)", testfld); + break; + } + + torture_comment(tctx, ((i < num_changes - 1) ? "%s," : "%s"), fldname); + } + torture_comment(tctx, "]\n"); + + status = libnet_rpc_usermod(tctx->ev, p->binding_handle, mem_ctx, mod); + torture_assert_ntstatus_ok(tctx, status, "Failed to call sync libnet_rpc_usermod"); + + return true; +} + + +static bool test_userdel(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *username) +{ + NTSTATUS status; + struct libnet_rpc_userdel user; + + ZERO_STRUCT(user); + + user.in.domain_handle = *handle; + user.in.username = username; + + status = libnet_rpc_userdel(tctx->ev, p->binding_handle, mem_ctx, &user); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to call sync libnet_rpc_userdel - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + + +#define CMP_LSA_STRING_FLD(fld, flags) \ + if ((mod->in.change.fields & flags) && \ + !strequal(i->fld.string, mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%s'\n", i->fld.string); \ + torture_comment(tctx, "expected: '%s'\n", mod->in.change.fld); \ + return false; \ + } + + +#define CMP_TIME_FLD(fld, flags) \ + if (mod->in.change.fields & flags) { \ + nttime_to_timeval(&t, i->fld); \ + if (timeval_compare(&t, mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%s (+%ld us)'\n", \ + timestring(mem_ctx, t.tv_sec), (long)t.tv_usec); \ + torture_comment(tctx, "expected: '%s (+%ld us)'\n", \ + timestring(mem_ctx, mod->in.change.fld->tv_sec), \ + (long)mod->in.change.fld->tv_usec); \ + return false; \ + } \ + } + +#define CMP_NUM_FLD(fld, flags) \ + if ((mod->in.change.fields & flags) && \ + (i->fld != mod->in.change.fld)) { \ + torture_comment(tctx, "'%s' field does not match\n", #fld); \ + torture_comment(tctx, "received: '%04x'\n", i->fld); \ + torture_comment(tctx, "expected: '%04x'\n", mod->in.change.fld); \ + return false; \ + } + + +static bool test_compare(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct policy_handle *handle, struct libnet_rpc_usermod *mod, + const char *username) +{ + NTSTATUS status; + struct libnet_rpc_userinfo info; + struct samr_UserInfo21 *i; + struct timeval t; + + ZERO_STRUCT(info); + + info.in.username = username; + info.in.domain_handle = *handle; + info.in.level = 21; /* the most rich infolevel available */ + + status = libnet_rpc_userinfo(tctx->ev, p->binding_handle, mem_ctx, &info); + torture_assert_ntstatus_ok(tctx, status, "Failed to call sync libnet_rpc_userinfo"); + + i = &info.out.info.info21; + + CMP_LSA_STRING_FLD(account_name, USERMOD_FIELD_ACCOUNT_NAME); + CMP_LSA_STRING_FLD(full_name, USERMOD_FIELD_FULL_NAME); + CMP_LSA_STRING_FLD(description, USERMOD_FIELD_DESCRIPTION); + CMP_LSA_STRING_FLD(comment, USERMOD_FIELD_COMMENT); + CMP_LSA_STRING_FLD(logon_script, USERMOD_FIELD_LOGON_SCRIPT); + CMP_LSA_STRING_FLD(profile_path, USERMOD_FIELD_PROFILE_PATH); + CMP_LSA_STRING_FLD(home_directory, USERMOD_FIELD_HOME_DIRECTORY); + CMP_LSA_STRING_FLD(home_drive, USERMOD_FIELD_HOME_DRIVE); + CMP_TIME_FLD(acct_expiry, USERMOD_FIELD_ACCT_EXPIRY); + CMP_NUM_FLD(acct_flags, USERMOD_FIELD_ACCT_FLAGS) + + return true; +} + + +bool torture_useradd(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + const char *name = TEST_USERNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_useradd"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connect failed"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_useradd(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_useradd_async(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + + if (!test_user_cleanup(torture, b, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_userdel(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + uint32_t rid; + const char *name = TEST_USERNAME; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userdel"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, name, &rid)) { + ret = false; + goto done; + } + + if (!test_userdel(torture, p, mem_ctx, &h, name)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} + + +bool torture_usermod(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct policy_handle h; + struct lsa_String domain_name; + struct dom_sid2 sid; + uint32_t rid; + int i; + char *name; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct dcerpc_binding_handle *b; + + mem_ctx = talloc_init("test_userdel"); + + status = torture_rpc_connection(torture, + &p, + &ndr_table_samr); + + torture_assert_ntstatus_ok(torture, status, "RPC connect"); + b = p->binding_handle; + + domain_name.string = lpcfg_workgroup(torture->lp_ctx); + name = talloc_strdup(mem_ctx, TEST_USERNAME); + + if (!test_domain_open(torture, b, &domain_name, mem_ctx, &h, &sid)) { + ret = false; + goto done; + } + + if (!test_user_create(torture, b, mem_ctx, &h, name, &rid)) { + ret = false; + goto done; + } + + for (i = USER_FIELD_FIRST; i <= USER_FIELD_LAST; i++) { + struct libnet_rpc_usermod m; + + if (!test_usermod(torture, p, mem_ctx, &h, i, &m, &name)) { + ret = false; + goto cleanup; + } + + if (!test_compare(torture, p, mem_ctx, &h, &m, name)) { + ret = false; + goto cleanup; + } + } + +cleanup: + if (!test_user_cleanup(torture, b, mem_ctx, &h, TEST_USERNAME)) { + ret = false; + goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source4/torture/libnet/usertest.h b/source4/torture/libnet/usertest.h new file mode 100644 index 0000000..b3b2dc1 --- /dev/null +++ b/source4/torture/libnet/usertest.h @@ -0,0 +1,42 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Rafal Szczesniak 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define TEST_USERNAME "libnetusertest" + +#define continue_if_field_set(field) \ + if (field != 0) { \ + i--; \ + continue; \ + } + + +#define USER_FIELD_FIRST acct_name +#define USER_FIELD_LAST acct_flags + +enum test_fields { none = 0, + acct_name, acct_full_name, acct_description, + acct_home_directory, acct_home_drive, acct_comment, + acct_logon_script, acct_profile_path, acct_expiry, acct_flags }; + + +#define TEST_CHG_ACCOUNTNAME "newlibnetusertest%02d" +#define TEST_CHG_DESCRIPTION "Sample description %ld" +#define TEST_CHG_FULLNAME "First%04x Last%04x" +#define TEST_CHG_COMMENT "Comment[%04lu%04lu]" +#define TEST_CHG_PROFILEPATH "\\\\srv%04ld\\profile%02u\\prof" diff --git a/source4/torture/libnet/utils.c b/source4/torture/libnet/utils.c new file mode 100644 index 0000000..c0e0e65 --- /dev/null +++ b/source4/torture/libnet/utils.c @@ -0,0 +1,556 @@ +/* + Unix SMB/CIFS implementation. + Test suite for libnet calls. + + Copyright (C) Rafal Szczesniak 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * These are more general use functions shared among the tests. + */ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/libnet/proto.h" +#include "ldb_wrap.h" + +/** + * Opens handle on Domain using SAMR + * + * @param _domain_handle [out] Ptr to storage to store Domain handle + * @param _dom_sid [out] If NULL, Domain SID won't be returned + */ +bool test_domain_open(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct lsa_String *domname, + TALLOC_CTX *mem_ctx, + struct policy_handle *_domain_handle, + struct dom_sid2 *_dom_sid) +{ + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct samr_Connect r1; + struct samr_LookupDomain r2; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain r3; + + torture_comment(tctx, "connecting\n"); + + r1.in.system_name = 0; + r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r1.out.connect_handle = &connect_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect_r(b, mem_ctx, &r1), + "Connect failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "Connect failed"); + + r2.in.connect_handle = &connect_handle; + r2.in.domain_name = domname; + r2.out.sid = &sid; + + torture_comment(tctx, "domain lookup on %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupDomain_r(b, mem_ctx, &r2), + "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "LookupDomain failed"); + + r3.in.connect_handle = &connect_handle; + r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r3.in.sid = *r2.out.sid; + r3.out.domain_handle = &domain_handle; + + torture_comment(tctx, "opening domain %s\n", domname->string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, mem_ctx, &r3), + "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "OpenDomain failed"); + + *_domain_handle = domain_handle; + + if (_dom_sid) { + *_dom_sid = **r2.out.sid; + } + + /* Close connect_handle, we don't need it anymore */ + test_samr_close_handle(tctx, b, mem_ctx, &connect_handle); + + return true; +} + + +/** + * Find out user's samAccountName for given + * user RDN. We need samAccountName value + * when deleting users. + */ +static bool _get_account_name_for_user_rdn(struct torture_context *tctx, + const char *user_rdn, + TALLOC_CTX *mem_ctx, + const char **_account_name) +{ + const char *url; + struct ldb_context *ldb; + TALLOC_CTX *tmp_ctx; + bool test_res = true; + const char *hostname = torture_setting_string(tctx, "host", NULL); + int ldb_ret; + struct ldb_result *ldb_res; + const char *account_name = NULL; + static const char *attrs[] = { + "samAccountName", + NULL + }; + + torture_assert(tctx, hostname != NULL, "Failed to get hostname"); + + tmp_ctx = talloc_new(tctx); + torture_assert(tctx, tmp_ctx != NULL, "Failed to create temporary mem context"); + + url = talloc_asprintf(tmp_ctx, "ldap://%s/", hostname); + torture_assert_goto(tctx, url != NULL, test_res, done, "Failed to allocate URL for ldb"); + + ldb = ldb_wrap_connect(tmp_ctx, + tctx->ev, tctx->lp_ctx, + url, NULL, samba_cmdline_get_creds(), 0); + torture_assert_goto(tctx, ldb != NULL, test_res, done, "Failed to make LDB connection"); + + ldb_ret = ldb_search(ldb, tmp_ctx, &ldb_res, + ldb_get_default_basedn(ldb), LDB_SCOPE_SUBTREE, + attrs, + "(&(objectClass=user)(name=%s))", user_rdn); + if (LDB_SUCCESS == ldb_ret && 1 == ldb_res->count) { + account_name = ldb_msg_find_attr_as_string(ldb_res->msgs[0], "samAccountName", NULL); + } + + /* return user_rdn by default */ + if (!account_name) { + account_name = user_rdn; + } + + /* duplicate memory in parent context */ + *_account_name = talloc_strdup(mem_ctx, account_name); + +done: + talloc_free(tmp_ctx); + return test_res; +} + +/** + * Removes user by RDN through SAMR interface. + * + * @param domain_handle [in] Domain handle + * @param user_rdn [in] User's RDN in ldap database + */ +bool test_user_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *user_rdn) +{ + struct samr_LookupNames r1; + struct samr_OpenUser r2; + struct samr_DeleteUser r3; + struct lsa_String names[2]; + uint32_t rid; + struct policy_handle user_handle; + struct samr_Ids rids, types; + const char *account_name; + + if (!_get_account_name_for_user_rdn(tctx, user_rdn, mem_ctx, &account_name)) { + torture_result(tctx, TORTURE_FAIL, + __location__": Failed to find samAccountName for %s", user_rdn); + return false; + } + + names[0].string = account_name; + + r1.in.domain_handle = domain_handle; + r1.in.num_names = 1; + r1.in.names = names; + r1.out.rids = &rids; + r1.out.types = &types; + + torture_comment(tctx, "user account lookup '%s'\n", account_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupNames_r(b, mem_ctx, &r1), + "LookupNames failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "LookupNames failed"); + + rid = r1.out.rids->ids[0]; + + r2.in.domain_handle = domain_handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.rid = rid; + r2.out.user_handle = &user_handle; + + torture_comment(tctx, "opening user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenUser_r(b, mem_ctx, &r2), + "OpenUser failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "OpenUser failed"); + + r3.in.user_handle = &user_handle; + r3.out.user_handle = &user_handle; + + torture_comment(tctx, "deleting user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_DeleteUser_r(b, mem_ctx, &r3), + "DeleteUser failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "DeleteUser failed"); + + return true; +} + + +/** + * Creates new user using SAMR + * + * @param name [in] Username for user to create + * @param rid [out] If NULL, User's RID is not returned + */ +bool test_user_create(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name, + uint32_t *rid) +{ + struct policy_handle user_handle; + struct lsa_String username; + struct samr_CreateUser r; + uint32_t user_rid; + + username.string = name; + + r.in.domain_handle = domain_handle; + r.in.account_name = &username; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + /* return user's RID only if requested */ + r.out.rid = rid ? rid : &user_rid; + + torture_comment(tctx, "creating user '%s'\n", username.string); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateUser_r(b, mem_ctx, &r), + "CreateUser RPC call failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateUser failed - %s\n", nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + torture_comment(tctx, + "User (%s) already exists - " + "attempting to delete and recreate account again\n", + username.string); + if (!test_user_cleanup(tctx, b, mem_ctx, domain_handle, username.string)) { + return false; + } + + torture_comment(tctx, "creating user account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateUser_r(b, mem_ctx, &r), + "CreateUser RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateUser failed"); + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &user_handle); + + return true; + } + return false; + } + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &user_handle); + + return true; +} + + +/** + * Deletes a Group using SAMR interface + */ +bool test_group_cleanup(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_handle, + const char *name) +{ + struct samr_LookupNames r1; + struct samr_OpenGroup r2; + struct samr_DeleteDomainGroup r3; + struct lsa_String names[2]; + uint32_t rid; + struct policy_handle group_handle; + struct samr_Ids rids, types; + + names[0].string = name; + + r1.in.domain_handle = domain_handle; + r1.in.num_names = 1; + r1.in.names = names; + r1.out.rids = &rids; + r1.out.types = &types; + + torture_comment(tctx, "group account lookup '%s'\n", name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupNames_r(b, mem_ctx, &r1), + "LookupNames failed"); + torture_assert_ntstatus_ok(tctx, r1.out.result, + "LookupNames failed"); + + rid = r1.out.rids->ids[0]; + + r2.in.domain_handle = domain_handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.rid = rid; + r2.out.group_handle = &group_handle; + + torture_comment(tctx, "opening group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenGroup_r(b, mem_ctx, &r2), + "OpenGroup failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "OpenGroup failed"); + + r3.in.group_handle = &group_handle; + r3.out.group_handle = &group_handle; + + torture_comment(tctx, "deleting group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_DeleteDomainGroup_r(b, mem_ctx, &r3), + "DeleteGroup failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "DeleteGroup failed"); + + return true; +} + + +/** + * Creates a Group object using SAMR interface + * + * @param group_name [in] Name of the group to create + * @param rid [out] RID of group created. May be NULL in + * which case RID is not required by caller + */ +bool test_group_create(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, + const char *group_name, + uint32_t *rid) +{ + uint32_t group_rid; + struct lsa_String groupname; + struct samr_CreateDomainGroup r; + struct policy_handle group_handle; + + groupname.string = group_name; + + r.in.domain_handle = handle; + r.in.name = &groupname; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.group_handle = &group_handle; + /* use local variable in case caller + * don't care about the group RID */ + r.out.rid = rid ? rid : &group_rid; + + torture_comment(tctx, "creating group account %s\n", group_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateDomainGroup_r(b, mem_ctx, &r), + "CreateGroup failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateGroup failed - %s\n", nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_GROUP_EXISTS)) { + torture_comment(tctx, + "Group (%s) already exists - " + "attempting to delete and recreate group again\n", + group_name); + if (!test_group_cleanup(tctx, b, mem_ctx, handle, group_name)) { + return false; + } + + torture_comment(tctx, "creating group account\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateDomainGroup_r(b, mem_ctx, &r), + "CreateGroup failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateGroup failed"); + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &group_handle); + + return true; + } + return false; + } + + /* be nice and close opened handles */ + test_samr_close_handle(tctx, b, mem_ctx, &group_handle); + + return true; +} + +/** + * Closes SAMR handle obtained from Connect, Open User/Domain, etc + */ +bool test_samr_close_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *samr_handle) +{ + struct samr_Close r; + + r.in.handle = samr_handle; + r.out.handle = samr_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Close_r(b, mem_ctx, &r), + "Close SAMR handle RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close SAMR handle failed"); + + return true; +} + +/** + * Closes LSA handle obtained from Connect, Open Group, etc + */ +bool test_lsa_close_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *lsa_handle) +{ + struct lsa_Close r; + + r.in.handle = lsa_handle; + r.out.handle = lsa_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(b, mem_ctx, &r), + "Close LSA handle RPC call failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close LSA handle failed"); + + return true; +} + +/** + * Create and initialize libnet_context Context. + * Use this function in cases where we need to have SAMR and LSA pipes + * of libnet_context to be connected before executing any other + * libnet call + * + * @param rpc_connect [in] Connects SAMR and LSA pipes + */ +bool test_libnet_context_init(struct torture_context *tctx, + bool rpc_connect, + struct libnet_context **_net_ctx) +{ + NTSTATUS status; + bool bret = true; + struct libnet_context *net_ctx; + + net_ctx = libnet_context_init(tctx->ev, tctx->lp_ctx); + torture_assert(tctx, net_ctx != NULL, "Failed to create libnet_context"); + + /* Use command line credentials for testing */ + net_ctx->cred = samba_cmdline_get_creds(); + + if (rpc_connect) { + /* connect SAMR pipe */ + status = torture_rpc_connection(tctx, + &net_ctx->samr.pipe, + &ndr_table_samr); + torture_assert_ntstatus_ok_goto(tctx, status, bret, done, + "Failed to connect SAMR pipe"); + + net_ctx->samr.samr_handle = net_ctx->samr.pipe->binding_handle; + + /* connect LSARPC pipe */ + status = torture_rpc_connection(tctx, + &net_ctx->lsa.pipe, + &ndr_table_lsarpc); + torture_assert_ntstatus_ok_goto(tctx, status, bret, done, + "Failed to connect LSA pipe"); + + net_ctx->lsa.lsa_handle = net_ctx->lsa.pipe->binding_handle; + } + + *_net_ctx = net_ctx; + +done: + if (!bret) { + /* a previous call has failed, + * clean up memory before exit */ + talloc_free(net_ctx); + } + return bret; +} + + +void msg_handler(struct monitor_msg *m) +{ + struct msg_rpc_open_user *msg_open; + struct msg_rpc_query_user *msg_query; + struct msg_rpc_close_user *msg_close; + struct msg_rpc_create_user *msg_create; + + switch (m->type) { + case mon_SamrOpenUser: + msg_open = (struct msg_rpc_open_user*)m->data; + printf("monitor_msg: user opened (rid=%d, access_mask=0x%08x)\n", + msg_open->rid, msg_open->access_mask); + break; + case mon_SamrQueryUser: + msg_query = (struct msg_rpc_query_user*)m->data; + printf("monitor_msg: user queried (level=%d)\n", msg_query->level); + break; + case mon_SamrCloseUser: + msg_close = (struct msg_rpc_close_user*)m->data; + printf("monitor_msg: user closed (rid=%d)\n", msg_close->rid); + break; + case mon_SamrCreateUser: + msg_create = (struct msg_rpc_create_user*)m->data; + printf("monitor_msg: user created (rid=%d)\n", msg_create->rid); + break; + } +} diff --git a/source4/torture/libnetapi/libnetapi.c b/source4/torture/libnetapi/libnetapi.c new file mode 100644 index 0000000..fa6dcef --- /dev/null +++ b/source4/torture/libnetapi/libnetapi.c @@ -0,0 +1,94 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "source3/include/includes.h" +#include "torture/smbtorture.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "source3/lib/netapi/netapi.h" +#include "source3/lib/netapi/netapi_private.h" +#include "lib/param/param.h" +#include "torture/libnetapi/proto.h" + +bool torture_libnetapi_init_context(struct torture_context *tctx, + struct libnetapi_ctx **ctx_p) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx; + TALLOC_CTX *frame = talloc_stackframe(); + struct cli_credentials *creds = samba_cmdline_get_creds(); + + if (!lp_load_global(lpcfg_configfile(tctx->lp_ctx))) { + fprintf(stderr, "error loading %s\n", lpcfg_configfile(tctx->lp_ctx)); + talloc_free(frame); + return W_ERROR_V(WERR_GEN_FAILURE); + } + + load_interfaces(); + + status = libnetapi_net_init(&ctx, tctx->lp_ctx, creds); + if (status != 0) { + talloc_free(frame); + return false; + } + + *ctx_p = ctx; + + talloc_free(frame); + return true; +} + +static bool torture_libnetapi_initialize(struct torture_context *tctx) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx; + + /* We must do this first, as otherwise we fail if we don't + * have an smb.conf in the default path (we need to use the + * torture smb.conf */ + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + status = libnetapi_init(&ctx); + + torture_assert(tctx, ctx != NULL, "Failed to get a libnetapi_ctx"); + torture_assert_int_equal(tctx, status, 0, "libnetapi_init failed despite already being set up"); + + libnetapi_free(ctx); + + return true; +} + +NTSTATUS torture_libnetapi_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(ctx, "netapi"); + + torture_suite_add_simple_test(suite, "server", torture_libnetapi_server); + torture_suite_add_simple_test(suite, "group", torture_libnetapi_group); + torture_suite_add_simple_test(suite, "user", torture_libnetapi_user); + torture_suite_add_simple_test(suite, "initialize", torture_libnetapi_initialize); + + suite->description = talloc_strdup(suite, "libnetapi convenience interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/libnetapi/libnetapi_group.c b/source4/torture/libnetapi/libnetapi_group.c new file mode 100644 index 0000000..6f6d3e2 --- /dev/null +++ b/source4/torture/libnetapi/libnetapi_group.c @@ -0,0 +1,522 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include +#include "torture/libnetapi/proto.h" + +#undef strcasecmp + +#define TORTURE_TEST_USER "testuser" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +#define NETAPI_STATUS_MSG(tctx, x,y,fn,z) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d), %s\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y, z); + +static NET_API_STATUS test_netgroupenum(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_group = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_INFO_0 *info0 = NULL; + struct GROUP_INFO_1 *info1 = NULL; + struct GROUP_INFO_2 *info2 = NULL; + struct GROUP_INFO_3 *info3 = NULL; + + torture_comment(tctx, "Testing NetGroupEnum level %d\n", level); + + do { + status = NetGroupEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct GROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_INFO_1 *)buffer; + break; + case 2: + info2 = (struct GROUP_INFO_2 *)buffer; + break; + case 3: + info3 = (struct GROUP_INFO_3 *)buffer; + break; + default: + return -1; + } + + for (i=0; igrpi0_name; + break; + case 1: + current_name = info1->grpi1_name; + break; + case 2: + current_name = info2->grpi2_name; + break; + case 3: + current_name = info3->grpi3_name; + break; + default: + break; + } + + if (strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_group) { + torture_comment(tctx, "failed to get group\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupgetusers(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *groupname, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_user = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_USERS_INFO_0 *info0 = NULL; + struct GROUP_USERS_INFO_1 *info1 = NULL; + + torture_comment(tctx, "Testing NetGroupGetUsers level %d\n", level); + + do { + status = NetGroupGetUsers(hostname, + groupname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + break; + } + for (i=0; igrui0_name; + break; + case 1: + current_name = info1->grui1_name; + break; + default: + break; + } + + if (username && strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (username && !found_user) { + torture_comment(tctx, "failed to get user\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupsetusers(struct torture_context *tctx, + const char *hostname, + const char *groupname, + uint32_t level, + size_t num_entries, + const char **names) +{ + NET_API_STATUS status; + uint8_t *buffer = NULL; + int i = 0; + size_t buf_size = 0; + + struct GROUP_USERS_INFO_0 *g0 = NULL; + struct GROUP_USERS_INFO_1 *g1 = NULL; + + torture_comment(tctx, "Testing NetGroupSetUsers level %d\n", level); + + switch (level) { + case 0: + buf_size = sizeof(struct GROUP_USERS_INFO_0) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + goto out; + } + + for (i=0; i. +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include +#include "torture/libnetapi/proto.h" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +bool torture_libnetapi_server(struct torture_context *tctx) +{ + NET_API_STATUS status = 0; + uint8_t *buffer = NULL; + int i; + + const char *hostname = torture_setting_string(tctx, "host", NULL); + struct libnetapi_ctx *ctx; + + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + torture_comment(tctx, "NetServer tests\n"); + + torture_comment(tctx, "Testing NetRemoteTOD\n"); + + status = NetRemoteTOD(hostname, &buffer); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetRemoteTOD"); + goto out; + } + NetApiBufferFree(buffer); + + torture_comment(tctx, "Testing NetRemoteTOD 10 times\n"); + + for (i=0; i<10; i++) { + status = NetRemoteTOD(hostname, &buffer); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetRemoteTOD"); + goto out; + } + NetApiBufferFree(buffer); + } + + status = 0; + + torture_comment(tctx, "NetServer tests succeeded\n"); + out: + if (status != 0) { + torture_comment(tctx, "NetServer testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + libnetapi_free(ctx); + return false; + } + + libnetapi_free(ctx); + return true; +} diff --git a/source4/torture/libnetapi/libnetapi_user.c b/source4/torture/libnetapi/libnetapi_user.c new file mode 100644 index 0000000..1411d7e --- /dev/null +++ b/source4/torture/libnetapi/libnetapi_user.c @@ -0,0 +1,487 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include +#include "torture/libnetapi/proto.h" + +#undef strcasecmp + +#define TORTURE_TEST_USER "torture_testuser" +#define TORTURE_TEST_USER2 "torture_testuser2" + +#define NETAPI_STATUS(tctx, x,y,fn) \ + torture_warning(tctx, "FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +static NET_API_STATUS test_netuserenum(struct torture_context *tctx, + const char *hostname, + uint32_t level, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + const char *current_name = NULL; + int found_user = 0; + uint8_t *buffer = NULL; + int i; + + struct USER_INFO_0 *info0 = NULL; + struct USER_INFO_1 *info1 = NULL; + struct USER_INFO_2 *info2 = NULL; + struct USER_INFO_3 *info3 = NULL; + struct USER_INFO_4 *info4 = NULL; + struct USER_INFO_10 *info10 = NULL; + struct USER_INFO_11 *info11 = NULL; + struct USER_INFO_20 *info20 = NULL; + struct USER_INFO_23 *info23 = NULL; + + torture_comment(tctx, "Testing NetUserEnum level %d\n", level); + + do { + status = NetUserEnum(hostname, + level, + FILTER_NORMAL_ACCOUNT, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct USER_INFO_0 *)buffer; + break; + case 1: + info1 = (struct USER_INFO_1 *)buffer; + break; + case 2: + info2 = (struct USER_INFO_2 *)buffer; + break; + case 3: + info3 = (struct USER_INFO_3 *)buffer; + break; + case 4: + info4 = (struct USER_INFO_4 *)buffer; + break; + case 10: + info10 = (struct USER_INFO_10 *)buffer; + break; + case 11: + info11 = (struct USER_INFO_11 *)buffer; + break; + case 20: + info20 = (struct USER_INFO_20 *)buffer; + break; + case 23: + info23 = (struct USER_INFO_23 *)buffer; + break; + default: + return -1; + } + + for (i=0; iusri0_name; + break; + case 1: + current_name = info1->usri1_name; + break; + case 2: + current_name = info2->usri2_name; + break; + case 3: + current_name = info3->usri3_name; + break; + case 4: + current_name = info4->usri4_name; + break; + case 10: + current_name = info10->usri10_name; + break; + case 11: + current_name = info11->usri11_name; + break; + case 20: + current_name = info20->usri20_name; + break; + case 23: + current_name = info23->usri23_name; + break; + default: + return -1; + } + + if (strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + case 4: + info4++; + break; + case 10: + info10++; + break; + case 11: + info11++; + break; + case 20: + info20++; + break; + case 23: + info23++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_user) { + torture_comment(tctx, "failed to get user\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS test_netuseradd(struct torture_context *tctx, + const char *hostname, + const char *username) +{ + struct USER_INFO_1 u1; + uint32_t parm_err = 0; + + ZERO_STRUCT(u1); + + torture_comment(tctx, "Testing NetUserAdd\n"); + + u1.usri1_name = username; + u1.usri1_password = "W297!832jD8J"; + u1.usri1_password_age = 0; + u1.usri1_priv = 0; + u1.usri1_home_dir = NULL; + u1.usri1_comment = "User created using Samba NetApi Example code"; + u1.usri1_flags = 0; + u1.usri1_script_path = NULL; + + return NetUserAdd(hostname, 1, (uint8_t *)&u1, &parm_err); +} + +static NET_API_STATUS test_netusermodals(struct torture_context *tctx, + struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status; + struct USER_MODALS_INFO_0 *u0 = NULL; + struct USER_MODALS_INFO_0 *_u0 = NULL; + uint8_t *buffer = NULL; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 3 }; + int i = 0; + + for (i=0; igrui0_name; + break; + case 1: + current_name = i1->grui1_name; + break; + default: + return -1; + } + + if (groupname && strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + i0++; + break; + case 1: + i1++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (groupname && !found_group) { + torture_comment(tctx, "failed to get membership\n"); + return -1; + } + + return 0; +} + +bool torture_libnetapi_user(struct torture_context *tctx) +{ + NET_API_STATUS status = 0; + uint8_t *buffer = NULL; + uint32_t levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t enum_levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t getgr_levels[] = { 0, 1 }; + int i; + + struct USER_INFO_0 u0; + struct USER_INFO_1007 u1007; + uint32_t parm_err = 0; + + const char *hostname = torture_setting_string(tctx, "host", NULL); + struct libnetapi_ctx *ctx; + + torture_assert(tctx, torture_libnetapi_init_context(tctx, &ctx), + "failed to initialize libnetapi"); + + torture_comment(tctx, "NetUser tests\n"); + + /* cleanup */ + + NetUserDel(hostname, TORTURE_TEST_USER); + NetUserDel(hostname, TORTURE_TEST_USER2); + + /* add a user */ + + status = test_netuseradd(tctx, hostname, TORTURE_TEST_USER); + if (status) { + NETAPI_STATUS(tctx, ctx, status, "NetUserAdd"); + goto out; + } + + /* enum the new user */ + + for (i=0; i. +*/ + +#include "includes.h" +#include "system/dir.h" +#include "torture/smbtorture.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include +#include "torture/libsmbclient/proto.h" +#include "lib/param/loadparm.h" +#include "lib/param/param_global.h" +#include "libcli/smb/smb_constants.h" +#include "dynconfig.h" +#include "lib/util/time.h" + +/* test string to compare with when debug_callback is called */ +#define TEST_STRING "smbc_setLogCallback test" + +/* Dummy log callback function */ +static void debug_callback(void *private_ptr, int level, const char *msg) +{ + bool *found = private_ptr; + if (strstr(msg, TEST_STRING) != NULL) { + *found = true; + } + return; +} + +static void auth_callback(const char *srv, + const char *shr, + char *wg, int wglen, + char *un, int unlen, + char *pw, int pwlen) +{ + const char *workgroup = + cli_credentials_get_domain(samba_cmdline_get_creds()); + const char *username = + cli_credentials_get_username(samba_cmdline_get_creds()); + const char *password = + cli_credentials_get_password(samba_cmdline_get_creds()); + ssize_t ret; + + if (workgroup != NULL) { + ret = strlcpy(wg, workgroup, wglen); + if (ret >= wglen) { + abort(); + } + } + + if (username != NULL) { + ret = strlcpy(un, username, unlen); + if (ret >= unlen) { + abort(); + } + } + + if (password != NULL) { + ret = strlcpy(pw, password, pwlen); + if (ret >= pwlen) { + abort(); + } + } +}; + +bool torture_libsmbclient_init_context(struct torture_context *tctx, + SMBCCTX **ctx_p) +{ + const char *workgroup = + cli_credentials_get_domain(samba_cmdline_get_creds()); + const char *username = + cli_credentials_get_username(samba_cmdline_get_creds()); + const char *client_proto = + torture_setting_string(tctx, "clientprotocol", NULL); + SMBCCTX *ctx = NULL; + SMBCCTX *p = NULL; + bool ok = true; + int dbglevel = DEBUGLEVEL; + + ctx = smbc_new_context(); + torture_assert_not_null_goto(tctx, + ctx, + ok, + out, + "Failed to create new context"); + + p = smbc_init_context(ctx); + torture_assert_not_null_goto(tctx, + p, + ok, + out, + "Failed to initialize context"); + + smbc_setDebug(ctx, dbglevel); + smbc_setOptionDebugToStderr(ctx, 1); + + if (workgroup != NULL) { + smbc_setWorkgroup(ctx, workgroup); + } + if (username != NULL) { + smbc_setUser(ctx, username); + } + + smbc_setFunctionAuthData(ctx, auth_callback); + + if (client_proto != NULL) { + smbc_setOptionProtocols(ctx, client_proto, client_proto); + } + + *ctx_p = ctx; + +out: + if (!ok) { + smbc_free_context(ctx, 1); + } + + return ok; +} + +static bool torture_libsmbclient_version(struct torture_context *tctx) +{ + torture_comment(tctx, "Testing smbc_version\n"); + + torture_assert(tctx, smbc_version(), "failed to get version"); + + return true; +} + +static bool torture_libsmbclient_initialize(struct torture_context *tctx) +{ + SMBCCTX *ctx; + bool ret = false; + + torture_comment(tctx, "Testing smbc_new_context\n"); + + ctx = smbc_new_context(); + torture_assert(tctx, ctx, "failed to get new context"); + + torture_comment(tctx, "Testing smbc_init_context\n"); + + torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); + + smbc_setLogCallback(ctx, &ret, debug_callback); + DEBUG(0, (TEST_STRING"\n")); + torture_assert(tctx, ret, "Failed debug_callback not called"); + ret = false; + smbc_setLogCallback(ctx, NULL, NULL); + DEBUG(0, (TEST_STRING"\n")); + torture_assert(tctx, !ret, "Failed debug_callback called"); + + smbc_free_context(ctx, 1); + + return true; +} + +static bool torture_libsmbclient_setConfiguration(struct torture_context *tctx) +{ + SMBCCTX *ctx; + struct loadparm_global *global_config = NULL; + const char *new_smb_conf = torture_setting_string(tctx, + "replace_smbconf", + ""); + + ctx = smbc_new_context(); + torture_assert_not_null(tctx, ctx, "failed to get new context"); + + torture_assert_not_null( + tctx, smbc_init_context(ctx), "failed to init context"); + + torture_comment(tctx, "Testing smbc_setConfiguration - new file %s\n", + new_smb_conf); + + global_config = get_globals(); + torture_assert(tctx, global_config, "Global Config is NULL"); + + /* check configuration before smbc_setConfiguration call */ + torture_comment(tctx, "'workgroup' before setConfiguration %s\n", + global_config->workgroup); + torture_comment(tctx, "'client min protocol' before " + "setConfiguration %d\n", + global_config->client_min_protocol); + torture_comment(tctx, "'client max protocol' before " + "setConfiguration %d\n", + global_config->_client_max_protocol); + torture_comment(tctx, "'client signing' before setConfiguration %d\n", + global_config->client_signing); + torture_comment(tctx, "'deadtime' before setConfiguration %d\n", + global_config->deadtime); + + torture_assert_int_equal(tctx, smbc_setConfiguration(ctx, new_smb_conf), + 0, "setConfiguration conf file not found"); + + /* verify configuration */ + torture_assert_str_equal(tctx, global_config->workgroup, + "NEW_WORKGROUP", + "smbc_setConfiguration failed, " + "'workgroup' not updated"); + torture_assert_int_equal(tctx, global_config->client_min_protocol, PROTOCOL_NT1, + "smbc_setConfiguration failed, 'client min protocol' " + "not updated"); + torture_assert_int_equal(tctx, global_config->_client_max_protocol, PROTOCOL_SMB3_00, + "smbc_setConfiguration failed, 'client max protocol' " + "not updated"); + torture_assert_int_equal(tctx, global_config->client_signing, 1, + "smbc_setConfiguration failed, 'client signing' " + "not updated"); + torture_assert_int_equal(tctx, global_config->deadtime, 5, + "smbc_setConfiguration failed, 'deadtime' not updated"); + + /* Restore configuration to default */ + smbc_setConfiguration(ctx, get_dyn_CONFIGFILE()); + + smbc_free_context(ctx, 1); + + return true; +} + +static bool test_opendir(struct torture_context *tctx, + SMBCCTX *ctx, + const char *fname, + bool expect_success) +{ + int handle, ret; + + torture_comment(tctx, "Testing smbc_opendir(%s)\n", fname); + + handle = smbc_opendir(fname); + if (!expect_success) { + return true; + } + if (handle < 0) { + torture_fail(tctx, talloc_asprintf(tctx, "failed to obain file handle for '%s'", fname)); + } + + ret = smbc_closedir(handle); + torture_assert_int_equal(tctx, ret, 0, + talloc_asprintf(tctx, "failed to close file handle for '%s'", fname)); + + return true; +} + +static bool torture_libsmbclient_opendir(struct torture_context *tctx) +{ + size_t i; + SMBCCTX *ctx; + bool ret = true; + const char *bad_urls[] = { + "", + NULL, + "smb", + "smb:", + "smb:/", + "smb:///", + "bms://", + ":", + ":/", + "://", + ":///", + "/", + "//", + "///" + }; + const char *good_urls[] = { + "smb://", + "smb://WORKGROUP", + "smb://WORKGROUP/" + }; + + torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); + smbc_set_context(ctx); + + for (i=0; i < ARRAY_SIZE(bad_urls); i++) { + ret &= test_opendir(tctx, ctx, bad_urls[i], false); + } + for (i=0; i < ARRAY_SIZE(good_urls); i++) { + ret &= test_opendir(tctx, ctx, good_urls[i], true); + } + + smbc_free_context(ctx, 1); + + return ret; +} + +static bool torture_libsmbclient_readdirplus(struct torture_context *tctx) +{ + SMBCCTX *ctx; + int ret = -1; + int dhandle = -1; + int fhandle = -1; + bool found = false; + const char *filename = NULL; + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server/share missing\n"); + } + + torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); + smbc_set_context(ctx); + + filename = talloc_asprintf(tctx, + "%s/test_readdirplus.txt", + smburl); + if (filename == NULL) { + torture_fail(tctx, + "talloc fail\n"); + } + /* Ensure the file doesn't exist. */ + smbc_unlink(filename); + + /* Create it. */ + fhandle = smbc_creat(filename, 0666); + if (fhandle < 0) { + torture_fail(tctx, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + filename, + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal(tctx, + ret, + 0, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + filename)); + + dhandle = smbc_opendir(smburl); + if (dhandle < 0) { + int saved_errno = errno; + smbc_unlink(filename); + torture_fail(tctx, + talloc_asprintf(tctx, + "failed to obtain " + "directory handle for '%s' : %s", + smburl, + strerror(saved_errno))); + } + + /* Readdirplus to ensure we see the new file. */ + for (;;) { + const struct libsmb_file_info *exstat = + smbc_readdirplus(dhandle); + if (exstat == NULL) { + break; + } + if (strcmp(exstat->name, "test_readdirplus.txt") == 0) { + found = true; + break; + } + } + + /* Remove it again. */ + smbc_unlink(filename); + ret = smbc_closedir(dhandle); + torture_assert_int_equal(tctx, + ret, + 0, + talloc_asprintf(tctx, + "failed to close directory handle for '%s'", + smburl)); + + smbc_free_context(ctx, 1); + + if (!found) { + torture_fail(tctx, + talloc_asprintf(tctx, + "failed to find file '%s'", + filename)); + } + + return true; +} + +static bool torture_libsmbclient_readdirplus_seek(struct torture_context *tctx) +{ + SMBCCTX *ctx; + int ret = -1; + int dhandle = -1; + int fhandle = -1; + const char *dname = NULL; + const char *full_filename[100] = {0}; + const char *filename[100] = {0}; + const struct libsmb_file_info *direntries[102] = {0}; + unsigned int i = 0; + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + bool success = false; + off_t telldir_50 = (off_t)-1; + off_t telldir_20 = (off_t)-1; + size_t getdentries_size = 0; + struct smbc_dirent *getdentries = NULL; + struct smbc_dirent *dirent_20 = NULL; + const struct libsmb_file_info *direntries_20 = NULL; + const struct libsmb_file_info *direntriesplus_20 = NULL; + const char *plus2_stat_path = NULL; + struct stat st = {0}; + struct stat st2 = {0}; + + torture_assert_not_null( + tctx, + smburl, + "option --option=torture:smburl=" + "smb://user:password@server/share missing\n"); + + DEBUG(0,("torture_libsmbclient_readdirplus_seek start\n")); + + torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); + smbc_set_context(ctx); + + dname = talloc_asprintf(tctx, + "%s/rd_seek", + smburl); + torture_assert_not_null_goto( + tctx, dname, success, done, "talloc fail\n"); + + /* Ensure the files don't exist. */ + for (i = 0; i < 100; i++) { + filename[i] = talloc_asprintf(tctx, + "test_readdirplus_%u.txt", + i); + torture_assert_not_null_goto( + tctx, filename[i], success, done, "talloc fail"); + full_filename[i] = talloc_asprintf(tctx, + "%s/%s", + dname, + filename[i]); + torture_assert_not_null_goto( + tctx, full_filename[i], success, done, "talloc fail"); + (void)smbc_unlink(full_filename[i]); + } + /* Ensure the directory doesn't exist. */ + (void)smbc_rmdir(dname); + + /* Create containing directory. */ + ret = smbc_mkdir(dname, 0777); + torture_assert_goto( + tctx, + ret == 0, + success, + done, + talloc_asprintf(tctx, + "failed to create directory '%s': %s", + dname, + strerror(errno))); + + DEBUG(0,("torture_libsmbclient_readdirplus_seek create\n")); + + /* Create them. */ + for (i = 0; i < 100; i++) { + fhandle = smbc_creat(full_filename[i], 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + full_filename[i], + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + full_filename[i])); + } + + DEBUG(0,("torture_libsmbclient_readdirplus_seek enum\n")); + + /* Now enumerate the directory. */ + dhandle = smbc_opendir(dname); + torture_assert_goto( + tctx, + dhandle >= 0, + success, + done, + talloc_asprintf(tctx, + "failed to obtain " + "directory handle for '%s' : %s", + dname, + strerror(errno))); + + /* Read all the files. 100 we created plus . and .. */ + for (i = 0; i < 102; i++) { + bool found = false; + unsigned int j; + + direntries[i] = smbc_readdirplus(dhandle); + if (direntries[i] == NULL) { + break; + } + + /* Store at offset 50. */ + if (i == 50) { + telldir_50 = smbc_telldir(dhandle); + torture_assert_goto( + tctx, + telldir_50 != (off_t)-1, + success, + done, + talloc_asprintf(tctx, + "telldir failed file %s\n", + direntries[i]->name)); + } + + if (ISDOT(direntries[i]->name)) { + continue; + } + if (ISDOTDOT(direntries[i]->name)) { + continue; + } + + /* Ensure all our files exist. */ + for (j = 0; j < 100; j++) { + if (strcmp(direntries[i]->name, + filename[j]) == 0) { + found = true; + } + } + torture_assert_goto( + tctx, + found, + success, + done, + talloc_asprintf(tctx, + "failed to find file %s\n", + direntries[i]->name)); + } + + /* + * We're seeking on in-memory lists here, so + * whilst the handle is open we really should + * get the same files back in the same order. + */ + + ret = smbc_lseekdir(dhandle, telldir_50); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (50) directory handle for '%s'", + dname)); + + DEBUG(0,("torture_libsmbclient_readdirplus_seek seek\n")); + + for (i = 51; i < 102; i++) { + const struct libsmb_file_info *entry = + smbc_readdirplus(dhandle); + torture_assert_goto( + tctx, + entry == direntries[i], + success, + done, + talloc_asprintf(tctx, + "after seek - failed to find " + "file %s - got %s\n", + direntries[i]->name, + entry->name)); + } + + /* Seek back to the start. */ + ret = smbc_lseekdir(dhandle, 0); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek directory handle to start for '%s'", + dname)); + + /* + * Mix getdents/readdir/readdirplus with lseek to ensure + * we get the same result. + */ + + /* Allocate the space for 20 entries. + * Tricky as we need to allocate 20 struct smbc_dirent's + space + * for the name lengths. + */ + getdentries_size = 20 * (sizeof(struct smbc_dirent) + + strlen("test_readdirplus_1000.txt") + 1); + + getdentries = (struct smbc_dirent *)talloc_array_size(tctx, + getdentries_size, + 1); + torture_assert_not_null_goto( + tctx, + getdentries, + success, + done, + "talloc fail"); + + ret = smbc_getdents(dhandle, getdentries, getdentries_size); + torture_assert_goto(tctx, + (ret != -1), + success, + done, + talloc_asprintf(tctx, + "smbd_getdents(1) for '%s' failed\n", + dname)); + + telldir_20 = smbc_telldir(dhandle); + torture_assert_goto( + tctx, + telldir_20 != (off_t)-1, + success, + done, + "telldir (20) failed\n"); + + /* Read another 20. */ + ret = smbc_getdents(dhandle, getdentries, getdentries_size); + torture_assert_goto(tctx, + (ret != -1), + success, + done, + talloc_asprintf(tctx, + "smbd_getdents(2) for '%s' failed\n", + dname)); + + /* Seek back to 20. */ + ret = smbc_lseekdir(dhandle, telldir_20); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (20) directory handle for '%s'", + dname)); + + /* Read with readdir. */ + dirent_20 = smbc_readdir(dhandle); + torture_assert_not_null_goto( + tctx, + dirent_20, + success, + done, + "smbc_readdir (20) failed\n"); + + /* Ensure the getdents and readdir names are the same. */ + ret = strcmp(dirent_20->name, getdentries[0].name); + torture_assert_goto( + tctx, + ret == 0, + success, + done, + talloc_asprintf(tctx, + "after seek (20) readdir name mismatch " + "file %s - got %s\n", + dirent_20->name, + getdentries[0].name)); + + /* Seek back to 20. */ + ret = smbc_lseekdir(dhandle, telldir_20); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (20) directory handle for '%s'", + dname)); + /* Read with readdirplus. */ + direntries_20 = smbc_readdirplus(dhandle); + torture_assert_not_null_goto( + tctx, + direntries_20, + success, + done, + "smbc_readdirplus (20) failed\n"); + + /* Ensure the readdirplus and readdir names are the same. */ + ret = strcmp(dirent_20->name, direntries_20->name); + torture_assert_goto( + tctx, + ret == 0, + success, + done, + talloc_asprintf(tctx, + "after seek (20) readdirplus name mismatch " + "file %s - got %s\n", + dirent_20->name, + direntries_20->name)); + + /* Seek back to 20. */ + ret = smbc_lseekdir(dhandle, telldir_20); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (20) directory handle for '%s'", + dname)); + + /* Read with readdirplus2. */ + direntriesplus_20 = smbc_readdirplus2(dhandle, &st2); + torture_assert_not_null_goto( + tctx, + direntriesplus_20, + success, + done, + "smbc_readdirplus2 (20) failed\n"); + + /* Ensure the readdirplus2 and readdirplus names are the same. */ + ret = strcmp(direntries_20->name, direntriesplus_20->name); + torture_assert_goto( + tctx, + ret == 0, + success, + done, + talloc_asprintf(tctx, + "after seek (20) readdirplus2 name mismatch " + "file %s - got %s\n", + dirent_20->name, + direntries_20->name)); + + /* Ensure doing stat gets the same data. */ + plus2_stat_path = talloc_asprintf(tctx, + "%s/%s", + dname, + direntriesplus_20->name); + torture_assert_not_null_goto( + tctx, + plus2_stat_path, + success, + done, + "talloc fail\n"); + + ret = smbc_stat(plus2_stat_path, &st); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to stat file '%s'", + plus2_stat_path)); + + torture_assert_int_equal(tctx, + st.st_ino, + st2.st_ino, + talloc_asprintf(tctx, + "file %s mismatched ino value " + "stat got %"PRIx64" readdirplus2 got %"PRIx64"" , + plus2_stat_path, + (uint64_t)st.st_ino, + (uint64_t)st2.st_ino)); + + torture_assert_int_equal(tctx, + st.st_dev, + st2.st_dev, + talloc_asprintf(tctx, + "file %s mismatched dev value " + "stat got %"PRIx64" readdirplus2 got %"PRIx64"" , + plus2_stat_path, + (uint64_t)st.st_dev, + (uint64_t)st2.st_dev)); + + ret = smbc_closedir(dhandle); + torture_assert_int_equal(tctx, + ret, + 0, + talloc_asprintf(tctx, + "failed to close directory handle for '%s'", + dname)); + + dhandle = -1; + success = true; + + done: + + /* Clean up. */ + if (dhandle != -1) { + smbc_closedir(dhandle); + } + for (i = 0; i < 100; i++) { + if (full_filename[i] != NULL) { + smbc_unlink(full_filename[i]); + } + } + if (dname != NULL) { + smbc_rmdir(dname); + } + + smbc_free_context(ctx, 1); + + return success; +} + +#ifndef SMBC_FILE_MODE +#define SMBC_FILE_MODE (S_IFREG | 0444) +#endif + +static bool torture_libsmbclient_readdirplus2(struct torture_context *tctx) +{ + SMBCCTX *ctx = NULL; + int dhandle = -1; + int fhandle = -1; + bool found = false; + bool success = false; + const char *filename = NULL; + struct stat st2 = {0}; + struct stat st = {0}; + int ret; + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server/share missing\n"); + } + + torture_assert_goto(tctx, torture_libsmbclient_init_context(tctx, &ctx), success, done, ""); + smbc_set_context(ctx); + + filename = talloc_asprintf(tctx, + "%s/test_readdirplus.txt", + smburl); + if (filename == NULL) { + torture_fail_goto(tctx, done, "talloc fail\n"); + } + + /* Ensure the file doesn't exist. */ + smbc_unlink(filename); + + /* Create it. */ + fhandle = smbc_creat(filename, 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + filename, + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + filename)); + + dhandle = smbc_opendir(smburl); + if (dhandle < 0) { + int saved_errno = errno; + smbc_unlink(filename); + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to obtain " + "directory handle for '%s' : %s", + smburl, + strerror(saved_errno))); + } + + /* readdirplus2 to ensure we see the new file. */ + for (;;) { + const struct libsmb_file_info *exstat = + smbc_readdirplus2(dhandle, &st2); + if (exstat == NULL) { + break; + } + + if (strcmp(exstat->name, "test_readdirplus.txt") == 0) { + found = true; + break; + } + } + + if (!found) { + smbc_unlink(filename); + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to find file '%s'", + filename)); + } + + /* Ensure mode is as expected. */ + /* + * New file gets SMBC_FILE_MODE plus + * archive bit -> S_IXUSR + * !READONLY -> S_IWUSR. + */ + torture_assert_int_equal_goto(tctx, + st2.st_mode, + SMBC_FILE_MODE|S_IXUSR|S_IWUSR, + success, + done, + talloc_asprintf(tctx, + "file %s st_mode should be 0%o, got 0%o'", + filename, + SMBC_FILE_MODE|S_IXUSR|S_IWUSR, + (unsigned int)st2.st_mode)); + + /* Ensure smbc_stat() gets the same data. */ + ret = smbc_stat(filename, &st); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to stat file '%s'", + filename)); + + torture_assert_int_equal_goto(tctx, + st2.st_ino, + st.st_ino, + success, + done, + talloc_asprintf(tctx, + "filename '%s' ino mismatch. " + "From smbc_readdirplus2 = %"PRIx64" " + "From smbc_stat = %"PRIx64"", + filename, + (uint64_t)st2.st_ino, + (uint64_t)st.st_ino)); + + + /* Remove it again. */ + smbc_unlink(filename); + ret = smbc_closedir(dhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close directory handle for '%s'", + filename)); + success = true; + + done: + smbc_free_context(ctx, 1); + return success; +} + +bool torture_libsmbclient_configuration(struct torture_context *tctx) +{ + SMBCCTX *ctx; + bool ok = true; + + ctx = smbc_new_context(); + torture_assert(tctx, ctx, "failed to get new context"); + torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); + + torture_comment(tctx, "Testing smbc_(set|get)Debug\n"); + smbc_setDebug(ctx, DEBUGLEVEL); + torture_assert_int_equal_goto(tctx, + smbc_getDebug(ctx), + DEBUGLEVEL, + ok, + done, + "failed to set DEBUGLEVEL"); + + torture_comment(tctx, "Testing smbc_(set|get)NetbiosName\n"); + smbc_setNetbiosName(ctx, discard_const("torture_netbios")); + torture_assert_str_equal_goto(tctx, + smbc_getNetbiosName(ctx), + "torture_netbios", + ok, + done, + "failed to set NetbiosName"); + + torture_comment(tctx, "Testing smbc_(set|get)Workgroup\n"); + smbc_setWorkgroup(ctx, discard_const("torture_workgroup")); + torture_assert_str_equal_goto(tctx, + smbc_getWorkgroup(ctx), + "torture_workgroup", + ok, + done, + "failed to set Workgroup"); + + torture_comment(tctx, "Testing smbc_(set|get)User\n"); + smbc_setUser(ctx, "torture_user"); + torture_assert_str_equal_goto(tctx, + smbc_getUser(ctx), + "torture_user", + ok, + done, + "failed to set User"); + + torture_comment(tctx, "Testing smbc_(set|get)Timeout\n"); + smbc_setTimeout(ctx, 12345); + torture_assert_int_equal_goto(tctx, + smbc_getTimeout(ctx), + 12345, + ok, + done, + "failed to set Timeout"); + +done: + smbc_free_context(ctx, 1); + + return ok; +} + +bool torture_libsmbclient_options(struct torture_context *tctx) +{ + SMBCCTX *ctx; + bool ok = true; + + ctx = smbc_new_context(); + torture_assert(tctx, ctx, "failed to get new context"); + torture_assert(tctx, smbc_init_context(ctx), "failed to init context"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionDebugToStderr\n"); + smbc_setOptionDebugToStderr(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionDebugToStderr(ctx), + ok, + done, + "failed to set OptionDebugToStderr"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionFullTimeNames\n"); + smbc_setOptionFullTimeNames(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionFullTimeNames(ctx), + ok, + done, + "failed to set OptionFullTimeNames"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionOpenShareMode\n"); + smbc_setOptionOpenShareMode(ctx, SMBC_SHAREMODE_DENY_ALL); + torture_assert_int_equal_goto(tctx, + smbc_getOptionOpenShareMode(ctx), + SMBC_SHAREMODE_DENY_ALL, + ok, + done, + "failed to set OptionOpenShareMode"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionUserData\n"); + smbc_setOptionUserData(ctx, (void *)discard_const("torture_user_data")); + torture_assert_str_equal_goto(tctx, + (const char*)smbc_getOptionUserData(ctx), + "torture_user_data", + ok, + done, + "failed to set OptionUserData"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionSmbEncryptionLevel\n"); + smbc_setOptionSmbEncryptionLevel(ctx, SMBC_ENCRYPTLEVEL_REQUEST); + torture_assert_int_equal_goto(tctx, + smbc_getOptionSmbEncryptionLevel(ctx), + SMBC_ENCRYPTLEVEL_REQUEST, + ok, + done, + "failed to set OptionSmbEncryptionLevel"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionCaseSensitive\n"); + smbc_setOptionCaseSensitive(ctx, false); + torture_assert_goto(tctx, + !smbc_getOptionCaseSensitive(ctx), + ok, + done, + "failed to set OptionCaseSensitive"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionBrowseMaxLmbCount\n"); + smbc_setOptionBrowseMaxLmbCount(ctx, 2); + torture_assert_int_equal_goto(tctx, + smbc_getOptionBrowseMaxLmbCount(ctx), + 2, + ok, + done, + "failed to set OptionBrowseMaxLmbCount"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionUrlEncodeReaddirEntries\n"); + smbc_setOptionUrlEncodeReaddirEntries(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionUrlEncodeReaddirEntries(ctx), + ok, + done, + "failed to set OptionUrlEncodeReaddirEntries"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionOneSharePerServer\n"); + smbc_setOptionOneSharePerServer(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionOneSharePerServer(ctx), + ok, + done, + "failed to set OptionOneSharePerServer"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionUseKerberos\n"); + smbc_setOptionUseKerberos(ctx, false); + torture_assert_goto(tctx, + !smbc_getOptionUseKerberos(ctx), + ok, + done, + "failed to set OptionUseKerberos"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionFallbackAfterKerberos\n"); + smbc_setOptionFallbackAfterKerberos(ctx, false); + torture_assert_goto(tctx, + !smbc_getOptionFallbackAfterKerberos(ctx), + ok, + done, + "failed to set OptionFallbackAfterKerberos"); + + torture_comment(tctx, + "Testing smbc_(set|get)OptionNoAutoAnonymousLogin\n"); + smbc_setOptionNoAutoAnonymousLogin(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionNoAutoAnonymousLogin(ctx), + ok, + done, + "failed to set OptionNoAutoAnonymousLogin"); + + torture_comment(tctx, "Testing smbc_(set|get)OptionUseCCache\n"); + smbc_setOptionUseCCache(ctx, true); + torture_assert_goto(tctx, + smbc_getOptionUseCCache(ctx), + ok, + done, + "failed to set OptionUseCCache"); + +done: + smbc_free_context(ctx, 1); + + return ok; +} + +static bool torture_libsmbclient_list_shares(struct torture_context *tctx) +{ + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + struct smbc_dirent *dirent = NULL; + SMBCCTX *ctx = NULL; + int dhandle = -1; + bool ipc_share_found = false; + bool ok = true; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server missing\n"); + } + + ok = torture_libsmbclient_init_context(tctx, &ctx); + torture_assert_goto(tctx, + ok, + ok, + out, + "Failed to init context"); + smbc_set_context(ctx); + + torture_comment(tctx, "Listing: %s\n", smburl); + dhandle = smbc_opendir(smburl); + torture_assert_int_not_equal_goto(tctx, + dhandle, + -1, + ok, + out, + "Failed to open smburl"); + + while((dirent = smbc_readdir(dhandle)) != NULL) { + torture_comment(tctx, "DIR: %s\n", dirent->name); + torture_assert_not_null_goto(tctx, + dirent->name, + ok, + out, + "Failed to read name"); + + if (strequal(dirent->name, "IPC$")) { + ipc_share_found = true; + } + } + + torture_assert_goto(tctx, + ipc_share_found, + ok, + out, + "Failed to list IPC$ share"); + +out: + smbc_closedir(dhandle); + return ok; +} + +static bool torture_libsmbclient_utimes(struct torture_context *tctx) +{ + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + SMBCCTX *ctx = NULL; + struct stat st; + int fhandle, ret; + struct timeval tbuf[2]; + bool ok; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server missing\n"); + } + + ok = torture_libsmbclient_init_context(tctx, &ctx); + torture_assert(tctx, ok, "Failed to init context"); + smbc_set_context(ctx); + + fhandle = smbc_open(smburl, O_RDWR|O_CREAT, 0644); + torture_assert_int_not_equal(tctx, fhandle, -1, "smbc_open failed"); + + ret = smbc_fstat(fhandle, &st); + torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed"); + + tbuf[0] = convert_timespec_to_timeval(get_atimespec(&st)); + tbuf[1] = convert_timespec_to_timeval(get_mtimespec(&st)); + + tbuf[1] = timeval_add(&tbuf[1], 0, 100000); /* 100 msec */ + + ret = smbc_utimes(smburl, tbuf); + torture_assert_int_not_equal(tctx, ret, -1, "smbc_utimes failed"); + + ret = smbc_fstat(fhandle, &st); + torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed"); + + torture_assert_int_equal( + tctx, + get_mtimensec(&st) / 1000, + tbuf[1].tv_usec, + "smbc_utimes did not update msec"); + + smbc_close(fhandle); + smbc_unlink(smburl); + return true; +} + +static bool torture_libsmbclient_noanon_list(struct torture_context *tctx) +{ + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + struct smbc_dirent *dirent = NULL; + SMBCCTX *ctx = NULL; + int dhandle = -1; + bool ok = true; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server missing\n"); + } + + ok = torture_libsmbclient_init_context(tctx, &ctx); + torture_assert_goto(tctx, + ok, + ok, + out, + "Failed to init context"); + torture_comment(tctx, + "Testing smbc_setOptionNoAutoAnonymousLogin\n"); + smbc_setOptionNoAutoAnonymousLogin(ctx, true); + smbc_set_context(ctx); + + torture_comment(tctx, "Listing: %s\n", smburl); + dhandle = smbc_opendir(smburl); + torture_assert_int_not_equal_goto(tctx, + dhandle, + -1, + ok, + out, + "Failed to open smburl"); + + while((dirent = smbc_readdir(dhandle)) != NULL) { + torture_comment(tctx, "DIR: %s\n", dirent->name); + torture_assert_not_null_goto(tctx, + dirent->name, + ok, + out, + "Failed to read name"); + } + +out: + smbc_closedir(dhandle); + return ok; +} + +static bool torture_libsmbclient_rename(struct torture_context *tctx) +{ + SMBCCTX *ctx = NULL; + int fhandle = -1; + bool success = false; + const char *filename_src = NULL; + const char *filename_dst = NULL; + int ret; + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server/share missing\n"); + } + + torture_assert_goto(tctx, + torture_libsmbclient_init_context(tctx, &ctx), + success, + done, + ""); + + smbc_set_context(ctx); + + filename_src = talloc_asprintf(tctx, + "%s/src", + smburl); + if (filename_src == NULL) { + torture_fail_goto(tctx, done, "talloc fail\n"); + } + + filename_dst = talloc_asprintf(tctx, + "%s/dst", + smburl); + if (filename_dst == NULL) { + torture_fail_goto(tctx, done, "talloc fail\n"); + } + + /* Ensure the files don't exist. */ + smbc_unlink(filename_src); + smbc_unlink(filename_dst); + + /* Create them. */ + fhandle = smbc_creat(filename_src, 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + filename_src, + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + filename_src)); + + fhandle = smbc_creat(filename_dst, 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + filename_dst, + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + filename_dst)); + + ret = smbc_rename(filename_src, filename_dst); + + /* + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14938 + * gives ret == -1, but errno = 0 for overwrite renames + * over SMB2. + */ + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "smbc_rename '%s' -> '%s' failed with %s\n", + filename_src, + filename_dst, + strerror(errno))); + + /* Remove them again. */ + smbc_unlink(filename_src); + smbc_unlink(filename_dst); + success = true; + + done: + smbc_free_context(ctx, 1); + return success; +} + +static bool torture_libsmbclient_getatr(struct torture_context *tctx) +{ + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + SMBCCTX *ctx = NULL; + char *getatr_name = NULL; + struct stat st = {0}; + bool ok; + int ret = 0; + int err = 0; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server missing\n"); + } + + ok = torture_libsmbclient_init_context(tctx, &ctx); + torture_assert(tctx, ok, "Failed to init context"); + smbc_set_context(ctx); + + getatr_name = talloc_asprintf(tctx, + "%s/noexist", + smburl); + if (getatr_name == NULL) { + torture_result(tctx, + TORTURE_FAIL, + __location__": %s", + "talloc fail\n"); + return false; + } + /* Ensure the file doesn't exist. */ + smbc_unlink(getatr_name); + /* + * smbc_stat() internally uses SMBC_getatr(). + * Make sure doing getatr on a non-existent file gives + * an error of -1, errno = ENOENT. + */ + + ret = smbc_stat(getatr_name, &st); + if (ret == -1) { + err = errno; + } + torture_assert_int_equal(tctx, + ret, + -1, + talloc_asprintf(tctx, + "smbc_stat on '%s' should " + "get -1, got %d\n", + getatr_name, + ret)); + torture_assert_int_equal(tctx, + err, + ENOENT, + talloc_asprintf(tctx, + "smbc_stat on '%s' should " + "get errno = ENOENT, got %s\n", + getatr_name, + strerror(err))); + return true; +} + +static bool torture_libsmbclient_getxattr(struct torture_context *tctx) +{ + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + int fhandle = -1; + SMBCCTX *ctx = NULL; + char *getxattr_name = NULL; + char value[4096]; + bool ok = false; + int ret = -1; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server missing\n"); + } + + ok = torture_libsmbclient_init_context(tctx, &ctx); + torture_assert(tctx, ok, "Failed to init context"); + smbc_set_context(ctx); + + getxattr_name = talloc_asprintf(tctx, + "%s/getxattr", + smburl); + if (getxattr_name == NULL) { + torture_result(tctx, + TORTURE_FAIL, + __location__": %s", + "talloc fail\n"); + return false; + } + /* Ensure the file doesn't exist. */ + smbc_unlink(getxattr_name); + + /* Create testfile. */ + fhandle = smbc_creat(getxattr_name, 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + getxattr_name, + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + ok, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + getxattr_name)); + + /* + * Ensure getting a non-existent attribute returns -1. + */ + ret = smbc_getxattr(getxattr_name, "foobar", value, sizeof(value)); + torture_assert_int_equal_goto(tctx, + ret, + -1, + ok, + done, + talloc_asprintf(tctx, + "smbc_getxattr(foobar) on '%s' should " + "get -1, got %d\n", + getxattr_name, + ret)); + + /* + * Ensure getting a valid attribute computes its size. + */ + ret = smbc_getxattr(getxattr_name, "system.*", NULL, 0); + torture_assert_goto(tctx, + ret >= 0, + ok, + done, + talloc_asprintf(tctx, + "smbc_getxattr(foobar, NULL) on '%s' should " + "get >=0, got %d\n", + getxattr_name, + ret)); + + /* + * Ensure getting a valid attribute returns its size. + */ + ret = smbc_getxattr(getxattr_name, "system.*", value, sizeof(value)); + torture_assert_goto(tctx, + ret >= 0, + ok, + done, + talloc_asprintf(tctx, + "smbc_getxattr(foobar, value) on '%s' should " + "get >=0, got %d\n", + getxattr_name, + ret)); + + ok = true; + + done: + + smbc_unlink(getxattr_name); + smbc_free_context(ctx, 1); + return ok; +} + +NTSTATUS torture_libsmbclient_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(ctx, "libsmbclient"); + + torture_suite_add_simple_test(suite, "version", torture_libsmbclient_version); + torture_suite_add_simple_test(suite, "initialize", torture_libsmbclient_initialize); + torture_suite_add_simple_test(suite, "configuration", torture_libsmbclient_configuration); + torture_suite_add_simple_test(suite, "setConfiguration", torture_libsmbclient_setConfiguration); + torture_suite_add_simple_test(suite, "options", torture_libsmbclient_options); + torture_suite_add_simple_test(suite, "opendir", torture_libsmbclient_opendir); + torture_suite_add_simple_test(suite, "list_shares", torture_libsmbclient_list_shares); + torture_suite_add_simple_test(suite, "readdirplus", + torture_libsmbclient_readdirplus); + torture_suite_add_simple_test(suite, "readdirplus_seek", + torture_libsmbclient_readdirplus_seek); + torture_suite_add_simple_test(suite, "readdirplus2", + torture_libsmbclient_readdirplus2); + torture_suite_add_simple_test( + suite, "utimes", torture_libsmbclient_utimes); + torture_suite_add_simple_test( + suite, "noanon_list", torture_libsmbclient_noanon_list); + torture_suite_add_simple_test(suite, + "rename", + torture_libsmbclient_rename); + torture_suite_add_simple_test(suite, "getatr", + torture_libsmbclient_getatr); + torture_suite_add_simple_test(suite, "getxattr", + torture_libsmbclient_getxattr); + + suite->description = talloc_strdup(suite, "libsmbclient interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/libsmbclient/wscript_build b/source4/torture/libsmbclient/wscript_build new file mode 100644 index 0000000..61d819b --- /dev/null +++ b/source4/torture/libsmbclient/wscript_build @@ -0,0 +1,14 @@ +#!/usr/bin/env python + + +bld.SAMBA_MODULE('TORTURE_LIBSMBCLIENT', + source='libsmbclient.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_libsmbclient_init', + deps='smbclient CMDLINE_S4', + internal_module=True + ) + + + diff --git a/source4/torture/local/dbspeed.c b/source4/torture/local/dbspeed.c new file mode 100644 index 0000000..9452d6a --- /dev/null +++ b/source4/torture/local/dbspeed.c @@ -0,0 +1,268 @@ +/* + Unix SMB/CIFS implementation. + + local test for tdb/ldb speed + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include +#include +#include +#include "ldb_wrap.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include "torture/smbtorture.h" +#include "torture/local/proto.h" +#include "param/param.h" + +float tdb_speed; + +static bool tdb_add_record(struct tdb_wrap *tdbw, const char *p1, + const char *p2, int i) +{ + TDB_DATA key, data; + int ret; + + key.dptr = (uint8_t *)talloc_asprintf(tdbw, "%s%u", p1, i); + key.dsize = strlen((char *)key.dptr)+1; + data.dptr = (uint8_t *)talloc_asprintf(tdbw, "%s%u", p2, i+10000); + data.dsize = strlen((char *)data.dptr)+1; + + ret = tdb_store(tdbw->tdb, key, data, TDB_INSERT); + + talloc_free(key.dptr); + talloc_free(data.dptr); + return ret == 0; +} + +/* + test tdb speed +*/ +static bool test_tdb_speed(struct torture_context *torture, const void *_data) +{ + struct timeval tv; + struct tdb_wrap *tdbw; + int timelimit = torture_setting_int(torture, "timelimit", 10); + int i, count; + TALLOC_CTX *tmp_ctx = talloc_new(torture); + + unlink("test.tdb"); + + torture_comment(torture, "Testing tdb speed for sidmap\n"); + + tdbw = tdb_wrap_open(tmp_ctx, "test.tdb", 10000, + lpcfg_tdb_flags(torture->lp_ctx, 0), + O_RDWR|O_CREAT|O_TRUNC, 0600); + if (!tdbw) { + torture_result(torture, TORTURE_FAIL, "Failed to open test.tdb"); + goto failed; + } + + torture_comment(torture, "Adding %d SID records\n", torture_entries); + + for (i=0;itdb, key); + talloc_free(key.dptr); + if (data.dptr == NULL) { + torture_result(torture, TORTURE_FAIL, "Failed to find SID %d!", i); + goto failed; + } + free(data.dptr); + key.dptr = (uint8_t *)talloc_asprintf(tmp_ctx, "UID %u", i); + key.dsize = strlen((char *)key.dptr)+1; + data = tdb_fetch(tdbw->tdb, key); + talloc_free(key.dptr); + if (data.dptr == NULL) { + torture_result(torture, TORTURE_FAIL, "Failed to find UID %d!", i); + goto failed; + } + free(data.dptr); + } + + tdb_speed = count/timeval_elapsed(&tv); + torture_comment(torture, "tdb speed %.2f ops/sec\n", tdb_speed); + + talloc_free(tmp_ctx); + unlink("test.tdb"); + return true; + +failed: + talloc_free(tmp_ctx); + unlink("test.tdb"); + return false; +} + + +static bool ldb_add_record(struct ldb_context *ldb, unsigned rid) +{ + struct ldb_message *msg; + int ret; + + msg = ldb_msg_new(ldb); + if (msg == NULL) { + return false; + } + + msg->dn = ldb_dn_new_fmt(msg, ldb, "SID=S-1-5-21-53173311-3623041448-2049097239-%u", rid); + if (msg->dn == NULL) { + talloc_free(msg); + return false; + } + + ret = ldb_msg_add_fmt(msg, "UID", "%u", rid); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return false; + } + + ret = ldb_add(ldb, msg); + + talloc_free(msg); + + return ret == LDB_SUCCESS; +} + + +/* + test ldb speed +*/ +static bool test_ldb_speed(struct torture_context *torture, const void *_data) +{ + struct timeval tv; + struct ldb_context *ldb; + int timelimit = torture_setting_int(torture, "timelimit", 10); + int i, count; + TALLOC_CTX *tmp_ctx = talloc_new(torture); + struct ldb_ldif *ldif; + const char *init_ldif = "dn: @INDEXLIST\n" \ + "@IDXATTR: UID\n"; + float ldb_speed; + + unlink("./test.ldb"); + + torture_comment(torture, "Testing ldb speed for sidmap\n"); + + ldb = ldb_wrap_connect(tmp_ctx, torture->ev, torture->lp_ctx, "tdb://test.ldb", + NULL, NULL, LDB_FLG_NOSYNC); + if (!ldb) { + torture_result(torture, TORTURE_FAIL, "Failed to open test.ldb"); + goto failed; + } + + /* add an index */ + ldif = ldb_ldif_read_string(ldb, &init_ldif); + if (ldif == NULL) { + torture_result(torture, TORTURE_FAIL, "Didn't get LDIF data!"); + goto failed; + } + if (ldb_add(ldb, ldif->msg) != LDB_SUCCESS) { + torture_result(torture, TORTURE_FAIL, "Couldn't apply LDIF data!"); + talloc_free(ldif); + goto failed; + } + talloc_free(ldif); + + torture_comment(torture, "Adding %d SID records\n", torture_entries); + + for (i=0;i 100) { + torture_result(torture, TORTURE_FAIL, "memory leak in ldb add"); + goto failed; + } + + torture_comment(torture, "Testing for %d seconds\n", timelimit); + + tv = timeval_current(); + + for (count=0;timeval_elapsed(&tv) < timelimit;count++) { + struct ldb_dn *dn; + struct ldb_result *res; + + i = random() % torture_entries; + dn = ldb_dn_new_fmt(tmp_ctx, ldb, "SID=S-1-5-21-53173311-3623041448-2049097239-%u", i); + if (ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL) != LDB_SUCCESS || res->count != 1) { + torture_result(torture, TORTURE_FAIL, "Failed to find SID %d!", i); + goto failed; + } + talloc_free(res); + talloc_free(dn); + if (ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "(UID=%u)", i) != LDB_SUCCESS || res->count != 1) { + torture_result(torture, TORTURE_FAIL, "Failed to find UID %d!", i); + goto failed; + } + talloc_free(res); + } + + if (talloc_total_blocks(tmp_ctx) > 100) { + torture_result(torture, TORTURE_FAIL, "memory leak in ldb search"); + goto failed; + } + + ldb_speed = count/timeval_elapsed(&tv); + torture_comment(torture, "ldb speed %.2f ops/sec\n", ldb_speed); + + torture_comment(torture, "ldb/tdb speed ratio is %.2f%%\n", (100*ldb_speed/tdb_speed)); + + talloc_free(tmp_ctx); + unlink("./test.ldb"); + return true; + +failed: + talloc_free(tmp_ctx); + unlink("./test.ldb"); + return false; +} + +struct torture_suite *torture_local_dbspeed(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *s = torture_suite_create(mem_ctx, "dbspeed"); + torture_suite_add_simple_tcase_const(s, "tdb_speed", test_tdb_speed, + NULL); + torture_suite_add_simple_tcase_const(s, "ldb_speed", test_ldb_speed, + NULL); + return s; +} diff --git a/source4/torture/local/fsrvp_state.c b/source4/torture/local/fsrvp_state.c new file mode 100644 index 0000000..9b63ec1 --- /dev/null +++ b/source4/torture/local/fsrvp_state.c @@ -0,0 +1,492 @@ +/* + Test suite for FSRVP server state + + Copyright (C) David Disseldorp 2012-2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include + +#include "librpc/gen_ndr/security.h" +#include "lib/param/param.h" +#include "lib/util/dlinklist.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_fsrvp.h" +#include "librpc/gen_ndr/ndr_fsrvp_c.h" +#include "source3/rpc_server/fss/srv_fss_private.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_fsrvp_state_empty(struct torture_context *tctx) +{ + NTSTATUS status; + struct fss_global fss_global; + struct stat sbuf; + char db_dir[] = "fsrvp_torture_XXXXXX"; + char *db_path = talloc_asprintf(NULL, "%s/%s", + mkdtemp(db_dir), FSS_DB_NAME); + + memset(&fss_global, 0, sizeof(fss_global)); + fss_global.mem_ctx = talloc_new(NULL); + fss_global.db_path = db_path; + + status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets, + fss_global.sc_sets_count, fss_global.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to store empty fss state"); + + torture_assert_int_equal(tctx, stat(fss_global.db_path, &sbuf), 0, + "failed to stat fss state tdb"); + talloc_free(fss_global.mem_ctx); + + memset(&fss_global, 0, sizeof(fss_global)); + fss_global.mem_ctx = talloc_new(NULL); + fss_global.db_path = db_path; + + status = fss_state_retrieve(fss_global.mem_ctx, &fss_global.sc_sets, + &fss_global.sc_sets_count, + fss_global.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to retrieve empty fss state"); + torture_assert_int_equal(tctx, fss_global.sc_sets_count, 0, + "sc_sets_count set when it should be zero"); + talloc_free(fss_global.mem_ctx); + unlink(db_path); + rmdir(db_dir); + talloc_free(db_path); + + return true; +} + +static bool test_fsrvp_state_sc_set(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct fss_sc_set **sc_set_out) +{ + struct fss_sc_set *sc_set; + + sc_set = talloc_zero(mem_ctx, struct fss_sc_set); + sc_set->id = GUID_random(); + sc_set->id_str = GUID_string(sc_set, &sc_set->id); + sc_set->state = FSS_SC_COMMITED; + sc_set->context = FSRVP_CTX_FILE_SHARE_BACKUP; + *sc_set_out = sc_set; + + return true; +} + +static bool test_fsrvp_state_sc(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct fss_sc **sc_out) +{ + struct fss_sc *sc; + + sc = talloc_zero(mem_ctx, struct fss_sc); + sc->id = GUID_random(); + sc->id_str = GUID_string(sc, &sc->id); + sc->volume_name = talloc_strdup(sc, "/this/is/a/path"); + /* keep snap path NULL, i.e. not yet committed */ + sc->create_ts = time(NULL); + *sc_out = sc; + + return true; +} + +static bool test_fsrvp_state_smap(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *base_share_name, + const char *sc_share_name, + struct fss_sc_smap **smap_out) +{ + struct fss_sc_smap *smap; + + smap = talloc_zero(mem_ctx, struct fss_sc_smap); + smap->share_name = talloc_strdup(mem_ctx, base_share_name); + smap->sc_share_name = talloc_strdup(mem_ctx, sc_share_name); + smap->sc_share_comment = talloc_strdup(mem_ctx, "test sc share comment"); + smap->is_exposed = false; + *smap_out = smap; + + return true; +} + +static bool test_fsrvp_state_smap_compare(struct torture_context *tctx, + struct fss_sc_smap *smap_1, + struct fss_sc_smap *smap_2) +{ + /* already confirmed by caller */ + torture_assert_str_equal(tctx, smap_1->sc_share_name, + smap_2->sc_share_name, + "smap sc share name strings differ"); + + torture_assert_str_equal(tctx, smap_1->share_name, + smap_2->share_name, + "smap share name strings differ"); + + torture_assert_str_equal(tctx, smap_1->sc_share_comment, + smap_2->sc_share_comment, + "smap sc share comment strings differ"); + + torture_assert(tctx, (smap_1->is_exposed == smap_2->is_exposed), + "smap exposure settings differ"); + + return true; +} + +static bool test_fsrvp_state_sc_compare(struct torture_context *tctx, + struct fss_sc *sc_1, + struct fss_sc *sc_2) +{ + struct fss_sc_smap *smap_1; + struct fss_sc_smap *smap_2; + bool ok; + + /* should have already been confirmed by the caller */ + torture_assert(tctx, GUID_equal(&sc_1->id, &sc_2->id), + "sc guids differ"); + + torture_assert_str_equal(tctx, sc_1->volume_name, sc_2->volume_name, + "sc volume_name strings differ"); + + /* may be null, assert_str_eq handles null ptrs safely */ + torture_assert_str_equal(tctx, sc_1->sc_path, sc_2->sc_path, + "sc path strings differ"); + + torture_assert(tctx, difftime(sc_1->create_ts, sc_2->create_ts) == 0, + "sc create timestamps differ"); + + torture_assert_int_equal(tctx, sc_1->smaps_count, sc_2->smaps_count, + "sc smaps counts differ"); + + for (smap_1 = sc_1->smaps; smap_1; smap_1 = smap_1->next) { + bool matched = false; + for (smap_2 = sc_2->smaps; smap_2; smap_2 = smap_2->next) { + if (strcmp(smap_1->sc_share_name, + smap_2->sc_share_name) == 0) { + matched = true; + ok = test_fsrvp_state_smap_compare(tctx, + smap_1, + smap_2); + torture_assert(tctx, ok, ""); + break; + } + } + torture_assert(tctx, matched, "no match for smap"); + } + + return true; +} + +static bool test_fsrvp_state_sc_set_compare(struct torture_context *tctx, + struct fss_sc_set *sc_set_1, + struct fss_sc_set *sc_set_2) +{ + struct fss_sc *sc_1; + struct fss_sc *sc_2; + bool ok; + + /* should have already been confirmed by the caller */ + torture_assert(tctx, GUID_equal(&sc_set_1->id, &sc_set_2->id), + "sc_set guids differ"); + + torture_assert_str_equal(tctx, sc_set_1->id_str, sc_set_2->id_str, + "sc_set guid strings differ"); + + torture_assert_int_equal(tctx, sc_set_1->state, sc_set_2->state, + "sc_set state enums differ"); + + torture_assert_int_equal(tctx, sc_set_1->context, sc_set_2->context, + "sc_set contexts differ"); + + torture_assert_int_equal(tctx, sc_set_1->scs_count, sc_set_2->scs_count, + "sc_set sc counts differ"); + + for (sc_1 = sc_set_1->scs; sc_1; sc_1 = sc_1->next) { + bool matched = false; + for (sc_2 = sc_set_2->scs; sc_2; sc_2 = sc_2->next) { + if (GUID_equal(&sc_1->id, &sc_2->id)) { + matched = true; + ok = test_fsrvp_state_sc_compare(tctx, sc_1, + sc_2); + torture_assert(tctx, ok, ""); + break; + } + } + torture_assert(tctx, matched, "no match for sc"); + } + return true; +} + +static bool test_fsrvp_state_compare(struct torture_context *tctx, + struct fss_global *fss_1, + struct fss_global *fss_2) +{ + struct fss_sc_set *sc_set_1; + struct fss_sc_set *sc_set_2; + bool ok; + + torture_assert_int_equal(tctx, fss_1->sc_sets_count, + fss_2->sc_sets_count, + "sc_sets_count differ"); + + for (sc_set_1 = fss_1->sc_sets; sc_set_1; sc_set_1 = sc_set_1->next) { + bool matched = false; + for (sc_set_2 = fss_2->sc_sets; + sc_set_2; + sc_set_2 = sc_set_2->next) { + if (GUID_equal(&sc_set_1->id, &sc_set_2->id)) { + matched = true; + ok = test_fsrvp_state_sc_set_compare(tctx, + sc_set_1, + sc_set_2); + torture_assert(tctx, ok, ""); + break; + } + } + torture_assert(tctx, matched, "no match for sc_set"); + } + + return true; +} + +/* + * test a simple hierarchy of: + * + * | + * sc_set + * | + * sc + * \ + * smap + */ +static bool test_fsrvp_state_single(struct torture_context *tctx) +{ + NTSTATUS status; + bool ok; + struct fss_global fss_gs; + struct fss_global fss_gr; + struct fss_sc_set *sc_set; + struct fss_sc *sc; + struct fss_sc_smap *smap; + char db_dir[] = "fsrvp_torture_XXXXXX"; + char *db_path = talloc_asprintf(NULL, "%s/%s", + mkdtemp(db_dir), FSS_DB_NAME); + + memset(&fss_gs, 0, sizeof(fss_gs)); + fss_gs.mem_ctx = talloc_new(NULL); + fss_gs.db_path = db_path; + + ok = test_fsrvp_state_sc_set(tctx, fss_gs.mem_ctx, &sc_set); + torture_assert(tctx, ok, "failed to create sc set"); + + /* use parent as mem ctx */ + ok = test_fsrvp_state_sc(tctx, sc_set, &sc); + torture_assert(tctx, ok, "failed to create sc"); + + ok = test_fsrvp_state_smap(tctx, sc, "base_share", "sc_share", &smap); + torture_assert(tctx, ok, "failed to create smap"); + + DLIST_ADD_END(fss_gs.sc_sets, sc_set); + fss_gs.sc_sets_count++; + DLIST_ADD_END(sc_set->scs, sc); + sc_set->scs_count++; + sc->sc_set = sc_set; + DLIST_ADD_END(sc->smaps, smap); + sc->smaps_count++; + + status = fss_state_store(fss_gs.mem_ctx, fss_gs.sc_sets, + fss_gs.sc_sets_count, fss_gs.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to store fss state"); + + memset(&fss_gr, 0, sizeof(fss_gr)); + fss_gr.mem_ctx = talloc_new(NULL); + fss_gr.db_path = db_path; + + status = fss_state_retrieve(fss_gr.mem_ctx, &fss_gr.sc_sets, + &fss_gr.sc_sets_count, fss_gr.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to retrieve fss state"); + + ok = test_fsrvp_state_compare(tctx, &fss_gs, &fss_gr); + torture_assert(tctx, ok, + "stored and retrieved state comparison failed"); + + talloc_free(fss_gs.mem_ctx); + talloc_free(fss_gr.mem_ctx); + unlink(db_path); + rmdir(db_dir); + talloc_free(db_path); + + return true; +} + +/* + * test a complex hierarchy of: + * + * /\ + * / \ + * sc_set_a sc_set_b + * / \ + * sc_aa sc_ab + * | | \ + * smap_aaa | \ + * | \ + * smap_aba smap_abb + */ +static bool test_fsrvp_state_multi(struct torture_context *tctx) +{ + NTSTATUS status; + bool ok; + struct fss_global fss_gs; + struct fss_global fss_gr; + struct fss_sc_set *sc_set_a; + struct fss_sc_set *sc_set_b; + struct fss_sc *sc_aa; + struct fss_sc *sc_ab; + struct fss_sc_smap *smap_aaa; + struct fss_sc_smap *smap_aba; + struct fss_sc_smap *smap_abb; + char db_dir[] = "fsrvp_torture_XXXXXX"; + char *db_path = talloc_asprintf(NULL, "%s/%s", + mkdtemp(db_dir), FSS_DB_NAME); + + memset(&fss_gs, 0, sizeof(fss_gs)); + fss_gs.mem_ctx = talloc_new(NULL); + fss_gs.db_path = db_path; + + ok = test_fsrvp_state_sc_set(tctx, fss_gs.mem_ctx, &sc_set_a); + torture_assert(tctx, ok, "failed to create sc set"); + + ok = test_fsrvp_state_sc_set(tctx, fss_gs.mem_ctx, &sc_set_b); + torture_assert(tctx, ok, "failed to create sc set"); + + /* use parent as mem ctx */ + ok = test_fsrvp_state_sc(tctx, sc_set_a, &sc_aa); + torture_assert(tctx, ok, "failed to create sc"); + + ok = test_fsrvp_state_sc(tctx, sc_set_a, &sc_ab); + torture_assert(tctx, ok, "failed to create sc"); + + ok = test_fsrvp_state_smap(tctx, sc_ab, "share_aa", "sc_share_aaa", + &smap_aaa); + torture_assert(tctx, ok, "failed to create smap"); + + ok = test_fsrvp_state_smap(tctx, sc_ab, "share_ab", "sc_share_aba", + &smap_aba); + torture_assert(tctx, ok, "failed to create smap"); + + ok = test_fsrvp_state_smap(tctx, sc_ab, "share_ab", "sc_share_abb", + &smap_abb); + torture_assert(tctx, ok, "failed to create smap"); + + DLIST_ADD_END(fss_gs.sc_sets, sc_set_a); + fss_gs.sc_sets_count++; + DLIST_ADD_END(fss_gs.sc_sets, sc_set_b); + fss_gs.sc_sets_count++; + + DLIST_ADD_END(sc_set_a->scs, sc_aa); + sc_set_a->scs_count++; + sc_aa->sc_set = sc_set_a; + DLIST_ADD_END(sc_set_a->scs, sc_ab); + sc_set_a->scs_count++; + sc_ab->sc_set = sc_set_a; + + DLIST_ADD_END(sc_aa->smaps, smap_aaa); + sc_aa->smaps_count++; + DLIST_ADD_END(sc_ab->smaps, smap_aba); + sc_ab->smaps_count++; + DLIST_ADD_END(sc_ab->smaps, smap_abb); + sc_ab->smaps_count++; + + status = fss_state_store(fss_gs.mem_ctx, fss_gs.sc_sets, + fss_gs.sc_sets_count, fss_gs.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to store fss state"); + + memset(&fss_gr, 0, sizeof(fss_gr)); + fss_gr.mem_ctx = talloc_new(NULL); + fss_gr.db_path = db_path; + status = fss_state_retrieve(fss_gr.mem_ctx, &fss_gr.sc_sets, + &fss_gr.sc_sets_count, fss_gr.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to retrieve fss state"); + + ok = test_fsrvp_state_compare(tctx, &fss_gs, &fss_gr); + torture_assert(tctx, ok, + "stored and retrieved state comparison failed"); + + talloc_free(fss_gs.mem_ctx); + talloc_free(fss_gr.mem_ctx); + unlink(db_path); + rmdir(db_dir); + talloc_free(db_path); + + return true; +} + +static bool test_fsrvp_state_none(struct torture_context *tctx) +{ + NTSTATUS status; + struct fss_global fss_global; + char db_dir[] = "fsrvp_torture_XXXXXX"; + char *db_path = talloc_asprintf(NULL, "%s/%s", + mkdtemp(db_dir), FSS_DB_NAME); + + memset(&fss_global, 0, sizeof(fss_global)); + fss_global.mem_ctx = talloc_new(NULL); + fss_global.db_path = db_path; + + status = fss_state_retrieve(fss_global.mem_ctx, &fss_global.sc_sets, + &fss_global.sc_sets_count, + fss_global.db_path); + torture_assert_ntstatus_ok(tctx, status, + "failed to retrieve fss state"); + torture_assert_int_equal(tctx, fss_global.sc_sets_count, 0, + "sc_sets_count set when it should be zero"); + talloc_free(fss_global.mem_ctx); + unlink(db_path); + rmdir(db_dir); + talloc_free(db_path); + + return true; +} + +struct torture_suite *torture_local_fsrvp(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "fsrvp_state"); + + /* dbwrap uses talloc_tos(), hence we need a stackframe :( */ + talloc_stackframe(); + + torture_suite_add_simple_test(suite, + "state_empty", + test_fsrvp_state_empty); + + torture_suite_add_simple_test(suite, + "state_single", + test_fsrvp_state_single); + + torture_suite_add_simple_test(suite, + "state_multi", + test_fsrvp_state_multi); + + torture_suite_add_simple_test(suite, + "state_none", + test_fsrvp_state_none); + + return suite; +} diff --git a/source4/torture/local/local.c b/source4/torture/local/local.c new file mode 100644 index 0000000..95417ef --- /dev/null +++ b/source4/torture/local/local.c @@ -0,0 +1,105 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/local/proto.h" +#include "torture/ndr/proto.h" +#include "torture/auth/proto.h" +#include "../lib/crypto/test_proto.h" +#include "lib/registry/tests/proto.h" +#include "lib/replace/replace-testsuite.h" + +/* ignore me */ static struct torture_suite * + (*suite_generators[]) (TALLOC_CTX *mem_ctx) = +{ + torture_local_binding_string, + torture_ntlmssp, + torture_smbencrypt, + torture_local_messaging, + torture_local_irpc, + torture_local_util_strlist, + torture_local_util_file, + torture_local_util_str, + torture_local_util_time, + torture_local_util_data_blob, + torture_local_util_binsearch, + torture_local_util_asn1, + torture_local_util_anonymous_shared, + torture_local_util_strv, + torture_local_util_strv_util, + torture_local_util, + torture_local_idtree, + torture_local_dlinklist, + torture_local_genrand, + torture_local_iconv, + torture_local_socket, + torture_pac, + torture_local_resolve, + torture_local_ndr, + torture_local_tdr, + torture_local_share, + torture_local_loadparm, + torture_local_charset, + torture_local_convert_string_handle, + torture_local_convert_string, + torture_local_string_case_handle, + torture_local_string_case, + torture_local_util_unistr, + torture_local_event, + torture_local_tevent_req, + torture_local_torture, + torture_local_dbspeed, + torture_ldb, + torture_dsdb_dn, + torture_dsdb_syntax, + torture_registry, + torture_local_verif_trailer, + torture_local_nss, + torture_local_fsrvp, + torture_local_util_str_escape, + torture_local_tfork, + torture_local_mdspkt, + torture_local_smbtorture, + NULL +}; + +NTSTATUS torture_local_init(TALLOC_CTX *ctx) +{ + int i; + struct torture_suite *suite = torture_suite_create( + ctx, "local"); + + torture_suite_add_simple_test(suite, "talloc", torture_local_talloc); + torture_suite_add_simple_test(suite, "replace", torture_local_replace); + + torture_suite_add_simple_test(suite, + "crypto.md4", torture_local_crypto_md4); + + for (i = 0; suite_generators[i]; i++) + torture_suite_add_suite(suite, + suite_generators[i](ctx)); + + suite->description = talloc_strdup(suite, + "Local, Samba-specific tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/local/mdspkt.c b/source4/torture/local/mdspkt.c new file mode 100644 index 0000000..dd9c391 --- /dev/null +++ b/source4/torture/local/mdspkt.c @@ -0,0 +1,104 @@ +/* + * Tests for mdssvc packets (un)marshalling + * + * Copyright Ralph Boehme 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "replace.h" +#include +#include "libcli/util/ntstatus.h" +#include "lib/util/samba_util.h" +#include "lib/torture/torture.h" +#include "lib/util/data_blob.h" +#include "torture/local/proto.h" +#include "mdssvc/marshalling.h" + +static const unsigned char mdspkt_empty_cnid_fm[] = { + 0x34, 0x33, 0x32, 0x31, 0x33, 0x30, 0x64, 0x6d, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x87, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const char *mdspkt_empty_cnid_fm_dump = +"DALLOC_CTX(#1): {\n" +" sl_array_t(#3): {\n" +" uint64_t: 0x0023\n" +" CNIDs: unkn1: 0x0, unkn2: 0x0\n" +" DALLOC_CTX(#0): {\n" +" }\n" +" sl_filemeta_t(#0): {\n" +" }\n" +" }\n" +"}\n"; + +static bool test_mdspkt_empty_cnid_fm(struct torture_context *tctx) +{ + DALLOC_CTX *d = NULL; + sl_cnids_t *cnids = NULL; + char *dstr = NULL; + size_t ncnids; + bool ret = true; + + d = dalloc_new(tctx); + torture_assert_not_null_goto(tctx, d, ret, done, + "dalloc_new failed\n"); + + ret = sl_unpack(d, + (const char *)mdspkt_empty_cnid_fm, + sizeof(mdspkt_empty_cnid_fm)); + torture_assert_goto(tctx, ret, ret, done, "sl_unpack failed\n"); + + cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1); + torture_assert_not_null_goto(tctx, cnids, ret, done, + "dalloc_get cnids failed\n"); + + ncnids = dalloc_size(cnids->ca_cnids); + torture_assert_int_equal_goto(tctx, ncnids, 0, ret, done, + "Wrong number of CNIDs\n"); + + dstr = dalloc_dump(d, 0); + torture_assert_not_null_goto(tctx, dstr, ret, done, + "dalloc_dump failed\n"); + + torture_assert_str_equal_goto(tctx, dstr, mdspkt_empty_cnid_fm_dump, + ret, done, "Bad dump\n"); + +done: + TALLOC_FREE(d); + return ret; +} + +struct torture_suite *torture_local_mdspkt(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = + torture_suite_create(mem_ctx, "mdspkt"); + + torture_suite_add_simple_test(suite, + "empty_cnid_fm", + test_mdspkt_empty_cnid_fm); + + return suite; +} diff --git a/source4/torture/local/nss_tests.c b/source4/torture/local/nss_tests.c new file mode 100644 index 0000000..e911aa2 --- /dev/null +++ b/source4/torture/local/nss_tests.c @@ -0,0 +1,1061 @@ +/* + Unix SMB/CIFS implementation. + + local testing of the nss wrapper + + Copyright (C) Guenther Deschner 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" + +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "lib/replace/system/passwd.h" + +static bool copy_passwd(struct torture_context *tctx, + const struct passwd *pwd, + struct passwd *p) +{ + p->pw_name = talloc_strdup(tctx, pwd->pw_name); + torture_assert(tctx, (p->pw_name != NULL || pwd->pw_name == NULL), __location__); + p->pw_passwd = talloc_strdup(tctx, pwd->pw_passwd); + torture_assert(tctx, (p->pw_passwd != NULL || pwd->pw_passwd == NULL), __location__); + p->pw_uid = pwd->pw_uid; + p->pw_gid = pwd->pw_gid; + p->pw_gecos = talloc_strdup(tctx, pwd->pw_gecos); + torture_assert(tctx, (p->pw_gecos != NULL || pwd->pw_gecos == NULL), __location__); + p->pw_dir = talloc_strdup(tctx, pwd->pw_dir); + torture_assert(tctx, (p->pw_dir != NULL || pwd->pw_dir == NULL), __location__); + p->pw_shell = talloc_strdup(tctx, pwd->pw_shell); + torture_assert(tctx, (p->pw_shell != NULL || pwd->pw_shell == NULL), __location__); + + return true; +} + +static void print_passwd(struct passwd *pwd) +{ + printf("%s:%s:%lu:%lu:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + (unsigned long)pwd->pw_uid, + (unsigned long)pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + + +static bool test_getpwnam(struct torture_context *tctx, + const char *name, + struct passwd *pwd_p) +{ + struct passwd *pwd; + int ret; + + torture_comment(tctx, "Testing getpwnam: %s\n", name); + + errno = 0; + pwd = getpwnam(name); + ret = errno; + torture_assert(tctx, (pwd != NULL), talloc_asprintf(tctx, + "getpwnam(%s) failed - %d - %s", + name, ret, strerror(ret))); + + if (pwd_p != NULL) { + torture_assert(tctx, copy_passwd(tctx, pwd, pwd_p), __location__); + } + + return true; +} + +static bool test_getpwnam_r(struct torture_context *tctx, + const char *name, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getpwnam_r: %s\n", name); + + ret = getpwnam_r(name, &pwd, buffer, sizeof(buffer), &pwdp); + torture_assert(tctx, ret == 0, talloc_asprintf(tctx, + "getpwnam_r(%s) failed - %d - %s", + name, ret, strerror(ret))); + + print_passwd(&pwd); + + if (pwd_p != NULL) { + torture_assert(tctx, copy_passwd(tctx, &pwd, pwd_p), __location__); + } + + return true; +} + +static bool test_getpwuid(struct torture_context *tctx, + uid_t uid, + struct passwd *pwd_p) +{ + struct passwd *pwd; + int ret; + + torture_comment(tctx, "Testing getpwuid: %lu\n", (unsigned long)uid); + + errno = 0; + pwd = getpwuid(uid); + ret = errno; + torture_assert(tctx, (pwd != NULL), talloc_asprintf(tctx, + "getpwuid(%lu) failed - %d - %s", + (unsigned long)uid, ret, strerror(ret))); + + print_passwd(pwd); + + if (pwd_p != NULL) { + torture_assert(tctx, copy_passwd(tctx, pwd, pwd_p), __location__); + } + + return true; +} + +static bool test_getpwuid_r(struct torture_context *tctx, + uid_t uid, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getpwuid_r: %lu\n", (unsigned long)uid); + + ret = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &pwdp); + torture_assert(tctx, ret == 0, talloc_asprintf(tctx, + "getpwuid_r(%lu) failed - %d - %s", + (unsigned long)uid, ret, strerror(ret))); + + print_passwd(&pwd); + + if (pwd_p != NULL) { + torture_assert(tctx, copy_passwd(tctx, &pwd, pwd_p), __location__); + } + + return true; +} + + +static bool copy_group(struct torture_context *tctx, + const struct group *grp, + struct group *g) +{ + int i; + + g->gr_name = talloc_strdup(tctx, grp->gr_name); + torture_assert(tctx, (g->gr_name != NULL || grp->gr_name == NULL), __location__); + g->gr_passwd = talloc_strdup(tctx, grp->gr_passwd); + torture_assert(tctx, (g->gr_passwd != NULL || grp->gr_passwd == NULL), __location__); + g->gr_gid = grp->gr_gid; + g->gr_mem = NULL; + + for (i=0; grp->gr_mem && grp->gr_mem[i]; i++) { + g->gr_mem = talloc_realloc(tctx, g->gr_mem, char *, i + 2); + torture_assert(tctx, (g->gr_mem != NULL), __location__); + g->gr_mem[i] = talloc_strdup(g->gr_mem, grp->gr_mem[i]); + torture_assert(tctx, (g->gr_mem[i] != NULL), __location__); + g->gr_mem[i+1] = NULL; + } + + return true; +} + +static void print_group(struct group *grp) +{ + int i; + printf("%s:%s:%lu:", + grp->gr_name, + grp->gr_passwd, + (unsigned long)grp->gr_gid); + + if ((grp->gr_mem == NULL) || !grp->gr_mem[0]) { + printf("\n"); + return; + } + + for (i=0; grp->gr_mem[i+1]; i++) { + printf("%s,", grp->gr_mem[i]); + } + printf("%s\n", grp->gr_mem[i]); +} + +static bool test_getgrnam(struct torture_context *tctx, + const char *name, + struct group *grp_p) +{ + struct group *grp; + int ret; + + torture_comment(tctx, "Testing getgrnam: %s\n", name); + + errno = 0; + grp = getgrnam(name); + ret = errno; + torture_assert(tctx, (grp != NULL), talloc_asprintf(tctx, + "getgrnam(%s) failed - %d - %s", + name, ret, strerror(ret))); + + print_group(grp); + + if (grp_p != NULL) { + torture_assert(tctx, copy_group(tctx, grp, grp_p), __location__); + } + + return true; +} + +static bool test_getgrnam_r(struct torture_context *tctx, + const char *name, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getgrnam_r: %s\n", name); + + ret = getgrnam_r(name, &grp, buffer, sizeof(buffer), &grpp); + torture_assert(tctx, ret == 0, talloc_asprintf(tctx, + "getgrnam_r(%s) failed - %d - %s", + name, ret, strerror(ret))); + + print_group(&grp); + + if (grp_p != NULL) { + torture_assert(tctx, copy_group(tctx, &grp, grp_p), __location__); + } + + return true; +} + + +static bool test_getgrgid(struct torture_context *tctx, + gid_t gid, + struct group *grp_p) +{ + struct group *grp; + int ret; + + torture_comment(tctx, "Testing getgrgid: %lu\n", (unsigned long)gid); + + errno = 0; + grp = getgrgid(gid); + ret = errno; + torture_assert(tctx, (grp != NULL), talloc_asprintf(tctx, + "getgrgid(%lu) failed - %d - %s", + (unsigned long)gid, ret, strerror(ret))); + + print_group(grp); + + if (grp_p != NULL) { + torture_assert(tctx, copy_group(tctx, grp, grp_p), __location__); + } + + return true; +} + +static bool test_getgrgid_r(struct torture_context *tctx, + gid_t gid, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getgrgid_r: %lu\n", (unsigned long)gid); + + ret = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &grpp); + torture_assert(tctx, ret == 0, talloc_asprintf(tctx, + "getgrgid_r(%lu) failed - %d - %s", + (unsigned long)gid, ret, strerror(ret))); + + print_group(&grp); + + if (grp_p != NULL) { + torture_assert(tctx, copy_group(tctx, &grp, grp_p), __location__); + } + + return true; +} + +static bool test_enum_passwd(struct torture_context *tctx, + struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd *pwd; + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; + + torture_comment(tctx, "Testing setpwent\n"); + setpwent(); + + while ((pwd = getpwent()) != NULL) { + torture_comment(tctx, "Testing getpwent\n"); + + print_passwd(pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1); + torture_assert(tctx, pwd_array, "out of memory"); + copy_passwd(tctx, pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } + + torture_comment(tctx, "Testing endpwent\n"); + endpwent(); + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool test_enum_r_passwd(struct torture_context *tctx, + struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd pwd, *pwdp; + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing setpwent\n"); + setpwent(); + +#ifdef HAVE_GETPWENT_R /* getpwent_r not supported on macOS */ + while (1) { + torture_comment(tctx, "Testing getpwent_r\n"); + +#ifdef SOLARIS_GETPWENT_R + ret = getpwent_r(&pwd, buffer, sizeof(buffer)); +#else /* SOLARIS_GETPWENT_R */ + ret = getpwent_r(&pwd, buffer, sizeof(buffer), &pwdp); +#endif /* SOLARIS_GETPWENT_R */ + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + break; + } + print_passwd(&pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1); + torture_assert(tctx, pwd_array, "out of memory"); + copy_passwd(tctx, &pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } +#endif /* getpwent_r not supported on macOS */ + + torture_comment(tctx, "Testing endpwent\n"); + endpwent(); + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool torture_assert_passwd_equal(struct torture_context *tctx, + const struct passwd *p1, + const struct passwd *p2, + const char *comment) +{ + torture_assert_str_equal(tctx, p1->pw_name, p2->pw_name, comment); + torture_assert_str_equal(tctx, p1->pw_passwd, p2->pw_passwd, comment); + torture_assert_int_equal(tctx, p1->pw_uid, p2->pw_uid, comment); + torture_assert_int_equal(tctx, p1->pw_gid, p2->pw_gid, comment); + torture_assert_str_equal(tctx, p1->pw_gecos, p2->pw_gecos, comment); + torture_assert_str_equal(tctx, p1->pw_dir, p2->pw_dir, comment); + torture_assert_str_equal(tctx, p1->pw_shell, p2->pw_shell, comment); + + return true; +} + +static bool test_passwd(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2; + size_t num_pwd; + + torture_assert(tctx, test_enum_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_getpwnam(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent and getpwnam gave different results"), + __location__); + torture_assert(tctx, test_getpwuid(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent and getpwuid gave different results"), + __location__); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam and getpwuid gave different results"), + __location__); + } + + return true; +} + +static bool test_passwd_r(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2; + size_t num_pwd; + + torture_assert(tctx, test_enum_r_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_getpwnam_r(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam_r for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent_r and getpwnam_r gave different results"), + __location__); + torture_assert(tctx, test_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid_r for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent_r and getpwuid_r gave different results"), + __location__); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam_r and getpwuid_r gave different results"), + __location__); + } + + return true; +} + +static bool test_passwd_r_cross(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2, pwd3, pwd4; + size_t num_pwd; + + torture_assert(tctx, test_enum_r_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_getpwnam_r(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam_r for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent_r and getpwnam_r gave different results"), + __location__); + torture_assert(tctx, test_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid_r for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent_r and getpwuid_r gave different results"), + __location__); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam_r and getpwuid_r gave different results"), + __location__); + torture_assert(tctx, test_getpwnam(tctx, pwd[i].pw_name, &pwd3), + "failed to call getpwnam for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd3, + "getpwent_r and getpwnam gave different results"), + __location__); + torture_assert(tctx, test_getpwuid(tctx, pwd[i].pw_uid, &pwd4), + "failed to call getpwuid for enumerated user"); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd4, + "getpwent_r and getpwuid gave different results"), + __location__); + torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd3, &pwd4, + "getpwnam and getpwuid gave different results"), + __location__); + } + + return true; +} + +static bool test_enum_group(struct torture_context *tctx, + struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group *grp; + struct group *grp_array = NULL; + size_t num_grp = 0; + + torture_comment(tctx, "Testing setgrent\n"); + setgrent(); + + while ((grp = getgrent()) != NULL) { + torture_comment(tctx, "Testing getgrent\n"); + + print_group(grp); + if (grp_array_p && num_grp_p) { + grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1); + torture_assert(tctx, grp_array, "out of memory"); + copy_group(tctx, grp, &grp_array[num_grp]); + num_grp++; + } + } + + torture_comment(tctx, "Testing endgrent\n"); + endgrent(); + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool test_enum_r_group(struct torture_context *tctx, + struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group grp, *grpp; + struct group *grp_array = NULL; + size_t num_grp = 0; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing setgrent\n"); + setgrent(); + +#ifdef HAVE_GETGRENT_R /* getgrent_r not supported on macOS */ + while (1) { + torture_comment(tctx, "Testing getgrent_r\n"); + +#ifdef SOLARIS_GETGRENT_R + ret = getgrent_r(&grp, buffer, sizeof(buffer)); +#else /* SOLARIS_GETGRENT_R */ + ret = getgrent_r(&grp, buffer, sizeof(buffer), &grpp); +#endif /* SOLARIS_GETGRENT_R */ + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + break; + } + print_group(&grp); + if (grp_array_p && num_grp_p) { + grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1); + torture_assert(tctx, grp_array, "out of memory"); + copy_group(tctx, &grp, &grp_array[num_grp]); + num_grp++; + } + } +#endif /* getgrent_r not supported on macOS */ + + torture_comment(tctx, "Testing endgrent\n"); + endgrent(); + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool torture_assert_group_equal(struct torture_context *tctx, + const struct group *g1, + const struct group *g2, + const char *comment) +{ + int i; + torture_assert_str_equal(tctx, g1->gr_name, g2->gr_name, comment); + torture_assert_str_equal(tctx, g1->gr_passwd, g2->gr_passwd, comment); + torture_assert_int_equal(tctx, g1->gr_gid, g2->gr_gid, comment); + torture_assert(tctx, !(g1->gr_mem && !g2->gr_mem), __location__); + torture_assert(tctx, !(!g1->gr_mem && g2->gr_mem), __location__); + if (!g1->gr_mem && !g2->gr_mem) { + return true; + } + for (i=0; g1->gr_mem[i] && g2->gr_mem[i]; i++) { + torture_assert_str_equal(tctx, g1->gr_mem[i], g2->gr_mem[i], comment); + } + + return true; +} + +static bool test_group(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2; + size_t num_grp; + + torture_assert(tctx, test_enum_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_getgrnam(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent and getgrnam gave different results"), + __location__); + torture_assert(tctx, test_getgrgid(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent and getgrgid gave different results"), + __location__); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam and getgrgid gave different results"), + __location__); + } + + return true; +} + +static bool test_group_r(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2; + size_t num_grp; + + torture_assert(tctx, test_enum_r_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_getgrnam_r(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam_r for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent_r and getgrnam_r gave different results"), + __location__); + torture_assert(tctx, test_getgrgid_r(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid_r for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent_r and getgrgid_r gave different results"), + __location__); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam_r and getgrgid_r gave different results"), + __location__); + } + + return true; +} + +static bool test_group_r_cross(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2, grp3, grp4; + size_t num_grp; + + torture_assert(tctx, test_enum_r_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_getgrnam_r(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam_r for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent_r and getgrnam_r gave different results"), + __location__); + torture_assert(tctx, test_getgrgid_r(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid_r for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent_r and getgrgid_r gave different results"), + __location__); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam_r and getgrgid_r gave different results"), + __location__); + torture_assert(tctx, test_getgrnam(tctx, grp[i].gr_name, &grp3), + "failed to call getgrnam for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp3, + "getgrent_r and getgrnam gave different results"), + __location__); + torture_assert(tctx, test_getgrgid(tctx, grp[i].gr_gid, &grp4), + "failed to call getgrgid for enumerated user"); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp4, + "getgrent_r and getgrgid gave different results"), + __location__); + torture_assert(tctx, torture_assert_group_equal(tctx, &grp3, &grp4, + "getgrnam and getgrgid gave different results"), + __location__); + } + + return true; +} + +#ifdef HAVE_GETGROUPLIST +static bool test_getgrouplist(struct torture_context *tctx, + const char *user, + gid_t gid, + gid_t **gids_p, + int *num_gids_p) +{ + int ret; + int num_groups = 0; + gid_t *groups = NULL; + + torture_comment(tctx, "Testing getgrouplist: %s\n", user); + + ret = getgrouplist(user, gid, NULL, &num_groups); + if (ret == -1 || num_groups != 0) { + + groups = talloc_array(tctx, gid_t, num_groups); + torture_assert(tctx, groups, "out of memory\n"); + + ret = getgrouplist(user, gid, groups, &num_groups); + } + + torture_assert(tctx, (ret != -1), "failed to call getgrouplist"); + + torture_comment(tctx, "%s is member in %d groups\n", user, num_groups); + + if (gids_p) { + *gids_p = groups; + } + if (num_gids_p) { + *num_gids_p = num_groups; + } + + return true; +} +#endif /* HAVE_GETGROUPLIST */ + +static bool test_user_in_group(struct torture_context *tctx, + const struct passwd *pwd, + const struct group *grp) +{ + int i; + + for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) { + if (strequal(grp->gr_mem[i], pwd->pw_name)) { + return true; + } + } + + return false; +} + +static bool test_membership_user(struct torture_context *tctx, + const struct passwd *pwd, + struct group *grp_array, + size_t num_grp) +{ + int num_user_groups = 0; + int num_user_groups_from_enum = 0; + gid_t *user_groups = NULL; + int g, i; + bool primary_group_had_user_member = false; + + /* + * For the local users ('LOCALADMEMBER') below, the test fails. + * wb_queryuser() wrongly defaults the group sid to RID 513 i.e. + * 'LOCALADMEMBER/domusers', but those users have a different group sid. + * + * The fix for wb_queryuser() is not part of this MR. It is a complex + * task that needs to fill samlogon cache using S4USelf and will come + * sometime later. Once wb_queryuser() gets fixed, this can be removed. + */ + if (strcmp(pwd->pw_name, "user1") == 0 || + strcmp(pwd->pw_name, "user2") == 0 || + strcmp(pwd->pw_name, "force_user") == 0 || pwd->pw_uid == 1000) { + return true; + } + +#ifdef HAVE_GETGROUPLIST + torture_assert(tctx, test_getgrouplist(tctx, + pwd->pw_name, + pwd->pw_gid, + &user_groups, + &num_user_groups), + "failed to test getgrouplist"); +#endif /* HAVE_GETGROUPLIST */ + + for (g=0; g < num_user_groups; g++) { + torture_assert(tctx, test_getgrgid(tctx, user_groups[g], NULL), + "failed to find the group the user is a member of"); + } + + + for (i=0; i < num_grp; i++) { + + struct group grp = grp_array[i]; + + if (test_user_in_group(tctx, pwd, &grp)) { + + struct group current_grp; + num_user_groups_from_enum++; + + torture_assert(tctx, test_getgrnam(tctx, grp.gr_name, ¤t_grp), + "failed to find the group the user is a member of"); + + if (current_grp.gr_gid == pwd->pw_gid) { + torture_comment(tctx, "primary group %s of user %s lists user as member\n", + current_grp.gr_name, + pwd->pw_name); + primary_group_had_user_member = true; + } + + continue; + } + } + + if (!primary_group_had_user_member) { + num_user_groups_from_enum++; + } + + torture_assert_int_equal(tctx, num_user_groups, num_user_groups_from_enum, + "getgrouplist and real inspection of grouplist gave different results\n"); + + return true; +} + +static bool test_membership(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + struct passwd *pwd; + size_t num_pwd; + struct group *grp; + size_t num_grp; + int i; + const char *env = getenv("ENVNAME"); + + if (!old_pwd || !old_group) { + torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + torture_skip(tctx, "nothing to test\n"); + } + + /* + * test_membership_user() fails for ad_dc with error like this: + * + * WARNING!: ../../source4/torture/local/nss_tests.c:823: + * num_user_groups was 3 (0x3), expected 2 (0x2): getgrouplist + * and real inspection of grouplist gave different results + + * There are at least 3 reasons: + + * 1. For each ADDOMAIN user, there is also a group with the same name: + +$ bin/wbinfo --user-info ADDOMAIN/alice +ADDOMAIN/alice:*:3000015:65531::/home/ADDOMAIN/alice:/bin/false + +$ bin/wbinfo --group-info ADDOMAIN/alice +ADDOMAIN/alice:x:3000015:ADDOMAIN/alice + + * 2. ADDOMAIN/joe is the only user of "ADDOMAIN/Domain Users" + * e.g. alice is not there: + +$ bin/wbinfo --group-info "ADDOMAIN/Domain users" +ADDOMAIN/domain users:x:65531:ADDOMAIN/joe + + * 3. getgrouplist() for joe returns also "ADDOMAIN/samba users" + * but "ADDOMAIN/samba users" is an empty group: + +$ bin/wbinfo --group-info "ADDOMAIN/samba users" +ADDOMAIN/samba users:x:3000051: + + */ + + /* Only ad_member_idmap_rid sets 'winbind expand groups' */ + if (strcmp(env, "ad_member_idmap_rid:local") != 0) { + torture_comment(tctx, + "Testing in env '%s' is not supported.\n", + env); + torture_skip(tctx, "nothing to test\n"); + return true; + } + + torture_assert(tctx, test_enum_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + torture_assert(tctx, test_enum_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_pwd; i++) { + + torture_assert(tctx, test_membership_user(tctx, &pwd[i], grp, num_grp), + "failed to test membership for user"); + + } + + return true; +} + +static bool test_enumeration(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + torture_skip(tctx, "nothing to test\n"); + } + + torture_assert(tctx, test_passwd(tctx), + "failed to test users"); + torture_assert(tctx, test_group(tctx), + "failed to test groups"); + + return true; +} + +static bool test_reentrant_enumeration(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + torture_skip(tctx, "nothing to test\n"); + } + + torture_comment(tctx, "Testing re-entrant calls\n"); + + torture_assert(tctx, test_passwd_r(tctx), + "failed to test users"); + torture_assert(tctx, test_group_r(tctx), + "failed to test groups"); + + return true; +} + +static bool test_reentrant_enumeration_crosschecks(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + torture_skip(tctx, "nothing to test\n"); + } + + torture_comment(tctx, "Testing re-entrant calls with cross checks\n"); + + torture_assert(tctx, test_passwd_r_cross(tctx), + "failed to test users"); + torture_assert(tctx, test_group_r_cross(tctx), + "failed to test groups"); + + return true; +} + +static bool test_passwd_duplicates(struct torture_context *tctx) +{ + size_t i, d; + struct passwd *pwd; + size_t num_pwd; + int duplicates = 0; + + torture_assert(tctx, test_enum_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + const char *current_name = pwd[i].pw_name; + for (d=0; d < num_pwd; d++) { + const char *dup_name = pwd[d].pw_name; + if (d == i) { + continue; + } + if (!strequal(current_name, dup_name)) { + continue; + } + + torture_warning(tctx, "found duplicate names:"); + print_passwd(&pwd[d]); + print_passwd(&pwd[i]); + duplicates++; + } + } + + if (duplicates) { + torture_fail(tctx, talloc_asprintf(tctx, "found %d duplicate names", duplicates)); + } + + return true; +} + +static bool test_group_duplicates(struct torture_context *tctx) +{ + size_t i, d; + struct group *grp; + size_t num_grp; + int duplicates = 0; + + torture_assert(tctx, test_enum_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + const char *current_name = grp[i].gr_name; + for (d=0; d < num_grp; d++) { + const char *dup_name = grp[d].gr_name; + if (d == i) { + continue; + } + if (!strequal(current_name, dup_name)) { + continue; + } + + torture_warning(tctx, "found duplicate names:"); + print_group(&grp[d]); + print_group(&grp[i]); + duplicates++; + } + } + + if (duplicates) { + torture_fail(tctx, talloc_asprintf(tctx, "found %d duplicate names", duplicates)); + } + + return true; +} + + +static bool test_duplicates(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + torture_skip(tctx, "nothing to test\n"); + } + + torture_assert(tctx, test_passwd_duplicates(tctx), + "failed to test users"); + torture_assert(tctx, test_group_duplicates(tctx), + "failed to test groups"); + + return true; +} + + +struct torture_suite *torture_local_nss(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "nss"); + + torture_suite_add_simple_test(suite, "enumeration", test_enumeration); + torture_suite_add_simple_test(suite, "reentrant enumeration", test_reentrant_enumeration); + torture_suite_add_simple_test(suite, "reentrant enumeration crosschecks", test_reentrant_enumeration_crosschecks); + torture_suite_add_simple_test(suite, "membership", test_membership); + torture_suite_add_simple_test(suite, "duplicates", test_duplicates); + + return suite; +} diff --git a/source4/torture/local/smbtorture_fullname.c b/source4/torture/local/smbtorture_fullname.c new file mode 100644 index 0000000..875b3cf --- /dev/null +++ b/source4/torture/local/smbtorture_fullname.c @@ -0,0 +1,31 @@ +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/local/proto.h" + +static bool test_smbtorture_always_pass(struct torture_context *tctx) +{ + return true; +} + +struct torture_suite *torture_local_smbtorture(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "smbtorture"); + struct torture_suite *suite_level1 = torture_suite_create(ctx, + "level1"); + struct torture_suite *suite_level2 = torture_suite_create(ctx, + "level2"); + struct torture_suite *suite_level3 = torture_suite_create(ctx, + "level3"); + + torture_suite_add_suite(suite_level2, suite_level3); + torture_suite_add_suite(suite_level1, suite_level2); + torture_suite_add_suite(suite, suite_level1); + + torture_suite_add_simple_test(suite_level3, "always_pass", + test_smbtorture_always_pass); + + suite->description = talloc_strdup(suite, + "smbtorture multilevel always pass test."); + + return suite; +} diff --git a/source4/torture/local/torture.c b/source4/torture/local/torture.c new file mode 100644 index 0000000..ca59ebf --- /dev/null +++ b/source4/torture/local/torture.c @@ -0,0 +1,85 @@ +/* + Unix SMB/CIFS implementation. + + local testing of torture + + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/wait.h" +#include "libcli/raw/libcliraw.h" +#include "torture/util.h" +#include "torture/local/proto.h" +#include "param/provision.h" + +static bool test_tempdir(struct torture_context *tctx) +{ + char *location = NULL; + TALLOC_CTX *mem_ctx = tctx; + + torture_assert_ntstatus_ok(tctx, torture_temp_dir(mem_ctx, "tempdir", &location), + "torture_temp_dir should return NT_STATUS_OK" ); + + torture_assert(tctx, directory_exist(location), + "created dir doesn't exist"); + return true; +} + +static bool test_provision(struct torture_context *tctx) +{ + NTSTATUS status; + struct provision_settings *settings = talloc_zero(tctx, struct provision_settings); + struct provision_result result; + char *targetdir = NULL; + + torture_assert_ntstatus_ok(tctx, torture_temp_dir(tctx, "torture_provision", &targetdir), + "torture_temp_dir should return NT_STATUS_OK" ); + settings->targetdir = talloc_steal(settings, targetdir); + + settings->site_name = "SOME-SITE-NAME"; + settings->root_dn_str = "DC=EXAMPLE,DC=COM"; + settings->domain_dn_str = "DC=EXAMPLE,DC=COM"; + settings->config_dn_str = NULL; + settings->schema_dn_str = NULL; + settings->invocation_id = NULL; + settings->netbios_name = "FOO"; + settings->realm = "EXAMPLE.COM"; + settings->domain = "EXAMPLE"; + settings->netbios_name = "torture"; + settings->ntds_dn_str = NULL; + settings->machine_password = "geheim"; + settings->use_ntvfs = true; + + status = provision_bare(settings, tctx->lp_ctx, settings, &result); + + torture_assert_ntstatus_ok(tctx, status, "provision"); + + torture_assert_str_equal(tctx, result.domaindn, "DC=EXAMPLE,DC=COM", + "domaindn incorrect"); + + return true; +} + +struct torture_suite *torture_local_torture(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "torture"); + + torture_suite_add_simple_test(suite, "tempdir", test_tempdir); + torture_suite_add_simple_test(suite, "provision", test_provision); + + return suite; +} diff --git a/source4/torture/local/verif_trailer.c b/source4/torture/local/verif_trailer.c new file mode 100644 index 0000000..acbd69b --- /dev/null +++ b/source4/torture/local/verif_trailer.c @@ -0,0 +1,99 @@ +/* + Unix SMB/CIFS implementation. + + test suite for DCE/RPC verification trailer parsing + + Copyright (C) David Disseldorp 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include + +#include "librpc/gen_ndr/security.h" +#include "lib/param/param.h" +#include "lib/util/dlinklist.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_dcerpc.h" +#include "librpc/rpc/rpc_common.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +/* VT blob obtained from an FSRVP request */ +uint8_t test_vt[] = {0x8a, 0xe3, 0x13, 0x71, 0x02, 0xf4, 0x36, 0x71, + 0x02, 0x40, 0x28, 0x00, 0x3c, 0x65, 0xe0, 0xa8, + 0x44, 0x27, 0x89, 0x43, 0xa6, 0x1d, 0x73, 0x73, + 0xdf, 0x8b, 0x22, 0x92, 0x01, 0x00, 0x00, 0x00, + 0x33, 0x05, 0x71, 0x71, 0xba, 0xbe, 0x37, 0x49, + 0x83, 0x19, 0xb5, 0xdb, 0xef, 0x9c, 0xcc, 0x36, + 0x01, 0x00, 0x00, 0x00}; + +const char *vt_abstr_syntax = "a8e0653c-2744-4389-a61d-7373df8b2292/0x00000001"; +const char *vt_trans_syntax = "71710533-beba-4937-8319-b5dbef9ccc36/0x00000001"; + +static bool test_verif_trailer_pctx(struct torture_context *tctx) +{ + DATA_BLOB blob; + bool ok; + struct dcerpc_sec_vt_pcontext pctx; + struct dcerpc_sec_verification_trailer *vt = NULL; + struct ndr_pull *ndr; + enum ndr_err_code ndr_err; + struct ndr_print *ndr_print; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + torture_assert(tctx, mem_ctx != NULL, "mem"); + + blob.data = test_vt; + blob.length = ARRAY_SIZE(test_vt); + + ndr = ndr_pull_init_blob(&blob, mem_ctx); + torture_assert(tctx, ndr != NULL, "ndr"); + + ndr_err = ndr_pop_dcerpc_sec_verification_trailer(ndr, mem_ctx, &vt); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr"); + + ndr_print = talloc_zero(mem_ctx, struct ndr_print); + torture_assert(tctx, ndr_print != NULL, "mem"); + ndr_print->print = ndr_print_printf_helper; + ndr_print->depth = 1; + + ndr_print_dcerpc_sec_verification_trailer(ndr_print, + "Verification Trailer", vt); + + ZERO_STRUCT(pctx); + ok = ndr_syntax_id_from_string(vt_abstr_syntax, &pctx.abstract_syntax); + torture_assert(tctx, ok, "vt_abstr_syntax"); + ok = ndr_syntax_id_from_string(vt_trans_syntax, &pctx.transfer_syntax); + torture_assert(tctx, ok, "vt_trans_syntax"); + + ok = dcerpc_sec_verification_trailer_check(vt, NULL, &pctx, NULL); + torture_assert(tctx, ok, "VT check"); + + talloc_free(mem_ctx); + + return true; +} + +struct torture_suite *torture_local_verif_trailer(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "verif_trailer"); + + torture_suite_add_simple_test(suite, + "pctx", + test_verif_trailer_pctx); + + return suite; +} diff --git a/source4/torture/local/wscript_build b/source4/torture/local/wscript_build new file mode 100644 index 0000000..741f667 --- /dev/null +++ b/source4/torture/local/wscript_build @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +provision = bld.pyembed_libname('PROVISION') + +TORTURE_LOCAL_SOURCE = '''../../../lib/util/charset/tests/iconv.c + ../../../lib/talloc/testsuite.c ../../lib/messaging/tests/messaging.c + ../../lib/messaging/tests/irpc.c ../../librpc/tests/binding_string.c + ../../../lib/util/tests/idtree.c ../../../lib/util/tests/dlinklist.c + ../../lib/socket/testsuite.c ../../libcli/resolve/testsuite.c + ../../../lib/util/tests/strlist.c ../../../lib/util/tests/binsearch.c + ../../../lib/util/tests/str.c ../../../lib/util/tests/time.c + ../../../lib/util/tests/asn1_tests.c ../../../lib/util/tests/data_blob.c + ../../../lib/util/tests/file.c ../../../lib/util/tests/genrand.c + ../../../lib/util/charset/tests/charset.c + ../../../lib/util/charset/tests/convert_string.c + ../../../lib/util/charset/tests/util_unistr.c + ../../../lib/tdr/testsuite.c + ../../../lib/tevent/testsuite.c ../../param/tests/share.c + ../../../lib/tevent/test_req.c + ../../param/tests/loadparm.c local.c + dbspeed.c torture.c ../ldb/ldb.c ../../dsdb/common/tests/dsdb_dn.c + ../../dsdb/schema/tests/schema_syntax.c + ../../../lib/util/tests/anonymous_shared.c + ../../../lib/util/tests/strv.c + ../../../lib/util/tests/strv_util.c + ../../../lib/util/tests/util.c + ../../../lib/util/tests/util_str_escape.c + ../../../lib/util/tests/tfork.c + verif_trailer.c + nss_tests.c + mdspkt.c + fsrvp_state.c + smbtorture_fullname.c''' + +TORTURE_LOCAL_DEPS = 'RPC_NDR_ECHO TDR LIBCLI_SMB MESSAGING iconv TORTURE_AUTH TORTURE_UTIL TORTURE_NDR TORTURE_LIBCRYPTO share torture_registry %s ldb samdb replace-test RPC_FSS_STATE util_str_escape' % provision + +bld.SAMBA_MODULE('TORTURE_LOCAL', + source=TORTURE_LOCAL_SOURCE, + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_local_init', + deps=TORTURE_LOCAL_DEPS, + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) diff --git a/source4/torture/locktest.c b/source4/torture/locktest.c new file mode 100644 index 0000000..11701a9 --- /dev/null +++ b/source4/torture/locktest.c @@ -0,0 +1,700 @@ +/* + Unix SMB/CIFS implementation. + randomised byte range lock tester + Copyright (C) Andrew Tridgell 1999 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "lib/events/events.h" +#include "system/filesys.h" +#include "system/time.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/gensec.h" +#include "libcli/libcli.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +static int numops = 1000; +static int showall; +static int analyze; +static int hide_unlock_fails; +static int use_oplocks; +static unsigned int lock_range = 100; +static unsigned int lock_base = 0; +static unsigned int min_length = 0; +static int exact_error_codes; +static int zero_zero; + +#define FILENAME "\\locktest.dat" + +#define READ_PCT 50 +#define LOCK_PCT 45 +#define UNLOCK_PCT 70 +#define RANGE_MULTIPLE 1 +#define NSERVERS 2 +#define NCONNECTIONS 2 +#define NFILES 2 +#define LOCK_TIMEOUT 0 + +static struct cli_credentials *servers[NSERVERS]; + +enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN}; + +struct record { + enum lock_op lock_op; + enum brl_type lock_type; + char conn, f; + uint64_t start, len; + char needed; + uint16_t pid; +}; + +#define PRESETS 0 + +#if PRESETS +static struct record preset[] = { +{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 3, 0, 1}, +{OP_UNLOCK, 0 , 0, 0, 2, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 3, 1, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 1, 1, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, +{OP_LOCK, READ_LOCK, 0, 0, 3, 1, 1}, +{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, +{OP_REOPEN, 0, 0, 0, 0, 0, 1}, + +}; +#endif + +static struct record *recorded; + +/***************************************************** +return a connection to a server +*******************************************************/ +static struct smbcli_state *connect_one(struct tevent_context *ev, + struct loadparm_context *lp_ctx, + TALLOC_CTX *mem_ctx, + char *share, int snum, int conn) +{ + struct smbcli_state *c; + char *server, *myname; + NTSTATUS status; + int retries = 10; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(lp_ctx, &options); + lpcfg_smbcli_session_options(lp_ctx, &session_options); + + printf("connect_one(%s, %d, %d)\n", share, snum, conn); + + server = talloc_strdup(mem_ctx, share+2); + share = strchr_m(server,'\\'); + if (!share) return NULL; + *share = 0; + share++; + + if (snum == 0) { + char **unc_list = NULL; + int num_unc_names; + const char *p; + p = lpcfg_parm_string(lp_ctx, NULL, "torture", "unclist"); + if (p) { + char *h, *s; + unc_list = file_lines_load(p, &num_unc_names, 0, NULL); + if (!unc_list || num_unc_names <= 0) { + printf("Failed to load unc names list from '%s'\n", p); + exit(1); + } + + if (!smbcli_parse_unc(unc_list[conn % num_unc_names], + NULL, &h, &s)) { + printf("Failed to parse UNC name %s\n", + unc_list[conn % num_unc_names]); + exit(1); + } + server = talloc_strdup(mem_ctx, h); + share = talloc_strdup(mem_ctx, s); + } + } + + + myname = talloc_asprintf(mem_ctx, "lock-%d-%d", (int) getpid(), snum); + cli_credentials_set_workstation(servers[snum], myname, CRED_SPECIFIED); + + do { + printf("\\\\%s\\%s\n", server, share); + status = smbcli_full_connection(NULL, &c, + server, + lpcfg_smb_ports(lp_ctx), + share, NULL, + lpcfg_socket_options(lp_ctx), + servers[snum], + lpcfg_resolve_context(lp_ctx), + ev, &options, &session_options, + lpcfg_gensec_settings(mem_ctx, lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + sleep(2); + } + } while (!NT_STATUS_IS_OK(status) && retries--); + + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return c; +} + + +static void reconnect(struct tevent_context *ev, + struct loadparm_context *lp_ctx, + TALLOC_CTX *mem_ctx, + struct smbcli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES], + char *share[NSERVERS]) +{ + int server, conn, f; + + for (server=0;servertree, fnum[server][conn][f]); + fnum[server][conn][f] = -1; + } + } + talloc_free(cli[server][conn]); + } + cli[server][conn] = connect_one(ev, lp_ctx, mem_ctx, share[server], + server, conn); + if (!cli[server][conn]) { + DEBUG(0,("Failed to connect to %s\n", share[server])); + exit(1); + } + } +} + + + +static bool test_one(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], + int fnum[NSERVERS][NCONNECTIONS][NFILES], + struct record *rec) +{ + unsigned int conn = rec->conn; + unsigned int f = rec->f; + uint64_t start = rec->start; + uint64_t len = rec->len; + enum brl_type op = rec->lock_type; + int server; + /* bool ret[NSERVERS]; */ + NTSTATUS status[NSERVERS]; + + switch (rec->lock_op) { + case OP_LOCK: + /* set a lock */ + for (server=0;servertree; + int fn=fnum[server][conn][f]; + + if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + res=smbcli_lock(tree, fn, start, len, LOCK_TIMEOUT, (enum brl_type) rec->lock_op); + } else { + union smb_lock parms; + int ltype; + struct smb_lock_entry lock[1]; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fn; + + ltype = (rec->lock_type == READ_LOCK? 1 : 0); + ltype |= LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.mode = ltype; + parms.lockx.in.timeout = LOCK_TIMEOUT; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = rec->pid; + lock[0].offset = start; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + res = smb_raw_lock(tree, &parms); + } + + /* ret[server] = NT_STATUS_IS_OK(res); */ + status[server] = res; + if (!exact_error_codes && + NT_STATUS_EQUAL(status[server], + NT_STATUS_FILE_LOCK_CONFLICT)) { + status[server] = NT_STATUS_LOCK_NOT_GRANTED; + } + } + if (showall || !NT_STATUS_EQUAL(status[0],status[1])) { + printf("lock conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n", + conn, f, + (double)start, (double)len, + op==READ_LOCK?"READ_LOCK":"WRITE_LOCK", + nt_errstr(status[0]), nt_errstr(status[1])); + } + if (!NT_STATUS_EQUAL(status[0],status[1])) return false; + break; + + case OP_UNLOCK: + /* unset a lock */ + for (server=0;servertree; + int fn=fnum[server][conn][f]; + + + if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + res=smbcli_unlock(tree, fn, start, len); + } else { + union smb_lock parms; + struct smb_lock_entry lock[1]; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fn; + parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.timeout = 0; + parms.lockx.in.ulock_cnt = 1; + parms.lockx.in.lock_cnt = 0; + lock[0].pid = rec->pid; + lock[0].count = len; + lock[0].offset = start; + parms.lockx.in.locks = &lock[0]; + + res = smb_raw_lock(tree, &parms); + } + + /* ret[server] = NT_STATUS_IS_OK(res); */ + status[server] = res; + } + if (showall || + (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) { + printf("unlock conn=%u f=%u range=%.0f(%.0f) -> %s:%s\n", + conn, f, + (double)start, (double)len, + nt_errstr(status[0]), nt_errstr(status[1])); + } + if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) + return false; + break; + + case OP_REOPEN: + /* reopen the file */ + for (server=0;servertree, fnum[server][conn][f]); + fnum[server][conn][f] = -1; + } + for (server=0;servertree, FILENAME, + O_RDWR|O_CREAT, + DENY_NONE); + if (fnum[server][conn][f] == -1) { + printf("failed to reopen on share%d\n", server); + return false; + } + } + if (showall) { + printf("reopen conn=%u f=%u\n", + conn, f); + } + break; + } + + return true; +} + +static void close_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], + int fnum[NSERVERS][NCONNECTIONS][NFILES]) +{ + int server, conn, f; + + for (server=0;servertree, fnum[server][conn][f]); + fnum[server][conn][f] = -1; + } + } + for (server=0;servertree, FILENAME); + } +} + +static void open_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], + int fnum[NSERVERS][NCONNECTIONS][NFILES]) +{ + int server, conn, f; + + for (server=0;servertree, FILENAME, + O_RDWR|O_CREAT, + DENY_NONE); + if (fnum[server][conn][f] == -1) { + fprintf(stderr,"Failed to open fnum[%u][%u][%u]\n", + server, conn, f); + exit(1); + } + } +} + + +static int retest(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], + int fnum[NSERVERS][NCONNECTIONS][NFILES], + int n) +{ + int i; + printf("Testing %u ...\n", n); + for (i=0; i 1) { + skip = skip/2; + printf("skip=%d\n", skip); + continue; + } + + if (n1 == n) break; + } + + close_files(cli, fnum); + reconnect(ev, lp_ctx, mem_ctx, cli, fnum, share); + open_files(cli, fnum); + showall = true; + n1 = retest(cli, fnum, n); + if (n1 != n-1) { + printf("ERROR - inconsistent result (%u %u)\n", n1, n); + } + close_files(cli, fnum); + + for (i=0;i "); + + lp_ctx = samba_cmdline_get_lp_ctx(); + + servers[0] = cli_credentials_init(mem_ctx); + servers[1] = cli_credentials_init(mem_ctx); + cli_credentials_guess(servers[0], lp_ctx); + cli_credentials_guess(servers[1], lp_ctx); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_UNCLIST: + lpcfg_set_cmdline(lp_ctx, "torture:unclist", poptGetOptArg(pc)); + break; + case OPT_USER1: + cli_credentials_parse_string(servers[0], + poptGetOptArg(pc), + CRED_SPECIFIED); + username_count++; + break; + case OPT_USER2: + cli_credentials_parse_string(servers[1], + poptGetOptArg(pc), + CRED_SPECIFIED); + username_count++; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + argv_new = discard_const_p(char *, poptGetArgs(pc)); + argc_new = argc; + for (i=0; i= 3)) { + usage(pc); + exit(1); + } + + setup_logging("locktest", DEBUG_STDOUT); + + for (server=0;server + + + + + gentest + 1 + Samba + Test Suite + 4.0 + + + + + gentest + Run random generic SMB operations against two SMB servers + and show the differences in behavior + + + + + gentest + //server1/share1 + //server2/share2 + -U user%pass + -U user%pass + -s seed + -o numops + -a + -A + -i FILE + -O + -S FILE + -L + -F + -C + -X + + + + + + DESCRIPTION + + gentest is a utility for + detecting differences in behaviour between SMB servers. + It will run a random set of generic operations against + //server1/share1 and then the same + random set against //server2/share2 + and display the differences in the responses it gets. + + + + This utility is used by the Samba team to find differences in + behaviour between Samba and Windows servers. + + + + + + OPTIONS + + + + -U user%pass + + Specify the user and password to use when logging on + on the shares. This parameter is mandatory and has to + be specified twice. + + + + + -s seed + + Seed the random number generator with the specified value. + + + + + -o numops + Set the number of operations to perform. + + + + -a + Print the operations that are performed. + + + + -A + Backtrack to find minimal number of operations + required to make the response to a certain call differ. + + + + + -i FILE + + Specify a file containing the names of fields that + have to be ignored (such as time fields). See + below for a description of the file format. + + + + + -O + Enable oplocks. + + + + -S FILE + Set preset seeds file. The default is gentest_seeds.dat. + + + + -L + Use preset seeds + + + + -F + Fast reconnect (just close files) + + + + -C + Continuous analysis mode + + + + -X + Analyse even when the test succeeded. + + + + + + VERSION + + This man page is correct for version 4.0 of the Samba suite. + + + + SEE ALSO + + Samba + + + + + AUTHOR + + This utility is part of the Samba suite, which is developed by the global Samba Team. + + gentest was written by Andrew Tridgell. + + This manpage was written by Jelmer Vernooij. + + + + diff --git a/source4/torture/man/locktest.1.xml b/source4/torture/man/locktest.1.xml new file mode 100644 index 0000000..3265823 --- /dev/null +++ b/source4/torture/man/locktest.1.xml @@ -0,0 +1,160 @@ + + + + + + locktest + 1 + Samba + Test Suite + 4.0 + + + + + locktest + Find differences in locking between two SMB servers + + + + + locktest + //server1/share1 + //server2/share2 + -U user%pass + -U user%pass + -s seed + -o numops + -a + -O + -E + -Z + -R range + -B base + -M min + + + + + + DESCRIPTION + + locktest is a utility for + detecting differences in behaviour in locking between SMB servers. + It will run a random set of locking operations against + //server1/share1 and then the same + random set against //server2/share2 + and display the differences in the responses it gets. + + + + This utility is used by the Samba team to find differences in + behaviour between Samba and Windows servers. + + + + + + OPTIONS + + + + -U user%pass + + Specify the user and password to use when logging on + on the shares. This parameter can be specified twice + (once for the first server, once for the second). + + + + + -s seed + + Seed the random number generator with the specified value. + + + + + -o numops + Set the number of operations to perform. + + + + -a + Print the operations that are performed. + + + + -A + Backtrack to find minimal number of operations + required to make the response to a certain call differ. + + + + + -O + Enable oplocks. + + + + -u + Hide unlock fails. + + + + -E + enable exact error code checking + + + + -Z + enable the zero/zero lock + + + + -R range + set lock range + + + + -B base + set lock base + + + + -M min + set min lock length + + + + -k + Use kerberos + + + + + + VERSION + + This man page is correct for version 4.0 of the Samba suite. + + + + SEE ALSO + + Samba + + + + + AUTHOR + + This utility is part of the Samba suite, which is developed by the global Samba Team. + + locktest was written by Andrew Tridgell. + + This manpage was written by Jelmer Vernooij. + + + + diff --git a/source4/torture/man/masktest.1.xml b/source4/torture/man/masktest.1.xml new file mode 100644 index 0000000..9cd46e3 --- /dev/null +++ b/source4/torture/man/masktest.1.xml @@ -0,0 +1,142 @@ + + + + + + masktest + 1 + Samba + Test Suite + 4.0 + + + + + masktest + Find differences in wildcard matching between + Samba's implementation and that of a remote server. + + + + + masktest + //server/share + -U user%pass + -d debuglevel + -W workgroup + -n numloops + -s seed + -a + -E + -M max protocol + -f filechars + -m maskchars + -v + + + + + + DESCRIPTION + + masktest is a utility for + detecting differences in behaviour between Samba's + own implementation and that of a remote server. + It will run generate random filenames/masks and + check if these match the same files they do on the remote file as + they do on the local server. It will display any differences it finds. + + + + This utility is used by the Samba team to find differences in + behaviour between Samba and Windows servers. + + + + + + OPTIONS + + + + -U user%pass + + Specify the user and password to use when logging on + on the shares. This parameter can be specified twice + (once for the first server, once for the second). + + + + + -s seed + + Seed the random number generator with the specified value. + + + + + -n numops + Set the number of operations to perform. + + + + -a + Print the operations that are performed. + + + + -M max_protocol + + Maximum protocol to use. + + + + + -f + Specify characters that can be used + when generating file names. Default: abcdefghijklm. + + + + -E + Abort when difference in behaviour is found. + + + + -m maskchars + Specify characters used for wildcards. + + + + -v + Be verbose + + + + + + + VERSION + + This man page is correct for version 4.0 of the Samba suite. + + + + SEE ALSO + + Samba + + + + + AUTHOR + + This utility is part of the Samba suite, which is developed by the global Samba Team. + + masktest was written by Andrew Tridgell. + + This manpage was written by Jelmer Vernooij. + + + + diff --git a/source4/torture/man/smbtorture.1.xml b/source4/torture/man/smbtorture.1.xml new file mode 100644 index 0000000..9d2f9c9 --- /dev/null +++ b/source4/torture/man/smbtorture.1.xml @@ -0,0 +1,253 @@ + + + + + + smbtorture + 1 + Samba + Test Suite + 4.0 + + + + + smbtorture + Run a series of tests against a SMB server + + + + + smbtorture + //server/share + -d debuglevel + -U user%pass + -k + -N numprocs + -n netbios_name + -W workgroup + -e num files(entries) + -O socket_options + -m maximum_protocol + -L + -c CLIENT.TXT + -t timelimit + -C filename + -A + -p port + -s seed + -f max_failures + -X + BINDING-STRING|UNC + TEST1 + TEST2 + ... + + + + + + DESCRIPTION + + smbtorture is a testsuite that runs several tests + against a SMB server. All tests are known to succeed + against a Windows 2003 server (?). Smbtorture's primary + goal is finding differences in implementations of the SMB protocol + and testing SMB servers. + + + Any number of tests can be specified + on the command-line. If no tests are specified, all tests + are run. + + If no arguments are specified at all, all available options + and tests are listed. + + + Binding string format + + The binding string format is: + + TRANSPORT:host[flags] + + Where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP or ncalrpc for local connections. + + + + 'host' is an IP or hostname or netbios name. If the binding string + identifies the server side of an endpoint, 'host' may be an empty + string. + + + + 'flags' can include a SMB pipe name if using the ncacn_np transport or + a TCP port number if using the ncacn_ip_tcp transport, otherwise they + will be auto-determined. + + + + other recognised flags are: + + + + sign + enable ntlmssp signing + + + seal + enable ntlmssp sealing + + + connect + enable rpc connect level auth (auth, but no sign or seal) + + + validate + enable the NDR validator + + + print + enable debugging of the packets + + + bigendian + use bigendian RPC + + + padcheck + check reply data for non-zero pad bytes + + + + For example, these all connect to the samr pipe: + + + ncacn_np:myserver + ncacn_np:myserver[samr] + ncacn_np:myserver[\\pipe\\samr] + ncacn_np:myserver[/pipe/samr] + ncacn_np:myserver[samr,sign,print] + ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian] + ncacn_np:myserver[/pipe/samr,seal,validate] + ncacn_np: + ncacn_np:[/pipe/samr] + ncacn_ip_tcp:myserver + ncacn_ip_tcp:myserver[1024] + ncacn_ip_tcp:myserver[1024,sign,seal] + ncalrpc: + + + + + + UNC Format + + The UNC format is: + + //server/share + + + + + + + OPTIONS + + + -d debuglevel + Use the specified Samba debug level. A higher debug level + means more output. + + -U user%pass + Use the specified username/password combination when logging in to a remote server. + + -k + Use kerberos when authenticating. + + -W workgroup + Use specified name as our workgroup name. + + -n netbios_name + Use specified name as our NetBIOS name. + + + -O socket_options + Use specified socket options, equivalent of the smb.conf option socket options. See the smb.conf(5) manpage for details. + + + -m max_protocol + Specify the maximum SMB dialect that should be used. Possible values are: CORE, COREPLUS, LANMAN1, LANMAN2, NT1 + + + -s seed + Initialize the randomizer using seed as seed. + + + -L + Use oplocks. + + + -X + Enable dangerous tests. Use with care! This might crash your server... + + + -t timelimit + Specify the NBENCH time limit in seconds. Defaults to 600. + + + -p ports + Specify ports to connect to. + + + -c file + Read NBENCH commands from file instead of from CLIENT.TXT. + + + -A + Show not just OK or FAILED but more detailed + output. Used only by DENY test at the moment. + + + -C filename + Load a list of UNC names from the specified filename. Smbtorture instances will connect to a random host from this list. + + + -N numprocs + Specify number of smbtorture processes to launch. + + + -e num_files + Number of entries to use in certain tests (such as creating X files) (default: 1000). + + + -f max_failures + Number of failures before aborting a test (default: 1). + + + + + + VERSION + + This man page is correct for version 4.0 of the Samba suite. + + + + SEE ALSO + + Samba + + + + + AUTHOR + + This utility is part of the Samba suite, which is developed by the global Samba Team. + + smbtorture was written by Andrew Tridgell. + + This manpage was written by Jelmer Vernooij. + + + + diff --git a/source4/torture/masktest.c b/source4/torture/masktest.c new file mode 100644 index 0000000..e311769 --- /dev/null +++ b/source4/torture/masktest.c @@ -0,0 +1,418 @@ +/* + Unix SMB/CIFS implementation. + mask_match tester + Copyright (C) Andrew Tridgell 1999 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "system/filesys.h" +#include "system/dir.h" +#include "libcli/libcli.h" +#include "system/time.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include "lib/events/events.h" + +static bool showall = false; +static bool old_list = false; +static const char *maskchars = "<>\"?*abc."; +static const char *filechars = "abcdefghijklm."; +static int die_on_error; +static int NumLoops = 0; +static int max_length = 20; +struct masktest_state { + TALLOC_CTX *mem_ctx; +}; + +static bool reg_match_one(struct smbcli_state *cli, const char *pattern, const char *file) +{ + /* oh what a weird world this is */ + if (old_list && strcmp(pattern, "*.*") == 0) return true; + + if (ISDOT(pattern)) return false; + + if (ISDOTDOT(file)) file = "."; + + return ms_fnmatch_protocol( + pattern, file, cli->transport->negotiate.protocol, false)==0; +} + +static char *reg_test(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *pattern, const char *long_name, const char *short_name) +{ + char *ret; + ret = talloc_strdup(mem_ctx, "---"); + + pattern = 1+strrchr_m(pattern,'\\'); + + if (reg_match_one(cli, pattern, ".")) ret[0] = '+'; + if (reg_match_one(cli, pattern, "..")) ret[1] = '+'; + if (reg_match_one(cli, pattern, long_name) || + (*short_name && reg_match_one(cli, pattern, short_name))) ret[2] = '+'; + return ret; +} + + +/***************************************************** +return a connection to a server +*******************************************************/ +static struct smbcli_state *connect_one(struct resolve_context *resolve_ctx, + struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + char *share, const char **ports, + const char *socket_options, + struct smbcli_options *options, + struct smbcli_session_options *session_options, + struct gensec_settings *gensec_settings) +{ + struct smbcli_state *c; + char *server; + NTSTATUS status; + struct cli_credentials *creds = samba_cmdline_get_creds(); + + server = talloc_strdup(mem_ctx, share+2); + share = strchr_m(server,'\\'); + if (!share) return NULL; + *share = 0; + share++; + + cli_credentials_set_workstation(creds, + "masktest", CRED_SPECIFIED); + + status = smbcli_full_connection(NULL, &c, + server, + ports, + share, NULL, + socket_options, + creds, + resolve_ctx, ev, + options, session_options, + gensec_settings); + + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return c; +} + +static char *resultp; +static struct { + char *long_name; + char *short_name; +} last_hit; +static bool f_info_hit; + +static void listfn(struct clilist_file_info *f, const char *s, void *state) +{ + struct masktest_state *m = (struct masktest_state *)state; + + if (ISDOT(f->name)) { + resultp[0] = '+'; + } else if (ISDOTDOT(f->name)) { + resultp[1] = '+'; + } else { + resultp[2] = '+'; + } + + last_hit.long_name = talloc_strdup(m->mem_ctx, f->name); + last_hit.short_name = talloc_strdup(m->mem_ctx, f->short_name); + f_info_hit = true; +} + +static void get_real_name(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, + char **long_name, char **short_name) +{ + const char *mask; + struct masktest_state state; + + if (cli->transport->negotiate.protocol <= PROTOCOL_LANMAN1) { + mask = "\\masktest\\*.*"; + } else { + mask = "\\masktest\\*"; + } + + f_info_hit = false; + + state.mem_ctx = mem_ctx; + + smbcli_list_new(cli->tree, mask, + FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, + listfn, &state); + + if (f_info_hit) { + *short_name = strlower_talloc(mem_ctx, last_hit.short_name); + *long_name = strlower_talloc(mem_ctx, last_hit.long_name); + } + + if (*short_name[0] == '\0') { + *short_name = talloc_strdup(mem_ctx, *long_name); + } +} + +static void testpair(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, char *mask, + char *file) +{ + int fnum; + char res1[256]; + char *res2; + static int count; + char *short_name = NULL; + char *long_name = NULL; + struct masktest_state state; + + count++; + + strlcpy(res1, "---", sizeof(res1)); + + state.mem_ctx = mem_ctx; + + fnum = smbcli_open(cli->tree, file, O_CREAT|O_TRUNC|O_RDWR, 0); + if (fnum == -1) { + DEBUG(0,("Can't create %s\n", file)); + return; + } + smbcli_close(cli->tree, fnum); + + resultp = res1; + short_name = talloc_strdup(mem_ctx, ""); + get_real_name(mem_ctx, cli, &long_name, &short_name); + strlcpy(res1, "---", sizeof(res1)); + smbcli_list_new(cli->tree, mask, + FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, + listfn, &state); + + res2 = reg_test(cli, mem_ctx, mask, long_name, short_name); + + if (showall || strcmp(res1, res2)) { + d_printf("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n", + res1, res2, count, mask, file, long_name, short_name); + if (die_on_error) exit(1); + } + + smbcli_unlink(cli->tree, file); + + if (count % 100 == 0) DEBUG(0,("%d\n", count)); + + resultp = NULL; +} + +static void test_mask(int argc, char *argv[], + TALLOC_CTX *mem_ctx, + struct smbcli_state *cli) +{ + char *mask, *file; + int l1, l2, i, l; + int mc_len = strlen(maskchars); + int fc_len = strlen(filechars); + + smbcli_mkdir(cli->tree, "\\masktest"); + + smbcli_unlink_wcard(cli->tree, "\\masktest\\*"); + + if (argc >= 2) { + while (argc >= 2) { + mask = talloc_strdup(mem_ctx, "\\masktest\\"); + file = talloc_strdup(mem_ctx, "\\masktest\\"); + mask = talloc_strdup_append(mask, argv[0]); + file = talloc_strdup_append(file, argv[1]); + testpair(mem_ctx, cli, mask, file); + argv += 2; + argc -= 2; + } + goto finished; + } + + while (1) { + l1 = 1 + random() % max_length; + l2 = 1 + random() % max_length; + mask = talloc_strdup(mem_ctx, "\\masktest\\"); + file = talloc_strdup(mem_ctx, "\\masktest\\"); + mask = talloc_realloc_size(mem_ctx, mask, strlen(mask)+l1+1); + file = talloc_realloc_size(mem_ctx, file, strlen(file)+l2+1); + l = strlen(mask); + for (i=0;itree, "\\masktest"); +} + + +static void usage(poptContext pc) +{ + printf( +"Usage:\n\ + masktest //server/share [options..]\n\ +\n\ + This program tests wildcard matching between two servers. It generates\n\ + random pairs of filenames/masks and tests that they match in the same\n\ + way on the servers and internally\n"); + poptPrintUsage(pc, stdout, 0); +} + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc, const char *argv[]) +{ + char *share; + struct smbcli_state *cli; + int opt; + int seed; + struct tevent_context *ev; + struct loadparm_context *lp_ctx; + struct smbcli_options options; + struct smbcli_session_options session_options; + poptContext pc; + int argc_new, i; + char **argv_new; + TALLOC_CTX *mem_ctx = NULL; + enum {OPT_UNCLIST=1000}; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"seed", 0, POPT_ARG_INT, &seed, 0, "Seed to use for randomizer", NULL}, + {"num-ops", 0, POPT_ARG_INT, &NumLoops, 0, "num ops", NULL}, + {"maxlength", 0, POPT_ARG_INT, &max_length,0, "maximum length", NULL}, + {"dieonerror", 0, POPT_ARG_NONE, &die_on_error, 0, "die on errors", NULL}, + {"showall", 0, POPT_ARG_NONE, &showall, 0, "display all operations", NULL}, + {"oldlist", 0, POPT_ARG_NONE, &old_list, 0, "use old list call", NULL}, + {"maskchars", 0, POPT_ARG_STRING, &maskchars, 0,"mask characters", NULL}, + {"filechars", 0, POPT_ARG_STRING, &filechars, 0,"file characters", NULL}, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_LEGACY_S4 + POPT_TABLEEND + }; + bool ok; + + setlinebuf(stdout); + seed = time(NULL); + + mem_ctx = talloc_named_const(NULL, 0, "masktest_ctx"); + if (mem_ctx == NULL) { + exit(1); + } + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + exit(1); + } + + poptSetOtherOptionHelp(pc, ""); + + lp_ctx = samba_cmdline_get_lp_ctx(); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_UNCLIST: + lpcfg_set_cmdline(lp_ctx, + "torture:unclist", + poptGetOptArg(pc)); + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + argv_new = discard_const_p(char *, poptGetArgs(pc)); + argc_new = argc; + for (i=0; i= 2)) { + usage(pc); + talloc_free(mem_ctx); + exit(1); + } + + setup_logging("masktest", DEBUG_STDOUT); + + share = argv_new[1]; + + all_string_sub(share,"/","\\",0); + + ev = s4_event_context_init(mem_ctx); + + gensec_init(); + + lpcfg_smbcli_options(lp_ctx, &options); + lpcfg_smbcli_session_options(lp_ctx, &session_options); + + cli = connect_one(lpcfg_resolve_context(lp_ctx), ev, mem_ctx, share, + lpcfg_smb_ports(lp_ctx), lpcfg_socket_options(lp_ctx), + &options, &session_options, + lpcfg_gensec_settings(mem_ctx, lp_ctx)); + if (!cli) { + DEBUG(0,("Failed to connect to %s\n", share)); + talloc_free(mem_ctx); + exit(1); + } + + /* need to init seed after connect as clientgen uses random numbers */ + DEBUG(0,("seed=%d format --- --- (server, correct)\n", seed)); + srandom(seed); + + test_mask(argc_new-1, argv_new+1, mem_ctx, cli); + + poptFreeContext(pc); + talloc_free(mem_ctx); + return(0); +} diff --git a/source4/torture/nbench/nbench.c b/source4/torture/nbench/nbench.c new file mode 100644 index 0000000..05ff3ab --- /dev/null +++ b/source4/torture/nbench/nbench.c @@ -0,0 +1,309 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - NBENCH test + Copyright (C) Andrew Tridgell 1997-2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/smbtorture.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "lib/util/smb_strtox.h" + +#include "torture/nbench/proto.h" + +int nbench_line_count = 0; +static int timelimit = 600; +static int warmup; +static const char *loadfile; +static int read_only; + +#define ival(s) strtoll(s, NULL, 0) + +static unsigned long nb_max_retries; + +#define NB_RETRY(op) \ + for (n=0;n<=nb_max_retries && !op;n++) do_reconnect(&cli, tctx, client) + +static void do_reconnect(struct smbcli_state **cli, struct torture_context *tctx, int client) +{ + int n; + printf("[%d] Reconnecting client %d\n", nbench_line_count, client); + for (n=0;n 0) && line[strlen(line)-1] == '\n') { + line[strlen(line)-1] = 0; + } + + all_string_sub(line, "client1", cname, sizeof(line)); + + params = params0 = const_str_list( + str_list_make_shell(NULL, line, " ")); + i = str_list_length(params); + + if (i > 0 && isdigit(params[0][0])) { + double targett = strtod(params[0], NULL); + if (target_rate != 0) { + nbio_target_rate(target_rate); + } else { + nbio_time_delay(targett); + } + params++; + i--; + } else if (target_rate != 0) { + nbio_target_rate(target_rate); + } + + if (i < 2 || params[0][0] == '#') continue; + + if (!strncmp(params[0],"SMB", 3)) { + printf("ERROR: You are using a dbench 1 load file\n"); + nb_exit(1); + } + + if (strncmp(params[i-1], "NT_STATUS_", 10) != 0 && + strncmp(params[i-1], "0x", 2) != 0) { + printf("Badly formed status at line %d\n", nbench_line_count); + talloc_free(params); + continue; + } + + /* accept numeric or string status codes */ + if (strncmp(params[i-1], "0x", 2) == 0) { + tmp = smb_strtoul(params[i-1], + NULL, + 16, + &error, + SMB_STR_STANDARD); + if (error != 0) { + tmp = error; + } + status = NT_STATUS(tmp); + } else { + status = nt_status_string_to_code(params[i-1]); + } + + DEBUG(9,("run_netbench(%d): %s %s\n", client, params[0], params[1])); + + if (!strcmp(params[0],"NTCreateX")) { + NB_RETRY(nb_createx(params[1], ival(params[2]), ival(params[3]), + ival(params[4]), status)); + } else if (!strcmp(params[0],"Close")) { + NB_RETRY(nb_close(ival(params[1]), status)); + } else if (!read_only && !strcmp(params[0],"Rename")) { + NB_RETRY(nb_rename(params[1], params[2], status, n>0)); + } else if (!read_only && !strcmp(params[0],"Unlink")) { + NB_RETRY(nb_unlink(params[1], ival(params[2]), status, n>0)); + } else if (!read_only && !strcmp(params[0],"Deltree")) { + NB_RETRY(nb_deltree(params[1], n>0)); + } else if (!read_only && !strcmp(params[0],"Rmdir")) { + NB_RETRY(nb_rmdir(params[1], status, n>0)); + } else if (!read_only && !strcmp(params[0],"Mkdir")) { + NB_RETRY(nb_mkdir(params[1], status, n>0)); + } else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) { + NB_RETRY(nb_qpathinfo(params[1], ival(params[2]), status)); + } else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) { + NB_RETRY(nb_qfileinfo(ival(params[1]), ival(params[2]), status)); + } else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) { + NB_RETRY(nb_qfsinfo(ival(params[1]), status)); + } else if (!read_only && !strcmp(params[0],"SET_FILE_INFORMATION")) { + NB_RETRY(nb_sfileinfo(ival(params[1]), ival(params[2]), status)); + } else if (!strcmp(params[0],"FIND_FIRST")) { + NB_RETRY(nb_findfirst(params[1], ival(params[2]), + ival(params[3]), ival(params[4]), status)); + } else if (!read_only && !strcmp(params[0],"WriteX")) { + NB_RETRY(nb_writex(ival(params[1]), + ival(params[2]), ival(params[3]), ival(params[4]), + status)); + } else if (!read_only && !strcmp(params[0],"Write")) { + NB_RETRY(nb_write(ival(params[1]), + ival(params[2]), ival(params[3]), ival(params[4]), + status)); + } else if (!strcmp(params[0],"LockX")) { + NB_RETRY(nb_lockx(ival(params[1]), + ival(params[2]), ival(params[3]), status)); + } else if (!strcmp(params[0],"UnlockX")) { + NB_RETRY(nb_unlockx(ival(params[1]), + ival(params[2]), ival(params[3]), status)); + } else if (!strcmp(params[0],"ReadX")) { + NB_RETRY(nb_readx(ival(params[1]), + ival(params[2]), ival(params[3]), ival(params[4]), + status)); + } else if (!strcmp(params[0],"Flush")) { + NB_RETRY(nb_flush(ival(params[1]), status)); + } else if (!strcmp(params[0],"Sleep")) { + nb_sleep(ival(params[1]), status); + } else { + printf("[%d] Unknown operation %s\n", nbench_line_count, params[0]); + } + + if (n > nb_max_retries) { + printf("Maximum reconnect retries reached for op '%s'\n", params[0]); + nb_exit(1); + } + + talloc_free(params0); + + if (nb_tick()) goto done; + } + + rewind(f); + goto again; + +done: + fclose(f); + + if (!read_only && torture_nprocs == 1) { + smbcli_deltree(cli->tree, "\\clients"); + } + if (!torture_close_connection(cli)) { + correct = false; + } + + return correct; +} + + +/* run a test that simulates an approximate netbench client load */ +bool torture_nbench(struct torture_context *torture) +{ + bool correct = true; + int torture_nprocs = torture_setting_int(torture, "nprocs", 4); + struct smbcli_state *cli; + const char *p; + + read_only = torture_setting_bool(torture, "readonly", false); + + nb_max_retries = torture_setting_int(torture, "nretries", 1); + + p = torture_setting_string(torture, "timelimit", NULL); + if (p && *p) { + timelimit = atoi(p); + } + + warmup = timelimit / 20; + + loadfile = torture_setting_string(torture, "loadfile", NULL); + if (!loadfile || !*loadfile) { + loadfile = "client.txt"; + } + + if (torture_nprocs > 1) { + if (!torture_open_connection(&cli, torture, 0)) { + return false; + } + + if (!read_only && !torture_setup_dir(cli, "\\clients")) { + return false; + } + } + + nbio_shmem(torture_nprocs, timelimit, warmup); + + printf("Running for %d seconds with load '%s' and warmup %d secs\n", + timelimit, loadfile, warmup); + + /* we need to reset SIGCHLD here as the name resolution + library may have changed it. We rely on correct signals + from children in the main torture code which reaps + children. This is why smbtorture BENCH-NBENCH was sometimes + failing */ + signal(SIGCHLD, SIG_DFL); + + + signal(SIGALRM, nb_alarm); + alarm(1); + torture_create_procs(torture, run_netbench, &correct); + alarm(0); + + if (!read_only && torture_nprocs > 1) { + smbcli_deltree(cli->tree, "\\clients"); + } + + printf("\nThroughput %g MB/sec\n", nbio_result()); + return correct; +} + +NTSTATUS torture_nbench_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "bench"); + + torture_suite_add_simple_test(suite, "nbench", torture_nbench); + + suite->description = talloc_strdup(suite, "Benchmarks"); + + torture_register_suite(ctx, suite); + return NT_STATUS_OK; +} diff --git a/source4/torture/nbench/nbio.c b/source4/torture/nbench/nbio.c new file mode 100644 index 0000000..1de988e --- /dev/null +++ b/source4/torture/nbench/nbio.c @@ -0,0 +1,994 @@ +/* + TODO: add splitting of writes for servers with signing +*/ + + +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" +#include "../lib/util/dlinklist.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/nbench/proto.h" + +extern int nbench_line_count; +static int nbio_id = -1; +static int nprocs; +static bool bypass_io; +static struct timeval tv_start, tv_end; +static int warmup, timelimit; +static int in_cleanup; + +struct lock_info { + struct lock_info *next, *prev; + off_t offset; + int size; +}; + +struct createx_params { + char *fname; + unsigned int create_options; + unsigned int create_disposition; + int handle; +}; + +struct ftable { + struct ftable *next, *prev; + int fd; /* the fd that we got back from the server */ + int handle; /* the handle in the load file */ + struct createx_params cp; + struct lock_info *locks; +}; + +static struct ftable *ftable; + +static struct { + double bytes, warmup_bytes; + int line; + int done; + bool connected; + double max_latency; + struct timeval starttime; +} *children; + +static bool nb_do_createx(struct ftable *f, + const char *fname, + unsigned int create_options, + unsigned int create_disposition, + int handle, + NTSTATUS status, + bool retry); + +static bool nb_do_lockx(bool relock, int handle, off_t offset, int size, NTSTATUS status); + +static void nb_set_createx_params(struct ftable *f, + const char *fname, + unsigned int create_options, + unsigned int create_disposition, + int handle) +{ + struct createx_params *cp = &f->cp; + + if (fname != NULL) { + cp->fname = talloc_strdup(f, fname); + if (cp->fname == NULL) { + perror("nb_set_createx_params: strdup"); + nb_exit(1); + } + } else { + cp->fname = NULL; + } + + cp->create_options = create_options; + cp->create_disposition = create_disposition; + cp->handle = handle; +} + +static bool nb_reestablish_locks(struct ftable *f) +{ + struct lock_info *linfo = f->locks; + + while (linfo != NULL) { + DEBUG(1,("nb_reestablish_locks: lock for file %d at %lu\n", + f->handle, (unsigned long) linfo->offset)); + + if (!nb_do_lockx(true, f->handle, linfo->offset, linfo->size, NT_STATUS_OK)) { + printf("nb_reestablish_locks: failed to get lock for file %s at %lu\n", + f->cp.fname, (unsigned long) linfo->offset); + return false; + } + + linfo = linfo->next; + } + + return true; +} + +static bool nb_reopen_all_files(void) +{ + struct ftable *f = ftable; + + while (f != NULL) { + DEBUG(1,("-- nb_reopen_all_files: opening %s (handle %d)\n", + f->cp.fname, f->cp.handle)); + + if (!nb_do_createx(f, + f->cp.fname, + f->cp.create_options, + f->cp.create_disposition, + f->cp.handle, + NT_STATUS_OK, + true)) + { + printf("-- nb_reopen_all_files: failed to open file %s\n", f->cp.fname); + return false; + } + + if (!nb_reestablish_locks(f)) { + printf("--nb_reopen_all_files: failed to reestablish locks\n"); + return false; + } + + f = f->next; + } + + return true; +} + +bool nb_reconnect(struct smbcli_state **cli, struct torture_context *tctx, int client) +{ + children[client].connected = false; + + if (*cli != NULL) { + talloc_free(*cli); + } + + if (!torture_open_connection(cli, tctx, client)) { + printf("nb_reconnect: failed to connect\n"); + *cli = NULL; + return false; + } + + nb_setup(*cli, client); + + if (!nb_reopen_all_files()) { + printf("nb_reconnect: failed to reopen files in client %d\n", client); + return false; + } + + return true; +} + +void nbio_target_rate(double rate) +{ + static double last_bytes; + static struct timeval last_time; + double tdelay; + + if (last_bytes == 0) { + last_bytes = children[nbio_id].bytes; + last_time = timeval_current(); + return; + } + + tdelay = (children[nbio_id].bytes - last_bytes)/(1.0e6*rate) - timeval_elapsed(&last_time); + if (tdelay > 0) { + smb_msleep(tdelay*1000); + } else { + children[nbio_id].max_latency = MAX(children[nbio_id].max_latency, -tdelay); + } + + last_time = timeval_current(); + last_bytes = children[nbio_id].bytes; +} + +void nbio_time_reset(void) +{ + children[nbio_id].starttime = timeval_current(); +} + +void nbio_time_delay(double targett) +{ + double elapsed = timeval_elapsed(&children[nbio_id].starttime); + if (targett > elapsed) { + smb_msleep(1000*(targett - elapsed)); + } else if (elapsed - targett > children[nbio_id].max_latency) { + children[nbio_id].max_latency = MAX(elapsed - targett, children[nbio_id].max_latency); + } +} + +double nbio_result(void) +{ + int i; + double total = 0; + for (i=0;i max_latency) { + max_latency = children[i].max_latency; + children[i].max_latency = 0; + } + } + return max_latency; +} + +bool nb_tick(void) +{ + return children[nbio_id].done; +} + + +void nb_alarm(int sig) +{ + int i; + int lines=0; + double t; + int in_warmup = 0; + int num_connected = 0; + + if (nbio_id != -1) return; + + for (i=0;i0 && t > warmup) { + tv_start = timeval_current(); + warmup = 0; + for (i=0;i timelimit) { + for (i=0;ioffset == offset && + linfo->size == size) + { + return linfo; + } + + linfo = linfo->next; + } + + return NULL; +} + +static struct ftable *find_ftable(int handle) +{ + struct ftable *f; + + for (f=ftable;f;f=f->next) { + if (f->handle == handle) return f; + } + return NULL; +} + +static int find_handle(int handle, struct ftable **f_ret) +{ + struct ftable *f; + + if (f_ret != NULL) + *f_ret = NULL; + + children[nbio_id].line = nbench_line_count; + + f = find_ftable(handle); + if (f) { + if (f_ret != NULL) + *f_ret = f; + return f->fd; + } + printf("(%d) ERROR: handle %d was not found\n", + nbench_line_count, handle); + nb_exit(1); + + return -1; /* Not reached */ +} + + + +static struct smbcli_state *c; + +/* + a handler function for oplock break requests +*/ +static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, + uint16_t fnum, uint8_t level, void *private_data) +{ + struct smbcli_tree *tree = (struct smbcli_tree *)private_data; + return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE); +} + +void nb_setup(struct smbcli_state *cli, int id) +{ + nbio_id = id; + c = cli; + if (bypass_io) + printf("skipping I/O\n"); + + if (cli) { + smbcli_oplock_handler(cli->transport, oplock_handler, cli->tree); + } + + children[id].connected = true; +} + + +static bool check_status(const char *op, NTSTATUS status, NTSTATUS ret) +{ + if ((NT_STATUS_EQUAL(ret, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(ret, NT_STATUS_NET_WRITE_FAULT) || + NT_STATUS_EQUAL(ret, NT_STATUS_CONNECTION_RESET)) + && !NT_STATUS_EQUAL (status, ret)) + { + return false; + } + + if (!NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(ret)) { + printf("[%d] Error: %s should have failed with %s\n", + nbench_line_count, op, nt_errstr(status)); + nb_exit(1); + } + + if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ret)) { + printf("[%d] Error: %s should have succeeded - %s\n", + nbench_line_count, op, nt_errstr(ret)); + nb_exit(1); + } + + if (!NT_STATUS_EQUAL(status, ret)) { + printf("[%d] Warning: got status %s but expected %s\n", + nbench_line_count, nt_errstr(ret), nt_errstr(status)); + } + + return true; +} + + +bool nb_unlink(const char *fname, int attr, NTSTATUS status, bool retry) +{ + union smb_unlink io; + NTSTATUS ret; + + io.unlink.in.pattern = fname; + + io.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + if (strchr(fname, '*') == 0) { + io.unlink.in.attrib |= FILE_ATTRIBUTE_DIRECTORY; + } + + ret = smb_raw_unlink(c->tree, &io); + + if (!retry) + return check_status("Unlink", status, ret); + + return true; +} + +static bool nb_do_createx(struct ftable *f, + const char *fname, + unsigned int create_options, + unsigned int create_disposition, + int handle, + NTSTATUS status, + bool retry) +{ + union smb_open io; + uint32_t desired_access; + NTSTATUS ret; + TALLOC_CTX *mem_ctx; + unsigned int flags = 0; + + mem_ctx = talloc_init("raw_open"); + + if (create_options & NTCREATEX_OPTIONS_DIRECTORY) { + desired_access = SEC_FILE_READ_DATA; + } else { + desired_access = + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE; + flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + } + + io.ntcreatex.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = flags; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = desired_access; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = create_disposition; + io.ntcreatex.in.create_options = create_options; + io.ntcreatex.in.impersonation = 0; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + if (retry) { + /* Reopening after a disconnect. */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + } else + if (f != NULL && + f->cp.create_disposition == NTCREATEX_DISP_CREATE && + NT_STATUS_IS_OK(status)) + { + /* Reopening after nb_createx() error. */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + } + + ret = smb_raw_open(c->tree, mem_ctx, &io); + + talloc_free(mem_ctx); + + if (!check_status("NTCreateX", status, ret)) + return false; + + if (!NT_STATUS_IS_OK(ret)) + return true; + + if (f == NULL) { + f = talloc (NULL, struct ftable); + f->locks = NULL; + nb_set_createx_params(f, fname, create_options, create_disposition, handle); + DLIST_ADD_END(ftable, f); + } + + f->handle = handle; + f->fd = io.ntcreatex.out.file.fnum; + + return true; +} + +bool nb_createx(const char *fname, + unsigned int create_options, unsigned int create_disposition, int handle, + NTSTATUS status) +{ + return nb_do_createx(NULL, fname, create_options, create_disposition, handle, status, false); +} + +bool nb_writex(int handle, off_t offset, int size, int ret_size, NTSTATUS status) +{ + union smb_write io; + int i; + NTSTATUS ret; + uint8_t *buf; + + i = find_handle(handle, NULL); + + if (bypass_io) + return true; + + buf = malloc(size); + memset(buf, 0xab, size); + + io.writex.level = RAW_WRITE_WRITEX; + io.writex.in.file.fnum = i; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.offset = offset; + io.writex.in.count = size; + io.writex.in.data = buf; + + ret = smb_raw_write(c->tree, &io); + + free(buf); + + if (!check_status("WriteX", status, ret)) + return false; + + if (NT_STATUS_IS_OK(ret) && io.writex.out.nwritten != ret_size) { + printf("[%d] Warning: WriteX got count %d expected %d\n", + nbench_line_count, + io.writex.out.nwritten, ret_size); + } + + children[nbio_id].bytes += ret_size; + + return true; +} + +bool nb_write(int handle, off_t offset, int size, int ret_size, NTSTATUS status) +{ + union smb_write io; + int i; + NTSTATUS ret; + uint8_t *buf; + + i = find_handle(handle, NULL); + + if (bypass_io) + return true; + + buf = malloc(size); + + memset(buf, 0x12, size); + + io.write.level = RAW_WRITE_WRITE; + io.write.in.file.fnum = i; + io.write.in.remaining = 0; + io.write.in.offset = offset; + io.write.in.count = size; + io.write.in.data = buf; + + ret = smb_raw_write(c->tree, &io); + + free(buf); + + if (!check_status("Write", status, ret)) + return false; + + if (NT_STATUS_IS_OK(ret) && io.write.out.nwritten != ret_size) { + printf("[%d] Warning: Write got count %d expected %d\n", + nbench_line_count, + io.write.out.nwritten, ret_size); + } + + children[nbio_id].bytes += ret_size; + + return true; +} + +static bool nb_do_lockx(bool relock, int handle, off_t offset, int size, NTSTATUS status) +{ + union smb_lock io; + int i; + NTSTATUS ret; + struct smb_lock_entry lck; + struct ftable *f; + + i = find_handle(handle, &f); + + lck.pid = getpid(); + lck.offset = offset; + lck.count = size; + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = i; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lck; + + ret = smb_raw_lock(c->tree, &io); + + if (!check_status("LockX", status, ret)) + return false; + + if (f != NULL && + !relock) + { + struct lock_info *linfo; + linfo = talloc (f, struct lock_info); + linfo->offset = offset; + linfo->size = size; + DLIST_ADD_END(f->locks, linfo); + } + + return true; +} + +bool nb_lockx(int handle, off_t offset, int size, NTSTATUS status) +{ + return nb_do_lockx(false, handle, offset, size, status); +} + +bool nb_unlockx(int handle, unsigned int offset, int size, NTSTATUS status) +{ + union smb_lock io; + int i; + NTSTATUS ret; + struct smb_lock_entry lck; + struct ftable *f; + + i = find_handle(handle, &f); + + lck.pid = getpid(); + lck.offset = offset; + lck.count = size; + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = i; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lck; + + ret = smb_raw_lock(c->tree, &io); + + if (!check_status("UnlockX", status, ret)) + return false; + + if (f != NULL) { + struct lock_info *linfo; + linfo = find_lock(f->locks, offset, size); + if (linfo != NULL) + DLIST_REMOVE(f->locks, linfo); + else + printf("nb_unlockx: unknown lock (%d)\n", handle); + } + + return true; +} + +bool nb_readx(int handle, off_t offset, int size, int ret_size, NTSTATUS status) +{ + union smb_read io; + int i; + NTSTATUS ret; + uint8_t *buf; + + i = find_handle(handle, NULL); + + if (bypass_io) + return true; + + buf = malloc(size); + + io.readx.level = RAW_READ_READX; + io.readx.in.file.fnum = i; + io.readx.in.offset = offset; + io.readx.in.mincnt = size; + io.readx.in.maxcnt = size; + io.readx.in.remaining = 0; + io.readx.in.read_for_execute = false; + io.readx.out.data = buf; + + ret = smb_raw_read(c->tree, &io); + + free(buf); + + if (!check_status("ReadX", status, ret)) + return false; + + if (NT_STATUS_IS_OK(ret) && io.readx.out.nread != ret_size) { + printf("[%d] ERROR: ReadX got count %d expected %d\n", + nbench_line_count, + io.readx.out.nread, ret_size); + nb_exit(1); + } + + children[nbio_id].bytes += ret_size; + + return true; +} + +bool nb_close(int handle, NTSTATUS status) +{ + NTSTATUS ret; + union smb_close io; + int i; + + i = find_handle(handle, NULL); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.file.fnum = i; + io.close.in.write_time = 0; + + ret = smb_raw_close(c->tree, &io); + + if (!check_status("Close", status, ret)) + return false; + + if (NT_STATUS_IS_OK(ret)) { + struct ftable *f = find_ftable(handle); + DLIST_REMOVE(ftable, f); + talloc_free(f); + } + + return true; +} + +bool nb_rmdir(const char *dname, NTSTATUS status, bool retry) +{ + NTSTATUS ret; + struct smb_rmdir io; + + io.in.path = dname; + + ret = smb_raw_rmdir(c->tree, &io); + + if (!retry) + return check_status("Rmdir", status, ret); + + return true; +} + +bool nb_mkdir(const char *dname, NTSTATUS status, bool retry) +{ + union smb_mkdir io; + + io.mkdir.level = RAW_MKDIR_MKDIR; + io.mkdir.in.path = dname; + + /* NOTE! no error checking. Used for base fileset creation */ + smb_raw_mkdir(c->tree, &io); + + return true; +} + +bool nb_rename(const char *o, const char *n, NTSTATUS status, bool retry) +{ + NTSTATUS ret; + union smb_rename io; + + io.generic.level = RAW_RENAME_RENAME; + io.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + io.rename.in.pattern1 = o; + io.rename.in.pattern2 = n; + + ret = smb_raw_rename(c->tree, &io); + + if (!retry) + return check_status("Rename", status, ret); + + return true; +} + + +bool nb_qpathinfo(const char *fname, int level, NTSTATUS status) +{ + union smb_fileinfo io; + TALLOC_CTX *mem_ctx; + NTSTATUS ret; + + mem_ctx = talloc_init("nb_qpathinfo"); + + io.generic.level = level; + io.generic.in.file.path = fname; + + ret = smb_raw_pathinfo(c->tree, mem_ctx, &io); + + talloc_free(mem_ctx); + + return check_status("Pathinfo", status, ret); +} + + +bool nb_qfileinfo(int fnum, int level, NTSTATUS status) +{ + union smb_fileinfo io; + TALLOC_CTX *mem_ctx; + NTSTATUS ret; + int i; + + i = find_handle(fnum, NULL); + + mem_ctx = talloc_init("nb_qfileinfo"); + + io.generic.level = level; + io.generic.in.file.fnum = i; + + ret = smb_raw_fileinfo(c->tree, mem_ctx, &io); + + talloc_free(mem_ctx); + + return check_status("Fileinfo", status, ret); +} + +bool nb_sfileinfo(int fnum, int level, NTSTATUS status) +{ + union smb_setfileinfo io; + NTSTATUS ret; + int i; + + if (level != RAW_SFILEINFO_BASIC_INFORMATION) { + printf("[%d] Warning: setfileinfo level %d not handled\n", nbench_line_count, level); + return true; + } + + ZERO_STRUCT(io); + + i = find_handle(fnum, NULL); + + io.generic.level = level; + io.generic.in.file.fnum = i; + unix_to_nt_time(&io.basic_info.in.create_time, time(NULL)); + unix_to_nt_time(&io.basic_info.in.access_time, 0); + unix_to_nt_time(&io.basic_info.in.write_time, 0); + unix_to_nt_time(&io.basic_info.in.change_time, 0); + io.basic_info.in.attrib = 0; + + ret = smb_raw_setfileinfo(c->tree, &io); + + return check_status("Setfileinfo", status, ret); +} + +bool nb_qfsinfo(int level, NTSTATUS status) +{ + union smb_fsinfo io; + TALLOC_CTX *mem_ctx; + NTSTATUS ret; + + mem_ctx = talloc_init("smbcli_dskattr"); + + io.generic.level = level; + ret = smb_raw_fsinfo(c->tree, mem_ctx, &io); + + talloc_free(mem_ctx); + + return check_status("Fsinfo", status, ret); +} + +/* callback function used for trans2 search */ +static bool findfirst_callback(void *private_data, const union smb_search_data *file) +{ + return true; +} + +bool nb_findfirst(const char *mask, int level, int maxcnt, int count, NTSTATUS status) +{ + union smb_search_first io; + TALLOC_CTX *mem_ctx; + NTSTATUS ret; + + mem_ctx = talloc_init("smbcli_dskattr"); + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = level; + io.t2ffirst.in.max_count = maxcnt; + io.t2ffirst.in.search_attrib = FILE_ATTRIBUTE_DIRECTORY; + io.t2ffirst.in.pattern = mask; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + io.t2ffirst.in.storage_type = 0; + + ret = smb_raw_search_first(c->tree, mem_ctx, &io, NULL, findfirst_callback); + + talloc_free(mem_ctx); + + if (!check_status("Search", status, ret)) + return false; + + if (NT_STATUS_IS_OK(ret) && io.t2ffirst.out.count != count) { + printf("[%d] Warning: got count %d expected %d\n", + nbench_line_count, + io.t2ffirst.out.count, count); + } + + return true; +} + +bool nb_flush(int fnum, NTSTATUS status) +{ + union smb_flush io; + NTSTATUS ret; + int i; + i = find_handle(fnum, NULL); + + io.flush.level = RAW_FLUSH_FLUSH; + io.flush.in.file.fnum = i; + + ret = smb_raw_flush(c->tree, &io); + + return check_status("Flush", status, ret); +} + +void nb_sleep(int usec, NTSTATUS status) +{ + usleep(usec); +} + +bool nb_deltree(const char *dname, bool retry) +{ + int total_deleted; + + smb_raw_exit(c->session); + + while (ftable) { + struct ftable *f = ftable; + DLIST_REMOVE(ftable, f); + talloc_free (f); + } + + total_deleted = smbcli_deltree(c->tree, dname); + + if (total_deleted == -1) { + printf("Failed to cleanup tree %s - exiting\n", dname); + nb_exit(1); + } + + smbcli_rmdir(c->tree, dname); + + return true; +} + + +void nb_exit(int status) +{ + children[nbio_id].connected = false; + printf("[%d] client %d exiting with status %d\n", + nbench_line_count, nbio_id, status); + exit(status); +} diff --git a/source4/torture/nbt/dgram.c b/source4/torture/nbt/dgram.c new file mode 100644 index 0000000..2f7ea19 --- /dev/null +++ b/source4/torture/nbt/dgram.c @@ -0,0 +1,699 @@ +/* + Unix SMB/CIFS implementation. + + NBT dgram testing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/dgram/libdgram.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/nbt/proto.h" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "param/param.h" + +#define TEST_NAME "TORTURE_TEST" + +/* + reply handler for netlogon request +*/ +static void netlogon_handler(struct dgram_mailslot_handler *dgmslot, + struct nbt_dgram_packet *packet, + struct socket_address *src) +{ + NTSTATUS status; + struct nbt_netlogon_response *netlogon = dgmslot->private_data; + + dgmslot->private_data = netlogon = talloc(dgmslot, struct nbt_netlogon_response); + + if (!dgmslot->private_data) { + return; + } + + printf("netlogon reply from %s:%d\n", src->addr, src->port); + + /* Fills in the netlogon pointer */ + status = dgram_mailslot_netlogon_parse_response(netlogon, packet, + netlogon); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to parse netlogon packet from %s:%d\n", + src->addr, src->port); + return; + } + +} + + +/* test UDP/138 netlogon requests */ +static bool nbt_test_netlogon(struct torture_context *tctx) +{ + struct dgram_mailslot_handler *dgmslot; + struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev); + struct socket_address *dest; + const char *myaddress; + struct nbt_netlogon_packet logon; + struct nbt_netlogon_response *response; + struct nbt_name myname; + NTSTATUS status; + struct timeval tv = timeval_current(); + + struct socket_address *socket_address; + + const char *address; + struct nbt_name name; + + struct interface *ifaces; + + name.name = lpcfg_workgroup(tctx->lp_ctx); + name.type = NBT_NAME_LOGON; + name.scope = NULL; + + /* do an initial name resolution to find its IP */ + torture_assert_ntstatus_ok(tctx, + resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, + &name, tctx, &address, tctx->ev), + talloc_asprintf(tctx, "Failed to resolve %s", name.name)); + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address)); + + + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + /* try receiving replies on port 138 first, which will only + work if we are root and smbd/nmbd are not running - fall + back to listening on any port, which means replies from + most windows versions won't be seen */ + status = socket_listen(dgmsock->sock, socket_address, 0, 0); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(socket_address); + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + socket_listen(dgmsock->sock, socket_address, 0, 0); + } + + /* setup a temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_PRIMARY_QUERY; + logon.req.pdc.computer_name = TEST_NAME; + logon.req.pdc.mailslot_name = dgmslot->mailslot_name; + logon.req.pdc.unicode_name = TEST_NAME; + logon.req.pdc.nt_version = 1; + logon.req.pdc.lmnt_token = 0xFFFF; + logon.req.pdc.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, dest != NULL, "Error getting address"); + + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert(tctx, response->response_type == NETLOGON_GET_PDC, "Got incorrect type of netlogon response"); + torture_assert(tctx, response->data.get_pdc.command == NETLOGON_RESPONSE_FROM_PDC, "Got incorrect netlogon response command"); + + return true; +} + + +/* test UDP/138 netlogon requests */ +static bool nbt_test_netlogon2(struct torture_context *tctx) +{ + struct dgram_mailslot_handler *dgmslot; + struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev); + struct socket_address *dest; + const char *myaddress; + struct nbt_netlogon_packet logon; + struct nbt_netlogon_response *response; + struct nbt_name myname; + NTSTATUS status; + struct timeval tv = timeval_current(); + + struct socket_address *socket_address; + + const char *address; + struct nbt_name name; + + struct interface *ifaces; + struct test_join *join_ctx; + struct cli_credentials *machine_credentials; + const struct dom_sid *dom_sid; + + name.name = lpcfg_workgroup(tctx->lp_ctx); + name.type = NBT_NAME_LOGON; + name.scope = NULL; + + /* do an initial name resolution to find its IP */ + torture_assert_ntstatus_ok(tctx, + resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, + &name, tctx, &address, tctx->ev), + talloc_asprintf(tctx, "Failed to resolve %s", name.name)); + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address)); + + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + /* try receiving replies on port 138 first, which will only + work if we are root and smbd/nmbd are not running - fall + back to listening on any port, which means replies from + some windows versions won't be seen */ + status = socket_listen(dgmsock->sock, socket_address, 0, 0); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(socket_address); + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + socket_listen(dgmsock->sock, socket_address, 0, 0); + } + + /* setup a temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = ""; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.nt_version = NETLOGON_NT_VERSION_5EX_WITH_IP|NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX, "Got incorrect netlogon response command"); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.nt_version, NETLOGON_NT_VERSION_5EX_WITH_IP|NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_1, "Got incorrect netlogon response command"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") == NULL, + "PDC name should not be in UNC form"); + + /* setup (another) temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command"); + + torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + join_ctx = torture_join_domain(tctx, TEST_NAME, + ACB_WSTRUST, &machine_credentials); + + torture_assert(tctx, join_ctx != NULL, + talloc_asprintf(tctx, "Failed to join domain %s as %s\n", + lpcfg_workgroup(tctx->lp_ctx), TEST_NAME)); + + dom_sid = torture_join_sid(join_ctx); + + /* setup (another) temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.sid = *dom_sid; + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + /* setup (another) temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error getting a Mailslot for GetDC reply"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.sid = *dom_sid; + logon.req.logon.acct_control = ACB_WSTRUST; + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + dgmslot->private_data = NULL; + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.sid = *dom_sid; + logon.req.logon.acct_control = ACB_NORMAL; + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, &name, dest, + NBT_MAILSLOT_NETLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request"); + + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + torture_leave_domain(tctx, join_ctx); + return true; +} + + +/* test UDP/138 ntlogon requests */ +static bool nbt_test_ntlogon(struct torture_context *tctx) +{ + struct dgram_mailslot_handler *dgmslot; + struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev); + struct socket_address *dest; + struct test_join *join_ctx; + const struct dom_sid *dom_sid; + struct cli_credentials *machine_credentials; + + const char *myaddress; + struct nbt_netlogon_packet logon; + struct nbt_netlogon_response *response; + struct nbt_name myname; + NTSTATUS status; + struct timeval tv = timeval_current(); + + struct socket_address *socket_address; + const char *address; + struct nbt_name name; + + struct interface *ifaces; + + name.name = lpcfg_workgroup(tctx->lp_ctx); + name.type = NBT_NAME_LOGON; + name.scope = NULL; + + /* do an initial name resolution to find its IP */ + torture_assert_ntstatus_ok(tctx, + resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, &name, tctx, &address, tctx->ev), + talloc_asprintf(tctx, "Failed to resolve %s", name.name)); + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address)); + + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + /* try receiving replies on port 138 first, which will only + work if we are root and smbd/nmbd are not running - fall + back to listening on any port, which means replies from + most windows versions won't be seen */ + status = socket_listen(dgmsock->sock, socket_address, 0, 0); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(socket_address); + socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, "Error getting address"); + + socket_listen(dgmsock->sock, socket_address, 0, 0); + } + + join_ctx = torture_join_domain(tctx, TEST_NAME, + ACB_WSTRUST, &machine_credentials); + + torture_assert(tctx, join_ctx != NULL, + talloc_asprintf(tctx, "Failed to join domain %s as %s\n", + lpcfg_workgroup(tctx->lp_ctx), TEST_NAME)); + dom_sid = torture_join_sid(join_ctx); + + /* setup a temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.acct_control = ACB_WSTRUST; + /* Try with a SID this time */ + logon.req.logon.sid = *dom_sid; + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, + &name, dest, + NBT_MAILSLOT_NTLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request"); + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command"); + + torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + /* setup a temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_SAM_LOGON_REQUEST; + logon.req.logon.request_count = 0; + logon.req.logon.computer_name = TEST_NAME; + logon.req.logon.user_name = TEST_NAME"$"; + logon.req.logon.mailslot_name = dgmslot->mailslot_name; + logon.req.logon.acct_control = ACB_WSTRUST; + /* Leave sid as all zero */ + logon.req.logon.nt_version = 1; + logon.req.logon.lmnt_token = 0xFFFF; + logon.req.logon.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, + &name, dest, + NBT_MAILSLOT_NTLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request"); + + while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response"); + map_netlogon_samlogon_response(&response->data.samlogon); + + torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command"); + + torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response"); + + torture_assert(tctx, + strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL, + "PDC name should be in UNC form"); + + /* setup (another) temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_PRIMARY_QUERY; + logon.req.pdc.computer_name = TEST_NAME; + logon.req.pdc.mailslot_name = dgmslot->mailslot_name; + logon.req.pdc.unicode_name = TEST_NAME; + logon.req.pdc.nt_version = 1; + logon.req.pdc.lmnt_token = 0xFFFF; + logon.req.pdc.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, + &name, dest, + NBT_MAILSLOT_NTLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request"); + + while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response"); + torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command"); + + torture_leave_domain(tctx, join_ctx); + + /* setup (another) temporary mailslot listener for replies */ + dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC, + netlogon_handler, NULL); + torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC"); + + ZERO_STRUCT(logon); + logon.command = LOGON_PRIMARY_QUERY; + logon.req.pdc.computer_name = TEST_NAME; + logon.req.pdc.mailslot_name = dgmslot->mailslot_name; + logon.req.pdc.unicode_name = TEST_NAME; + logon.req.pdc.nt_version = 1; + logon.req.pdc.lmnt_token = 0xFFFF; + logon.req.pdc.lm20_token = 0xFFFF; + + make_nbt_name_client(&myname, TEST_NAME); + + dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, + address, lpcfg_dgram_port(tctx->lp_ctx)); + torture_assert(tctx, dest != NULL, "Error getting address"); + status = dgram_mailslot_netlogon_send(dgmsock, + &name, dest, + NBT_MAILSLOT_NTLOGON, + &myname, &logon); + torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request"); + + while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) { + tevent_loop_once(dgmsock->event_ctx); + } + + response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response); + + torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet"); + + torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response"); + torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command"); + + + return true; +} + + +/* + test nbt dgram operations +*/ +struct torture_suite *torture_nbt_dgram(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "dgram"); + + torture_suite_add_simple_test(suite, "netlogon", nbt_test_netlogon); + torture_suite_add_simple_test(suite, "netlogon2", nbt_test_netlogon2); + torture_suite_add_simple_test(suite, "ntlogon", nbt_test_ntlogon); + + return suite; +} diff --git a/source4/torture/nbt/nbt.c b/source4/torture/nbt/nbt.c new file mode 100644 index 0000000..f350885 --- /dev/null +++ b/source4/torture/nbt/nbt.c @@ -0,0 +1,69 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "../libcli/nbt/libnbt.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "torture/smbtorture.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct nbt_name_socket *torture_init_nbt_socket(struct torture_context *tctx) +{ + return nbt_name_socket_init(tctx, tctx->ev); +} + +bool torture_nbt_get_name(struct torture_context *tctx, + struct nbt_name *name, + const char **address) +{ + make_nbt_name_server(name, strupper_talloc(tctx, + torture_setting_string(tctx, "host", NULL))); + + /* do an initial name resolution to find its IP */ + torture_assert_ntstatus_ok(tctx, + resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, + name, tctx, address, tctx->ev), + talloc_asprintf(tctx, + "Failed to resolve %s", name->name)); + + return true; +} + +NTSTATUS torture_nbt_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "nbt"); + /* nbt tests */ + torture_suite_add_suite(suite, torture_nbt_register(suite)); + torture_suite_add_suite(suite, torture_nbt_wins(suite)); + torture_suite_add_suite(suite, torture_nbt_dgram(suite)); + torture_suite_add_suite(suite, torture_nbt_winsreplication(suite)); + torture_suite_add_suite(suite, torture_bench_nbt(suite)); + torture_suite_add_suite(suite, torture_bench_wins(suite)); + + suite->description = talloc_strdup(suite, + "NetBIOS over TCP/IP and WINS tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/nbt/query.c b/source4/torture/nbt/query.c new file mode 100644 index 0000000..001ff19 --- /dev/null +++ b/source4/torture/nbt/query.c @@ -0,0 +1,115 @@ +/* + Unix SMB/CIFS implementation. + + NBT name query testing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "param/param.h" + +struct result_struct { + int num_pass; + int num_fail; +}; + +static void increment_handler(struct nbt_name_request *req) +{ + struct result_struct *v = talloc_get_type(req->async.private_data, struct result_struct); + if (req->state != NBT_REQUEST_DONE) { + v->num_fail++; + } else { + v->num_pass++; + } + talloc_free(req); +} + +/* + benchmark simple name queries +*/ +static bool bench_namequery(struct torture_context *tctx) +{ + struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx); + int num_sent=0; + struct result_struct *result; + struct nbt_name_query io; + struct timeval tv = timeval_current(); + int timelimit = torture_setting_int(tctx, "timelimit", 5); + + const char *address; + struct nbt_name name; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + io.in.name = name; + io.in.dest_addr = address; + io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + io.in.broadcast = false; + io.in.wins_lookup = false; + io.in.timeout = 1; + + result = talloc_zero(tctx, struct result_struct); + + torture_comment(tctx, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + while (num_sent - (result->num_pass+result->num_fail) < 10) { + struct nbt_name_request *req; + req = nbt_name_query_send(nbtsock, &io); + torture_assert(tctx, req != NULL, "Failed to setup request!"); + req->async.fn = increment_handler; + req->async.private_data = result; + num_sent++; + if (num_sent % 1000 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%.1f queries per second (%d failures) \r", + result->num_pass / timeval_elapsed(&tv), + result->num_fail); + fflush(stdout); + } + } + } + + tevent_loop_once(nbtsock->event_ctx); + } + + while (num_sent != (result->num_pass + result->num_fail)) { + tevent_loop_once(nbtsock->event_ctx); + } + + torture_comment(tctx, "%.1f queries per second (%d failures) \n", + result->num_pass / timeval_elapsed(&tv), + result->num_fail); + + return true; +} + + +/* + benchmark how fast a server can respond to name queries +*/ +struct torture_suite *torture_bench_nbt(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "bench"); + torture_suite_add_simple_test(suite, "namequery", bench_namequery); + + return suite; +} diff --git a/source4/torture/nbt/register.c b/source4/torture/nbt/register.c new file mode 100644 index 0000000..24ca328 --- /dev/null +++ b/source4/torture/nbt/register.c @@ -0,0 +1,176 @@ +/* + Unix SMB/CIFS implementation. + + NBT name registration testing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "param/param.h" + +#define CHECK_VALUE(tctx, v, correct) \ + torture_assert_int_equal(tctx, v, correct, "Incorrect value") + +#define CHECK_STRING(tctx, v, correct) \ + torture_assert_casestr_equal(tctx, v, correct, "Incorrect value") + + + + +/* + test that a server responds correctly to attempted registrations of its name +*/ +static bool nbt_register_own(struct torture_context *tctx) +{ + struct nbt_name_register io; + NTSTATUS status; + struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx); + struct socket_address *socket_address; + struct nbt_name name; + const char *address; + const char *myaddress; + struct interface *ifaces; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + + myaddress = iface_list_best_ip(ifaces, address); + + socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, "Unable to get address"); + + status = socket_listen(nbtsock->sock, socket_address, 0, 0); + torture_assert_ntstatus_ok(tctx, status, + "socket_listen for nbt_register_own failed"); + + torture_comment(tctx, "Testing name defense to name registration\n"); + + io.in.name = name; + io.in.dest_addr = address; + io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + io.in.address = myaddress; + io.in.nb_flags = NBT_NODE_B | NBT_NM_ACTIVE; + io.in.register_demand = false; + io.in.broadcast = true; + io.in.multi_homed = false; + io.in.ttl = 1234; + io.in.timeout = 3; + io.in.retries = 0; + + status = nbt_name_register(nbtsock, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name register", + address)); + + CHECK_STRING(tctx, io.out.name.name, name.name); + CHECK_VALUE(tctx, io.out.name.type, name.type); + CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT); + + /* check a register demand */ + io.in.address = myaddress; + io.in.register_demand = true; + + status = nbt_name_register(nbtsock, tctx, &io); + + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name register demand", address)); + + CHECK_STRING(tctx, io.out.name.name, name.name); + CHECK_VALUE(tctx, io.out.name.type, name.type); + CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT); + + return true; +} + + +/* + test that a server responds correctly to attempted name refresh requests +*/ +static bool nbt_refresh_own(struct torture_context *tctx) +{ + struct nbt_name_refresh io; + NTSTATUS status; + struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx); + const char *myaddress; + struct socket_address *socket_address; + struct nbt_name name; + const char *address; + struct interface *ifaces; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + + myaddress = iface_list_best_ip(ifaces, address); + + socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, + "Can't parse socket address"); + + status = socket_listen(nbtsock->sock, socket_address, 0, 0); + torture_assert_ntstatus_ok(tctx, status, + "socket_listen for nbt_referesh_own failed"); + + torture_comment(tctx, "Testing name defense to name refresh\n"); + + io.in.name = name; + io.in.dest_addr = address; + io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + io.in.address = myaddress; + io.in.nb_flags = NBT_NODE_B | NBT_NM_ACTIVE; + io.in.broadcast = false; + io.in.ttl = 1234; + io.in.timeout = 3; + io.in.retries = 0; + + status = nbt_name_refresh(nbtsock, tctx, &io); + + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name refresh", address)); + + CHECK_STRING(tctx, io.out.name.name, name.name); + CHECK_VALUE(tctx, io.out.name.type, name.type); + CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT); + + return true; +} + + +/* + test name registration to a server +*/ +struct torture_suite *torture_nbt_register(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(mem_ctx, "register"); + torture_suite_add_simple_test(suite, "register_own", nbt_register_own); + torture_suite_add_simple_test(suite, "refresh_own", nbt_refresh_own); + + return suite; +} diff --git a/source4/torture/nbt/wins.c b/source4/torture/nbt/wins.c new file mode 100644 index 0000000..8c847b5 --- /dev/null +++ b/source4/torture/nbt/wins.c @@ -0,0 +1,545 @@ +/* + Unix SMB/CIFS implementation. + + NBT WINS server testing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "param/param.h" + +#define CHECK_VALUE(tctx, v, correct) \ + torture_assert_int_equal(tctx, v, correct, "Incorrect value") + +#define CHECK_STRING(tctx, v, correct) \ + torture_assert_casestr_equal(tctx, v, correct, "Incorrect value") + +#define CHECK_NAME(tctx, _name, correct) do { \ + CHECK_STRING(tctx, (_name).name, (correct).name); \ + CHECK_VALUE(tctx, (uint8_t)(_name).type, (uint8_t)(correct).type); \ + CHECK_STRING(tctx, (_name).scope, (correct).scope); \ +} while (0) + + +/* + test operations against a WINS server +*/ +static bool nbt_test_wins_name(struct torture_context *tctx, const char *address, + struct nbt_name *name, uint16_t nb_flags, + bool try_low_port, + uint8_t register_rcode) +{ + struct nbt_name_register_wins io; + struct nbt_name_register name_register; + struct nbt_name_query query; + struct nbt_name_refresh_wins refresh; + struct nbt_name_release release; + struct nbt_name_request *req; + NTSTATUS status; + struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx); + const char *myaddress; + struct socket_address *socket_address; + struct interface *ifaces; + bool low_port = try_low_port; + char **l; + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + + myaddress = talloc_strdup(tctx, iface_list_best_ip(ifaces, address)); + + socket_address = socket_address_from_strings(tctx, + nbtsock->sock->backend_name, + myaddress, lpcfg_nbt_port(tctx->lp_ctx)); + torture_assert(tctx, socket_address != NULL, + "Error getting address"); + + /* we do the listen here to ensure the WINS server receives the packets from + the right IP */ + status = socket_listen(nbtsock->sock, socket_address, 0, 0); + talloc_free(socket_address); + if (!NT_STATUS_IS_OK(status)) { + low_port = false; + socket_address = socket_address_from_strings(tctx, + nbtsock->sock->backend_name, + myaddress, 0); + torture_assert(tctx, socket_address != NULL, + "Error getting address"); + + status = socket_listen(nbtsock->sock, socket_address, 0, 0); + talloc_free(socket_address); + torture_assert_ntstatus_ok(tctx, status, + "socket_listen for WINS failed"); + } + + torture_comment(tctx, "Testing name registration to WINS with name %s at %s nb_flags=0x%x\n", + nbt_name_string(tctx, name), myaddress, nb_flags); + + torture_comment(tctx, "release the name\n"); + release.in.name = *name; + release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release.in.dest_addr = address; + release.in.address = myaddress; + release.in.nb_flags = nb_flags; + release.in.broadcast = false; + release.in.timeout = 3; + release.in.retries = 0; + + status = nbt_name_release(nbtsock, tctx, &release); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address)); + CHECK_VALUE(tctx, release.out.rcode, 0); + + if (nb_flags & NBT_NM_GROUP) { + /* ignore this for group names */ + } else if (!low_port) { + torture_comment(tctx, "no low port - skip: register the name with a wrong address\n"); + } else { + torture_comment(tctx, "register the name with a wrong address (makes the next request slow!)\n"); + io.in.name = *name; + io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx); + io.in.wins_servers = const_str_list( + str_list_make_single(tctx, address)); + io.in.addresses = const_str_list( + str_list_make_single(tctx, "127.64.64.1")); + io.in.nb_flags = nb_flags; + io.in.ttl = 300000; + + status = nbt_name_register_wins(nbtsock, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name register\n", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name register\n", + address)); + + CHECK_STRING(tctx, io.out.wins_server, address); + CHECK_VALUE(tctx, io.out.rcode, 0); + + torture_comment(tctx, "register the name correct address\n"); + name_register.in.name = *name; + name_register.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + name_register.in.dest_addr = address; + name_register.in.address = myaddress; + name_register.in.nb_flags = nb_flags; + name_register.in.register_demand= false; + name_register.in.broadcast = false; + name_register.in.multi_homed = true; + name_register.in.ttl = 300000; + name_register.in.timeout = 3; + name_register.in.retries = 2; + + /* + * test if the server ignores resent requests + */ + req = nbt_name_register_send(nbtsock, &name_register); + while (true) { + tevent_loop_once(nbtsock->event_ctx); + if (req->state != NBT_REQUEST_WAIT) { + break; + } + if (req->received_wack) { + /* + * if we received the wack response + * we resend the request and the + * server should ignore that + * and not handle it as new request + */ + req->state = NBT_REQUEST_SEND; + DLIST_ADD_END(nbtsock->send_queue, req); + TEVENT_FD_WRITEABLE(nbtsock->fde); + break; + } + } + + status = nbt_name_register_recv(req, tctx, &name_register); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name register\n", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name register\n", + address)); + + CHECK_VALUE(tctx, name_register.out.rcode, 0); + CHECK_STRING(tctx, name_register.out.reply_addr, myaddress); + } + + torture_comment(tctx, "register the name correct address\n"); + io.in.name = *name; + io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx); + l = str_list_make_single(tctx, address); + io.in.wins_servers = discard_const_p(const char *, l); + l = str_list_make_single(tctx, myaddress); + io.in.addresses = discard_const_p(const char *, l); + io.in.nb_flags = nb_flags; + io.in.ttl = 300000; + + status = nbt_name_register_wins(nbtsock, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register", address)); + + CHECK_STRING(tctx, io.out.wins_server, address); + CHECK_VALUE(tctx, io.out.rcode, register_rcode); + + if (register_rcode != NBT_RCODE_OK) { + return true; + } + + if (name->type != NBT_NAME_MASTER && + name->type != NBT_NAME_LOGON && + name->type != NBT_NAME_BROWSER && + (nb_flags & NBT_NM_GROUP)) { + torture_comment(tctx, "Try to register as non-group\n"); + io.in.nb_flags &= ~NBT_NM_GROUP; + status = nbt_name_register_wins(nbtsock, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register\n", + address)); + CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT); + } + + torture_comment(tctx, "query the name to make sure its there\n"); + query.in.name = *name; + query.in.dest_addr = address; + query.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + query.in.broadcast = false; + query.in.wins_lookup = true; + query.in.timeout = 3; + query.in.retries = 0; + + status = nbt_name_query(nbtsock, tctx, &query); + if (name->type == NBT_NAME_MASTER) { + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + talloc_asprintf(tctx, "Bad response from %s for name query", address)); + return true; + } + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address)); + + CHECK_NAME(tctx, query.out.name, *name); + CHECK_VALUE(tctx, query.out.num_addrs, 1); + if (name->type != NBT_NAME_LOGON && + (nb_flags & NBT_NM_GROUP)) { + CHECK_STRING(tctx, query.out.reply_addrs[0], "255.255.255.255"); + } else { + CHECK_STRING(tctx, query.out.reply_addrs[0], myaddress); + } + + + query.in.name.name = strupper_talloc(tctx, name->name); + if (query.in.name.name && + strcmp(query.in.name.name, name->name) != 0) { + torture_comment(tctx, "check case sensitivity\n"); + status = nbt_name_query(nbtsock, tctx, &query); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, talloc_asprintf(tctx, "Bad response from %s for name query", address)); + } + + query.in.name = *name; + if (name->scope) { + query.in.name.scope = strupper_talloc(tctx, name->scope); + } + if (query.in.name.scope && + strcmp(query.in.name.scope, name->scope) != 0) { + torture_comment(tctx, "check case sensitivity on scope\n"); + status = nbt_name_query(nbtsock, tctx, &query); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, talloc_asprintf(tctx, "Bad response from %s for name query", address)); + } + + torture_comment(tctx, "refresh the name\n"); + refresh.in.name = *name; + refresh.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx); + l = str_list_make_single(tctx, address); + refresh.in.wins_servers = discard_const_p(const char *, l); + l = str_list_make_single(tctx, myaddress); + refresh.in.addresses = discard_const_p(const char *, l); + refresh.in.nb_flags = nb_flags; + refresh.in.ttl = 12345; + + status = nbt_name_refresh_wins(nbtsock, tctx, &refresh); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name refresh", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name refresh", + address)); + + CHECK_STRING(tctx, refresh.out.wins_server, address); + CHECK_VALUE(tctx, refresh.out.rcode, 0); + + printf("release the name\n"); + release.in.name = *name; + release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release.in.dest_addr = address; + release.in.address = myaddress; + release.in.nb_flags = nb_flags; + release.in.broadcast = false; + release.in.timeout = 3; + release.in.retries = 0; + + status = nbt_name_release(nbtsock, tctx, &release); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name release", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name release", + address)); + + CHECK_NAME(tctx, release.out.name, *name); + CHECK_VALUE(tctx, release.out.rcode, 0); + + if (nb_flags & NBT_NM_GROUP) { + /* ignore this for group names */ + } else if (!low_port) { + torture_comment(tctx, "no low port - skip: register the name with a wrong address\n"); + } else { + torture_comment(tctx, "register the name with a wrong address (makes the next request slow!)\n"); + io.in.name = *name; + io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx); + io.in.wins_servers = const_str_list( + str_list_make_single(tctx, address)); + io.in.addresses = const_str_list( + str_list_make_single(tctx, "127.64.64.1")); + io.in.nb_flags = nb_flags; + io.in.ttl = 300000; + + status = nbt_name_register_wins(nbtsock, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name register\n", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name register\n", + address)); + + CHECK_STRING(tctx, io.out.wins_server, address); + CHECK_VALUE(tctx, io.out.rcode, 0); + } + + torture_comment(tctx, "refresh the name with the correct address\n"); + refresh.in.name = *name; + refresh.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx); + refresh.in.wins_servers = const_str_list( + str_list_make_single(tctx, address)); + refresh.in.addresses = const_str_list( + str_list_make_single(tctx, myaddress)); + refresh.in.nb_flags = nb_flags; + refresh.in.ttl = 12345; + + status = nbt_name_refresh_wins(nbtsock, tctx, &refresh); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "No response from %s for name refresh", + address)); + } + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name refresh", + address)); + + CHECK_STRING(tctx, refresh.out.wins_server, address); + CHECK_VALUE(tctx, refresh.out.rcode, 0); + + torture_comment(tctx, "release the name\n"); + release.in.name = *name; + release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release.in.dest_addr = address; + release.in.address = myaddress; + release.in.nb_flags = nb_flags; + release.in.broadcast = false; + release.in.timeout = 3; + release.in.retries = 0; + + status = nbt_name_release(nbtsock, tctx, &release); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address)); + + CHECK_NAME(tctx, release.out.name, *name); + CHECK_VALUE(tctx, release.out.rcode, 0); + + torture_comment(tctx, "release again\n"); + status = nbt_name_release(nbtsock, tctx, &release); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "Bad response from %s for name query", + address)); + + CHECK_NAME(tctx, release.out.name, *name); + CHECK_VALUE(tctx, release.out.rcode, 0); + + + torture_comment(tctx, "query the name to make sure its gone\n"); + query.in.name = *name; + status = nbt_name_query(nbtsock, tctx, &query); + if (name->type != NBT_NAME_LOGON && + (nb_flags & NBT_NM_GROUP)) { + torture_assert_ntstatus_ok(tctx, status, + "ERROR: Name query failed after group release"); + } else { + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + "Incorrect response to name query"); + } + + return true; +} + + +static char *test_nbt_wins_scope_string(TALLOC_CTX *mem_ctx, uint8_t count) +{ + char *res; + uint8_t i; + + res = talloc_array(mem_ctx, char, count+1); + if (res == NULL) { + return NULL; + } + + for (i=0; i < count; i++) { + switch (i) { + case 63: + case 63 + 1 + 63: + case 63 + 1 + 63 + 1 + 63: + res[i] = '.'; + break; + default: + res[i] = '0' + (i%10); + break; + } + } + + res[count] = '\0'; + + talloc_set_name_const(res, res); + + return res; +} + +/* + test operations against a WINS server +*/ +static bool nbt_test_wins(struct torture_context *tctx) +{ + struct nbt_name name; + uint32_t r = (uint32_t)(random() % (100000)); + const char *address; + bool ret = true; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + name.name = talloc_asprintf(tctx, "_TORTURE-%5u", r); + + name.type = NBT_NAME_CLIENT; + name.scope = NULL; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, true, NBT_RCODE_OK); + + name.type = NBT_NAME_MASTER; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK); + + name.type = NBT_NAME_SERVER; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, true, NBT_RCODE_OK); + + name.type = NBT_NAME_LOGON; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK); + + name.type = NBT_NAME_BROWSER; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK); + + name.type = NBT_NAME_PDC; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, true, NBT_RCODE_OK); + + name.type = 0xBF; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, true, NBT_RCODE_OK); + + name.type = 0xBE; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.scope = "example"; + name.type = 0x72; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, true, NBT_RCODE_OK); + + name.scope = "example"; + name.type = 0x71; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK); + + name.scope = "foo.example.com"; + name.type = 0x72; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.name = talloc_asprintf(tctx, "_T\01-%5u.foo", r); + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.name = ""; + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.name = talloc_asprintf(tctx, "."); + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.name = talloc_asprintf(tctx, "%5u-\377\200\300FOO", r); + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.scope = test_nbt_wins_scope_string(tctx, 237); + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_OK); + + name.scope = test_nbt_wins_scope_string(tctx, 238); + ret &= nbt_test_wins_name(tctx, address, &name, + NBT_NODE_H, false, NBT_RCODE_SVR); + + return ret; +} + +/* + test WINS operations +*/ +struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "wins"); + + torture_suite_add_simple_test(suite, "wins", nbt_test_wins); + + return suite; +} diff --git a/source4/torture/nbt/winsbench.c b/source4/torture/nbt/winsbench.c new file mode 100644 index 0000000..3722202 --- /dev/null +++ b/source4/torture/nbt/winsbench.c @@ -0,0 +1,300 @@ +/* + Unix SMB/CIFS implementation. + + WINS benchmark test + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "param/param.h" + +struct wins_state { + int num_names; + bool *registered; + int pass_count; + int fail_count; + const char *wins_server; + uint16_t wins_port; + const char *my_ip; + uint32_t ttl; +}; + +struct idx_state { + int idx; + struct wins_state *state; +}; + +static struct nbt_name generate_name(TALLOC_CTX *tctx, int idx) +{ + struct nbt_name name; + name.name = talloc_asprintf(tctx, "WINSBench%6u", idx); + name.type = 0x4; + name.scope = NULL; + return name; +} + +static void register_handler(struct nbt_name_request *req) +{ + struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); + struct wins_state *state = istate->state; + struct nbt_name_register io; + NTSTATUS status; + + status = nbt_name_register_recv(req, istate, &io); + if (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK) { + state->fail_count++; + } else { + state->pass_count++; + state->registered[istate->idx] = true; + } + talloc_free(istate); +} + +/* + generate a registration +*/ +static void generate_register(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) +{ + struct nbt_name_register io; + TALLOC_CTX *tmp_ctx = talloc_new(state); + struct nbt_name_request *req; + struct idx_state *istate; + + istate = talloc(nbtsock, struct idx_state); + istate->idx = idx; + istate->state = state; + + io.in.name = generate_name(tmp_ctx, idx); + io.in.dest_addr = state->wins_server; + io.in.dest_port = state->wins_port; + io.in.address = state->my_ip; + io.in.nb_flags = NBT_NODE_H; + io.in.register_demand = false; + io.in.broadcast = false; + io.in.multi_homed = false; + io.in.ttl = state->ttl; + io.in.timeout = 2; + io.in.retries = 1; + + req = nbt_name_register_send(nbtsock, &io); + + req->async.fn = register_handler; + req->async.private_data = istate; + + talloc_free(tmp_ctx); +} + + +static void release_handler(struct nbt_name_request *req) +{ + struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); + struct wins_state *state = istate->state; + struct nbt_name_release io; + NTSTATUS status; + + status = nbt_name_release_recv(req, istate, &io); + if (state->registered[istate->idx] && + (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK)) { + state->fail_count++; + } else { + state->pass_count++; + state->registered[istate->idx] = false; + } + talloc_free(istate); +} + +/* + generate a name release +*/ +static void generate_release(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) +{ + struct nbt_name_release io; + TALLOC_CTX *tmp_ctx = talloc_new(state); + struct nbt_name_request *req; + struct idx_state *istate; + + istate = talloc(nbtsock, struct idx_state); + istate->idx = idx; + istate->state = state; + + io.in.name = generate_name(tmp_ctx, idx); + io.in.dest_port = state->wins_port; + io.in.dest_addr = state->wins_server; + io.in.address = state->my_ip; + io.in.nb_flags = NBT_NODE_H; + io.in.broadcast = false; + io.in.timeout = 2; + io.in.retries = 1; + + req = nbt_name_release_send(nbtsock, &io); + + req->async.fn = release_handler; + req->async.private_data = istate; + + talloc_free(tmp_ctx); +} + + +static void query_handler(struct nbt_name_request *req) +{ + struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); + struct wins_state *state = istate->state; + struct nbt_name_query io; + NTSTATUS status; + + status = nbt_name_query_recv(req, istate, &io); + if (!NT_STATUS_IS_OK(status) && state->registered[istate->idx]) { + state->fail_count++; + } else { + state->pass_count++; + } + talloc_free(istate); +} + +/* + generate a name query +*/ +static void generate_query(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) +{ + struct nbt_name_query io; + TALLOC_CTX *tmp_ctx = talloc_new(state); + struct nbt_name_request *req; + struct idx_state *istate; + + istate = talloc(nbtsock, struct idx_state); + istate->idx = idx; + istate->state = state; + + io.in.name = generate_name(tmp_ctx, idx); + io.in.dest_addr = state->wins_server; + io.in.dest_port = state->wins_port; + io.in.broadcast = false; + io.in.wins_lookup = true; + io.in.timeout = 2; + io.in.retries = 1; + + req = nbt_name_query_send(nbtsock, &io); + + req->async.fn = query_handler; + req->async.private_data = istate; + + talloc_free(tmp_ctx); +} + +/* + generate one WINS request +*/ +static void generate_request(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) +{ + if (random() % 5 == 0) { + generate_register(nbtsock, state, idx); + return; + } + + if (random() % 20 == 0) { + generate_release(nbtsock, state, idx); + return; + } + + generate_query(nbtsock, state, idx); +} + +/* + benchmark simple name queries +*/ +static bool bench_wins(struct torture_context *tctx) +{ + struct nbt_name_socket *nbtsock = nbt_name_socket_init(tctx, tctx->ev); + int num_sent=0; + struct timeval tv = timeval_current(); + bool ret = true; + int timelimit = torture_setting_int(tctx, "timelimit", 5); + struct wins_state *state; + extern int torture_entries; + struct socket_address *my_ip; + struct nbt_name name; + const char *address; + struct interface *ifaces; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + state = talloc_zero(nbtsock, struct wins_state); + + state->num_names = torture_entries; + state->registered = talloc_zero_array(state, bool, state->num_names); + state->wins_server = address; + state->wins_port = lpcfg_nbt_port(tctx->lp_ctx); + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + state->my_ip = talloc_strdup(tctx, iface_list_best_ip(ifaces, address)); + state->ttl = timelimit; + + my_ip = socket_address_from_strings(nbtsock, nbtsock->sock->backend_name, + state->my_ip, 0); + + socket_listen(nbtsock->sock, my_ip, 0, 0); + + torture_comment(tctx, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + while (num_sent - (state->pass_count+state->fail_count) < 10) { + generate_request(nbtsock, state, num_sent % state->num_names); + num_sent++; + if (num_sent % 50 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%.1f queries per second (%d failures) \r", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + fflush(stdout); + } + } + } + + tevent_loop_once(nbtsock->event_ctx); + } + + while (num_sent != (state->pass_count + state->fail_count)) { + tevent_loop_once(nbtsock->event_ctx); + } + + torture_comment(tctx, "%.1f queries per second (%d failures) \n", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + + talloc_free(nbtsock); + return ret; +} + + +/* + benchmark how fast a WINS server can respond to a mixture of + registration/refresh/release and name query requests +*/ +struct torture_suite *torture_bench_wins(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "bench-wins"); + + torture_suite_add_simple_test(suite, "wins", bench_wins); + + return suite; +} diff --git a/source4/torture/nbt/winsreplication.c b/source4/torture/nbt/winsreplication.c new file mode 100644 index 0000000..e1c9a4d --- /dev/null +++ b/source4/torture/nbt/winsreplication.c @@ -0,0 +1,9884 @@ +/* + Unix SMB/CIFS implementation. + + WINS replication testing + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/wrepl/winsrepl.h" +#include "lib/events/events.h" +#include "lib/socket/socket.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "libcli/nbt/libnbt.h" +#include "torture/torture.h" +#include "torture/nbt/proto.h" +#include "param/param.h" + +#define CHECK_STATUS(tctx, status, correct) \ + torture_assert_ntstatus_equal(tctx, status, correct, \ + "Incorrect status") + +#define CHECK_VALUE(tctx, v, correct) \ + torture_assert(tctx, (v) == (correct), \ + talloc_asprintf(tctx, "Incorrect value %s=%d - should be %d\n", \ + #v, v, correct)) + +#define CHECK_VALUE_UINT64(tctx, v, correct) \ + torture_assert(tctx, (v) == (correct), \ + talloc_asprintf(tctx, "Incorrect value %s=%llu - should be %llu\n", \ + #v, (long long)v, (long long)correct)) + +#define CHECK_VALUE_STRING(tctx, v, correct) \ + torture_assert_str_equal(tctx, v, correct, "Invalid value") + +#define _NBT_NAME(n,t,s) {\ + .name = n,\ + .type = t,\ + .scope = s\ +} + +static const char *wrepl_name_type_string(enum wrepl_name_type type) +{ + switch (type) { + case WREPL_TYPE_UNIQUE: return "UNIQUE"; + case WREPL_TYPE_GROUP: return "GROUP"; + case WREPL_TYPE_SGROUP: return "SGROUP"; + case WREPL_TYPE_MHOMED: return "MHOMED"; + } + return "UNKNOWN_TYPE"; +} + +static const char *wrepl_name_state_string(enum wrepl_name_state state) +{ + switch (state) { + case WREPL_STATE_ACTIVE: return "ACTIVE"; + case WREPL_STATE_RELEASED: return "RELEASED"; + case WREPL_STATE_TOMBSTONE: return "TOMBSTONE"; + case WREPL_STATE_RESERVED: return "RESERVED"; + } + return "UNKNOWN_STATE"; +} + +/* + test how assoc_ctx's are only usable on the connection + they are created on. +*/ +static bool test_assoc_ctx1(struct torture_context *tctx) +{ + bool ret = true; + struct tevent_req *subreq; + struct wrepl_socket *wrepl_socket1; + struct wrepl_associate associate1; + struct wrepl_socket *wrepl_socket2; + struct wrepl_associate associate2; + struct wrepl_packet packet; + struct wrepl_send_ctrl ctrl; + struct wrepl_packet *rep_packet; + struct wrepl_associate_stop assoc_stop; + NTSTATUS status; + struct nbt_name name; + const char *address; + bool ok; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + torture_comment(tctx, "Test if assoc_ctx is only valid on the connection it was created on\n"); + + wrepl_socket1 = wrepl_socket_init(tctx, tctx->ev); + wrepl_socket2 = wrepl_socket_init(tctx, tctx->ev); + + torture_comment(tctx, "Setup 2 wrepl connections\n"); + status = wrepl_connect(wrepl_socket1, wrepl_best_ip(tctx->lp_ctx, address), address); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + status = wrepl_connect(wrepl_socket2, wrepl_best_ip(tctx->lp_ctx, address), address); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send a start association request (conn1)\n"); + status = wrepl_associate(wrepl_socket1, &associate1); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "association context (conn1): 0x%x\n", associate1.out.assoc_ctx); + + torture_comment(tctx, "Send a start association request (conn2)\n"); + status = wrepl_associate(wrepl_socket2, &associate2); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "association context (conn2): 0x%x\n", associate2.out.assoc_ctx); + + torture_comment(tctx, "Send a replication table query, with assoc 1 (conn2), the answer should be on conn1\n"); + ZERO_STRUCT(packet); + packet.opcode = WREPL_OPCODE_BITS; + packet.assoc_ctx = associate1.out.assoc_ctx; + packet.mess_type = WREPL_REPLICATION; + packet.message.replication.command = WREPL_REPL_TABLE_QUERY; + ZERO_STRUCT(ctrl); + ctrl.send_only = true; + subreq = wrepl_request_send(tctx, tctx->ev, wrepl_socket2, &packet, &ctrl); + ok = tevent_req_poll(subreq, tctx->ev); + if (!ok) { + CHECK_STATUS(tctx, NT_STATUS_INTERNAL_ERROR, NT_STATUS_OK); + } + status = wrepl_request_recv(subreq, tctx, &rep_packet); + TALLOC_FREE(subreq); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send a association request (conn2), to make sure the last request was ignored\n"); + status = wrepl_associate(wrepl_socket2, &associate2); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send a replication table query, with invalid assoc (conn1), receive answer from conn2\n"); + ZERO_STRUCT(packet); + packet.opcode = WREPL_OPCODE_BITS; + packet.assoc_ctx = 0; + packet.mess_type = WREPL_REPLICATION; + packet.message.replication.command = WREPL_REPL_TABLE_QUERY; + status = wrepl_request(wrepl_socket1, tctx, &packet, &rep_packet); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send a association request (conn1), to make sure the last request was handled correct\n"); + status = wrepl_associate(wrepl_socket1, &associate2); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + assoc_stop.in.assoc_ctx = associate1.out.assoc_ctx; + assoc_stop.in.reason = 4; + torture_comment(tctx, "Send a association stop request (conn1), reason: %u\n", assoc_stop.in.reason); + status = wrepl_associate_stop(wrepl_socket1, &assoc_stop); + CHECK_STATUS(tctx, status, NT_STATUS_END_OF_FILE); + + assoc_stop.in.assoc_ctx = associate2.out.assoc_ctx; + assoc_stop.in.reason = 0; + torture_comment(tctx, "Send a association stop request (conn2), reason: %u\n", assoc_stop.in.reason); + status = wrepl_associate_stop(wrepl_socket2, &assoc_stop); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Close 2 wrepl connections\n"); + talloc_free(wrepl_socket1); + talloc_free(wrepl_socket2); + return ret; +} + +/* + test if we always get back the same assoc_ctx +*/ +static bool test_assoc_ctx2(struct torture_context *tctx) +{ + struct wrepl_socket *wrepl_socket; + struct wrepl_associate associate; + uint32_t assoc_ctx1; + struct nbt_name name; + NTSTATUS status; + const char *address; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + torture_comment(tctx, "Test if we always get back the same assoc_ctx\n"); + + wrepl_socket = wrepl_socket_init(tctx, tctx->ev); + + torture_comment(tctx, "Setup wrepl connections\n"); + status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, address), address); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send 1st start association request\n"); + status = wrepl_associate(wrepl_socket, &associate); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + assoc_ctx1 = associate.out.assoc_ctx; + torture_comment(tctx, "1st association context: 0x%x\n", associate.out.assoc_ctx); + + torture_comment(tctx, "Send 2nd start association request\n"); + status = wrepl_associate(wrepl_socket, &associate); + torture_assert_ntstatus_ok(tctx, status, "2nd start association failed"); + torture_assert(tctx, associate.out.assoc_ctx == assoc_ctx1, + "Different context returned"); + torture_comment(tctx, "2nd association context: 0x%x\n", associate.out.assoc_ctx); + + torture_comment(tctx, "Send 3rd start association request\n"); + status = wrepl_associate(wrepl_socket, &associate); + torture_assert(tctx, associate.out.assoc_ctx == assoc_ctx1, + "Different context returned"); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + torture_comment(tctx, "3rd association context: 0x%x\n", associate.out.assoc_ctx); + + torture_comment(tctx, "Close wrepl connections\n"); + talloc_free(wrepl_socket); + return true; +} + + +/* + display a replication entry +*/ +static void display_entry(struct torture_context *tctx, struct wrepl_name *name) +{ + int i; + + torture_comment(tctx, "%s\n", nbt_name_string(tctx, &name->name)); + torture_comment(tctx, "\tTYPE:%u STATE:%u NODE:%u STATIC:%u VERSION_ID: %llu\n", + name->type, name->state, name->node, name->is_static, (long long)name->version_id); + torture_comment(tctx, "\tRAW_FLAGS: 0x%08X OWNER: %-15s\n", + name->raw_flags, name->owner); + for (i=0;inum_addresses;i++) { + torture_comment(tctx, "\tADDR: %-15s OWNER: %-15s\n", + name->addresses[i].address, name->addresses[i].owner); + } +} + +/* + test a full replication dump from a WINS server +*/ +static bool test_wins_replication(struct torture_context *tctx) +{ + struct wrepl_socket *wrepl_socket; + NTSTATUS status; + int i, j; + struct wrepl_associate associate; + struct wrepl_pull_table pull_table; + struct wrepl_pull_names pull_names; + struct nbt_name name; + const char *address; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + torture_comment(tctx, "Test one pull replication cycle\n"); + + wrepl_socket = wrepl_socket_init(tctx, tctx->ev); + + torture_comment(tctx, "Setup wrepl connections\n"); + status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, address), address); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Send a start association request\n"); + + status = wrepl_associate(wrepl_socket, &associate); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "association context: 0x%x\n", associate.out.assoc_ctx); + + torture_comment(tctx, "Send a replication table query\n"); + pull_table.in.assoc_ctx = associate.out.assoc_ctx; + + status = wrepl_pull_table(wrepl_socket, tctx, &pull_table); + if (NT_STATUS_EQUAL(NT_STATUS_NETWORK_ACCESS_DENIED,status)) { + struct wrepl_associate_stop assoc_stop; + + assoc_stop.in.assoc_ctx = associate.out.assoc_ctx; + assoc_stop.in.reason = 0; + + wrepl_associate_stop(wrepl_socket, &assoc_stop); + + torture_fail(tctx, "We are not a valid pull partner for the server"); + } + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Found %d replication partners\n", pull_table.out.num_partners); + + for (i=0;iaddress, + (long long)partner->max_version, + (long long)partner->min_version, + partner->type); + + pull_names.in.assoc_ctx = associate.out.assoc_ctx; + pull_names.in.partner = *partner; + + status = wrepl_pull_names(wrepl_socket, tctx, &pull_names); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "Received %d names\n", pull_names.out.num_names); + + for (j=0;jaddress = address; + ctx->pull = wrepl_socket_init(ctx, tctx->ev); + if (!ctx->pull) return NULL; + + torture_comment(tctx, "Setup wrepl conflict pull connection\n"); + status = wrepl_connect(ctx->pull, wrepl_best_ip(tctx->lp_ctx, ctx->address), ctx->address); + if (!NT_STATUS_IS_OK(status)) return NULL; + + status = wrepl_associate(ctx->pull, &associate); + if (!NT_STATUS_IS_OK(status)) return NULL; + + ctx->pull_assoc = associate.out.assoc_ctx; + + ctx->a.address = TEST_OWNER_A_ADDRESS; + ctx->a.max_version = 0; + ctx->a.min_version = 0; + ctx->a.type = 1; + + ctx->b.address = TEST_OWNER_B_ADDRESS; + ctx->b.max_version = 0; + ctx->b.min_version = 0; + ctx->b.type = 1; + + ctx->x.address = TEST_OWNER_X_ADDRESS; + ctx->x.max_version = 0; + ctx->x.min_version = 0; + ctx->x.type = 1; + + ctx->c.address = address; + ctx->c.max_version = 0; + ctx->c.min_version = 0; + ctx->c.type = 1; + + pull_table.in.assoc_ctx = ctx->pull_assoc; + status = wrepl_pull_table(ctx->pull, ctx->pull, &pull_table); + if (!NT_STATUS_IS_OK(status)) return NULL; + + for (i=0; i < pull_table.out.num_partners; i++) { + if (strcmp(TEST_OWNER_A_ADDRESS,pull_table.out.partners[i].address)==0) { + ctx->a.max_version = pull_table.out.partners[i].max_version; + ctx->a.min_version = pull_table.out.partners[i].min_version; + } + if (strcmp(TEST_OWNER_B_ADDRESS,pull_table.out.partners[i].address)==0) { + ctx->b.max_version = pull_table.out.partners[i].max_version; + ctx->b.min_version = pull_table.out.partners[i].min_version; + } + if (strcmp(TEST_OWNER_X_ADDRESS,pull_table.out.partners[i].address)==0) { + ctx->x.max_version = pull_table.out.partners[i].max_version; + ctx->x.min_version = pull_table.out.partners[i].min_version; + } + if (strcmp(address,pull_table.out.partners[i].address)==0) { + ctx->c.max_version = pull_table.out.partners[i].max_version; + ctx->c.min_version = pull_table.out.partners[i].min_version; + } + } + + talloc_free(pull_table.out.partners); + + ctx->nbtsock = nbt_name_socket_init(ctx, tctx->ev); + if (!ctx->nbtsock) return NULL; + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + + ctx->myaddr = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_list_best_ip(ifaces, address), 0); + if (!ctx->myaddr) return NULL; + + for (i = 0; i < iface_list_count(ifaces); i++) { + if (!iface_list_n_is_v4(ifaces, i)) continue; + if (strcmp(ctx->myaddr->addr, iface_list_n_ip(ifaces, i)) == 0) continue; + ctx->myaddr2 = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_list_n_ip(ifaces, i), 0); + if (!ctx->myaddr2) return NULL; + break; + } + + status = socket_listen(ctx->nbtsock->sock, ctx->myaddr, 0, 0); + if (!NT_STATUS_IS_OK(status)) return NULL; + + ctx->nbtsock_srv = nbt_name_socket_init(ctx, tctx->ev); + if (!ctx->nbtsock_srv) return NULL; + + /* Make a port 137 version of ctx->myaddr */ + nbt_srv_addr = socket_address_from_strings(tctx, ctx->nbtsock_srv->sock->backend_name, ctx->myaddr->addr, lpcfg_nbt_port(tctx->lp_ctx)); + if (!nbt_srv_addr) return NULL; + + /* And if possible, bind to it. This won't work unless we are root or in socketwrapper */ + status = socket_listen(ctx->nbtsock_srv->sock, nbt_srv_addr, 0, 0); + talloc_free(nbt_srv_addr); + if (!NT_STATUS_IS_OK(status)) { + /* this isn't fatal */ + talloc_free(ctx->nbtsock_srv); + ctx->nbtsock_srv = NULL; + } + + if (ctx->myaddr2 && ctx->nbtsock_srv) { + ctx->nbtsock2 = nbt_name_socket_init(ctx, tctx->ev); + if (!ctx->nbtsock2) return NULL; + + status = socket_listen(ctx->nbtsock2->sock, ctx->myaddr2, 0, 0); + if (!NT_STATUS_IS_OK(status)) return NULL; + + ctx->nbtsock_srv2 = nbt_name_socket_init(ctx, ctx->nbtsock_srv->event_ctx); + if (!ctx->nbtsock_srv2) return NULL; + + /* Make a port 137 version of ctx->myaddr2 */ + nbt_srv_addr = socket_address_from_strings(tctx, + ctx->nbtsock_srv->sock->backend_name, + ctx->myaddr2->addr, + lpcfg_nbt_port(tctx->lp_ctx)); + if (!nbt_srv_addr) return NULL; + + /* And if possible, bind to it. This won't work unless we are root or in socketwrapper */ + status = socket_listen(ctx->nbtsock_srv2->sock, ctx->myaddr2, 0, 0); + talloc_free(nbt_srv_addr); + if (!NT_STATUS_IS_OK(status)) { + /* this isn't fatal */ + talloc_free(ctx->nbtsock_srv2); + ctx->nbtsock_srv2 = NULL; + } + } + + ctx->addresses_best_num = 1; + ctx->addresses_best = talloc_array(ctx, struct wrepl_ip, ctx->addresses_best_num); + if (!ctx->addresses_best) return NULL; + ctx->addresses_best[0].owner = ctx->b.address; + ctx->addresses_best[0].ip = ctx->myaddr->addr; + + + num_ifaces = iface_list_count(ifaces); + ctx->addresses_all = talloc_array(ctx, struct wrepl_ip, num_ifaces); + ctx->addresses_all_num = 0; + if (!ctx->addresses_all) return NULL; + for (i=0; i < num_ifaces; i++) { + if (!iface_list_n_is_v4(ifaces, i)) continue; + ctx->addresses_all[i].owner = ctx->b.address; + ctx->addresses_all[i].ip = talloc_strdup(ctx->addresses_all, iface_list_n_ip(ifaces, i)); + ctx->addresses_all_num++; + if (!ctx->addresses_all[i].ip) return NULL; + } + + if (ctx->nbtsock_srv2) { + ctx->addresses_best2_num = 1; + ctx->addresses_best2 = talloc_array(ctx, struct wrepl_ip, ctx->addresses_best2_num); + if (!ctx->addresses_best2) return NULL; + ctx->addresses_best2[0].owner = ctx->b.address; + ctx->addresses_best2[0].ip = ctx->myaddr2->addr; + + ctx->addresses_mhomed_num = 2; + ctx->addresses_mhomed = talloc_array(ctx, struct wrepl_ip, ctx->addresses_mhomed_num); + if (!ctx->addresses_mhomed) return NULL; + ctx->addresses_mhomed[0].owner = ctx->b.address; + ctx->addresses_mhomed[0].ip = ctx->myaddr->addr; + ctx->addresses_mhomed[1].owner = ctx->b.address; + ctx->addresses_mhomed[1].ip = ctx->myaddr2->addr; + } + + return ctx; +} + +static bool test_wrepl_update_one(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx, + const struct wrepl_wins_owner *owner, + const struct wrepl_wins_name *name) +{ + struct wrepl_socket *wrepl_socket; + struct wrepl_associate associate; + struct wrepl_packet update_packet, repl_send; + struct wrepl_table *update; + struct wrepl_wins_owner wrepl_wins_owners[1]; + struct wrepl_packet *repl_recv; + struct wrepl_send_reply *send_reply; + struct wrepl_wins_name wrepl_wins_names[1]; + uint32_t assoc_ctx; + NTSTATUS status; + + wrepl_socket = wrepl_socket_init(ctx, tctx->ev); + + status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, ctx->address), ctx->address); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + status = wrepl_associate(wrepl_socket, &associate); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + assoc_ctx = associate.out.assoc_ctx; + + /* now send a WREPL_REPL_UPDATE message */ + ZERO_STRUCT(update_packet); + update_packet.opcode = WREPL_OPCODE_BITS; + update_packet.assoc_ctx = assoc_ctx; + update_packet.mess_type = WREPL_REPLICATION; + update_packet.message.replication.command = WREPL_REPL_UPDATE; + update = &update_packet.message.replication.info.table; + + update->partner_count = ARRAY_SIZE(wrepl_wins_owners); + update->partners = wrepl_wins_owners; + update->initiator = "0.0.0.0"; + + wrepl_wins_owners[0] = *owner; + + status = wrepl_request(wrepl_socket, wrepl_socket, + &update_packet, &repl_recv); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VALUE(tctx, repl_recv->mess_type, WREPL_REPLICATION); + CHECK_VALUE(tctx, repl_recv->message.replication.command, WREPL_REPL_SEND_REQUEST); + + ZERO_STRUCT(repl_send); + repl_send.opcode = WREPL_OPCODE_BITS; + repl_send.assoc_ctx = assoc_ctx; + repl_send.mess_type = WREPL_REPLICATION; + repl_send.message.replication.command = WREPL_REPL_SEND_REPLY; + send_reply = &repl_send.message.replication.info.reply; + + send_reply->num_names = ARRAY_SIZE(wrepl_wins_names); + send_reply->names = wrepl_wins_names; + + wrepl_wins_names[0] = *name; + + status = wrepl_request(wrepl_socket, wrepl_socket, + &repl_send, &repl_recv); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VALUE(tctx, repl_recv->mess_type, WREPL_STOP_ASSOCIATION); + CHECK_VALUE(tctx, repl_recv->message.stop.reason, 0); + + talloc_free(wrepl_socket); + return true; +} + +static bool test_wrepl_is_applied(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx, + const struct wrepl_wins_owner *owner, + const struct wrepl_wins_name *name, + bool expected) +{ + NTSTATUS status; + struct wrepl_pull_names pull_names; + struct wrepl_name *names; + + pull_names.in.assoc_ctx = ctx->pull_assoc; + pull_names.in.partner = *owner; + pull_names.in.partner.min_version = pull_names.in.partner.max_version; + + status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + torture_assert(tctx, pull_names.out.num_names == (expected?1:0), + talloc_asprintf(tctx, "Invalid number of records returned - expected %d got %d", expected, pull_names.out.num_names)); + + names = pull_names.out.names; + + if (expected) { + uint32_t flags = WREPL_NAME_FLAGS(names[0].type, + names[0].state, + names[0].node, + names[0].is_static); + char *expected_scope = NULL; + CHECK_VALUE(tctx, names[0].name.type, name->name->type); + CHECK_VALUE_STRING(tctx, names[0].name.name, name->name->name); + + if (names[0].name.scope) { + expected_scope = talloc_strndup(tctx, + name->name->scope, + 237); + } + CHECK_VALUE_STRING(tctx, names[0].name.scope, expected_scope); + CHECK_VALUE(tctx, flags, name->flags); + CHECK_VALUE_UINT64(tctx, names[0].version_id, name->id); + + if (flags & 2) { + CHECK_VALUE(tctx, names[0].num_addresses, + name->addresses.addresses.num_ips); + } else { + CHECK_VALUE(tctx, names[0].num_addresses, 1); + CHECK_VALUE_STRING(tctx, names[0].addresses[0].address, + name->addresses.ip); + } + } + talloc_free(pull_names.out.names); + return true; +} + +static bool test_wrepl_mhomed_merged(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx, + const struct wrepl_wins_owner *owner1, + uint32_t num_ips1, const struct wrepl_ip *ips1, + const struct wrepl_wins_owner *owner2, + uint32_t num_ips2, const struct wrepl_ip *ips2, + const struct wrepl_wins_name *name2) +{ + NTSTATUS status; + struct wrepl_pull_names pull_names; + struct wrepl_name *names; + uint32_t flags; + uint32_t i, j; + uint32_t num_ips = num_ips1 + num_ips2; + + for (i = 0; i < num_ips2; i++) { + for (j = 0; j < num_ips1; j++) { + if (strcmp(ips2[i].ip,ips1[j].ip) == 0) { + num_ips--; + break; + } + } + } + + pull_names.in.assoc_ctx = ctx->pull_assoc; + pull_names.in.partner = *owner2; + pull_names.in.partner.min_version = pull_names.in.partner.max_version; + + status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VALUE(tctx, pull_names.out.num_names, 1); + + names = pull_names.out.names; + + flags = WREPL_NAME_FLAGS(names[0].type, + names[0].state, + names[0].node, + names[0].is_static); + CHECK_VALUE(tctx, names[0].name.type, name2->name->type); + CHECK_VALUE_STRING(tctx, names[0].name.name, name2->name->name); + CHECK_VALUE_STRING(tctx, names[0].name.scope, name2->name->scope); + CHECK_VALUE(tctx, flags, name2->flags | WREPL_TYPE_MHOMED); + CHECK_VALUE_UINT64(tctx, names[0].version_id, name2->id); + + CHECK_VALUE(tctx, names[0].num_addresses, num_ips); + + for (i = 0; i < names[0].num_addresses; i++) { + const char *addr = names[0].addresses[i].address; + const char *owner = names[0].addresses[i].owner; + bool found = false; + + for (j = 0; j < num_ips2; j++) { + if (strcmp(addr, ips2[j].ip) == 0) { + found = true; + CHECK_VALUE_STRING(tctx, owner, owner2->address); + break; + } + } + + if (found) continue; + + for (j = 0; j < num_ips1; j++) { + if (strcmp(addr, ips1[j].ip) == 0) { + found = true; + CHECK_VALUE_STRING(tctx, owner, owner1->address); + break; + } + } + + if (found) continue; + + CHECK_VALUE_STRING(tctx, addr, "not found in address list"); + } + talloc_free(pull_names.out.names); + return true; +} + +static bool test_wrepl_sgroup_merged(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx, + struct wrepl_wins_owner *merge_owner, + struct wrepl_wins_owner *owner1, + uint32_t num_ips1, const struct wrepl_ip *ips1, + struct wrepl_wins_owner *owner2, + uint32_t num_ips2, const struct wrepl_ip *ips2, + const struct wrepl_wins_name *name2) +{ + NTSTATUS status; + struct wrepl_pull_names pull_names; + struct wrepl_name *names; + struct wrepl_name *name = NULL; + uint32_t flags; + uint32_t i, j; + uint32_t num_ips = num_ips1 + num_ips2; + + if (!merge_owner) { + merge_owner = &ctx->c; + } + + for (i = 0; i < num_ips1; i++) { + if (owner1 != &ctx->c && strcmp(ips1[i].owner,owner2->address) == 0) { + num_ips--; + continue; + } + for (j = 0; j < num_ips2; j++) { + if (strcmp(ips1[i].ip,ips2[j].ip) == 0) { + num_ips--; + break; + } + } + } + + + pull_names.in.assoc_ctx = ctx->pull_assoc; + pull_names.in.partner = *merge_owner; + pull_names.in.partner.min_version = pull_names.in.partner.max_version; + pull_names.in.partner.max_version = 0; + + status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + names = pull_names.out.names; + + for (i = 0; i < pull_names.out.num_names; i++) { + if (names[i].name.type != name2->name->type) continue; + if (!names[i].name.name) continue; + if (strcmp(names[i].name.name, name2->name->name) != 0) continue; + if (names[i].name.scope) continue; + + name = &names[i]; + } + + if (pull_names.out.num_names > 0) { + merge_owner->max_version = names[pull_names.out.num_names-1].version_id; + } + + if (!name) { + torture_comment(tctx, "%s: Name '%s' not found\n", __location__, nbt_name_string(ctx, name2->name)); + return false; + } + + flags = WREPL_NAME_FLAGS(name->type, + name->state, + name->node, + name->is_static); + CHECK_VALUE(tctx, name->name.type, name2->name->type); + CHECK_VALUE_STRING(tctx, name->name.name, name2->name->name); + CHECK_VALUE_STRING(tctx, name->name.scope, name2->name->scope); + CHECK_VALUE(tctx, flags, name2->flags); + + CHECK_VALUE(tctx, name->num_addresses, num_ips); + + for (i = 0; i < name->num_addresses; i++) { + const char *addr = name->addresses[i].address; + const char *owner = name->addresses[i].owner; + bool found = false; + + for (j = 0; j < num_ips2; j++) { + if (strcmp(addr, ips2[j].ip) == 0) { + found = true; + CHECK_VALUE_STRING(tctx, owner, ips2[j].owner); + break; + } + } + + if (found) continue; + + for (j = 0; j < num_ips1; j++) { + if (strcmp(addr, ips1[j].ip) == 0) { + found = true; + if (owner1 == &ctx->c) { + CHECK_VALUE_STRING(tctx, owner, owner1->address); + } else { + CHECK_VALUE_STRING(tctx, owner, ips1[j].owner); + } + break; + } + } + + if (found) continue; + + CHECK_VALUE_STRING(tctx, addr, "not found in address list"); + } + talloc_free(pull_names.out.names); + return true; +} + +static char *test_nbt_winsrepl_scope_string(TALLOC_CTX *mem_ctx, uint8_t count) +{ + char *res; + uint8_t i; + + res = talloc_array(mem_ctx, char, count+1); + if (res == NULL) { + return NULL; + } + + for (i=0; i < count; i++) { + res[i] = '0' + (i%10); + } + + res[count] = '\0'; + + talloc_set_name_const(res, res); + + return res; +} + +static bool test_conflict_same_owner(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx) +{ + bool ret = true; + struct wrepl_wins_name wins_name1; + struct wrepl_wins_name wins_name2; + struct wrepl_wins_name *wins_name_tmp; + struct wrepl_wins_name *wins_name_last; + struct wrepl_wins_name *wins_name_cur; + uint32_t i,j; + struct nbt_name names[] = { + _NBT_NAME("_SAME_OWNER_A", 0x00, NULL), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 1)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 2)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 3)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 4)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 5)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 6)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 7)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 8)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 9)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 237)), + _NBT_NAME("_SAME_OWNER_A", 0x00, + test_nbt_winsrepl_scope_string(tctx, 238)), + _NBT_NAME("_SAME_OWNER_A", 0x1C, NULL), + }; + struct { + enum wrepl_name_type type; + enum wrepl_name_state state; + enum wrepl_name_node node; + bool is_static; + uint32_t num_ips; + const struct wrepl_ip *ips; + } records[] = { + { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + },{ + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + },{ + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_2), + .ips = addresses_A_2, + },{ + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = true, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + },{ + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_2), + .ips = addresses_A_2, + },{ + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_2), + .ips = addresses_A_2, + },{ + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + },{ + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_2), + .ips = addresses_A_2, + },{ + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + },{ + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + },{ + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + },{ + /* the last one should always be a unique,tombstone record! */ + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + } + }; + + wins_name_tmp = NULL; + wins_name_last = &wins_name2; + wins_name_cur = &wins_name1; + + for (j=0; ret && j < ARRAY_SIZE(names); j++) { + torture_comment(tctx, "Test Replica Conflicts with same owner[%s] for %s\n", + nbt_name_string(ctx, &names[j]), ctx->a.address); + + for(i=0; ret && i < ARRAY_SIZE(records); i++) { + wins_name_tmp = wins_name_last; + wins_name_last = wins_name_cur; + wins_name_cur = wins_name_tmp; + + if (i > 0) { + torture_comment(tctx, "%s,%s%s vs. %s,%s%s with %s ip(s) => %s\n", + wrepl_name_type_string(records[i-1].type), + wrepl_name_state_string(records[i-1].state), + (records[i-1].is_static?",static":""), + wrepl_name_type_string(records[i].type), + wrepl_name_state_string(records[i].state), + (records[i].is_static?",static":""), + (records[i-1].ips==records[i].ips?"same":"different"), + "REPLACE"); + } + + wins_name_cur->name = &names[j]; + wins_name_cur->flags = WREPL_NAME_FLAGS(records[i].type, + records[i].state, + records[i].node, + records[i].is_static); + wins_name_cur->id = ++ctx->a.max_version; + if (wins_name_cur->flags & 2) { + wins_name_cur->addresses.addresses.num_ips = records[i].num_ips; + wins_name_cur->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + records[i].ips); + } else { + wins_name_cur->addresses.ip = records[i].ips[0].ip; + } + wins_name_cur->unknown = "255.255.255.255"; + + ret &= test_wrepl_update_one(tctx, ctx, &ctx->a,wins_name_cur); + if (records[i].state == WREPL_STATE_RELEASED) { + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_last, false); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_cur, false); + } else { + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_cur, true); + } + + /* the first one is a cleanup run */ + if (!ret && i == 0) ret = true; + + if (!ret) { + torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, __location__); + return ret; + } + } + } + return ret; +} + +static bool test_conflict_different_owner(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx) +{ + bool ret = true; + struct wrepl_wins_name wins_name1; + struct wrepl_wins_name wins_name2; + struct wrepl_wins_name *wins_name_r1; + struct wrepl_wins_name *wins_name_r2; + uint32_t i; + struct { + const char *line; /* just better debugging */ + struct nbt_name name; + const char *comment; + bool extra; /* not the worst case, this is an extra test */ + bool cleanup; + struct { + struct wrepl_wins_owner *owner; + enum wrepl_name_type type; + enum wrepl_name_state state; + enum wrepl_name_node node; + bool is_static; + uint32_t num_ips; + const struct wrepl_ip *ips; + bool apply_expected; + bool sgroup_merge; + struct wrepl_wins_owner *merge_owner; + bool sgroup_cleanup; + } r1, r2; + } records[] = { + /* + * NOTE: the first record and the last applied one + * needs to be from the same owner, + * to not conflict in the next smbtorture run!!! + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true /* ignored */ + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true /* ignored */ + } + }, + +/* + * unique vs unique section + */ + /* + * unique,active vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * unique,active vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * unique,released vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * unique,released vs. unique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. unique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + +/* + * unique vs normal groups section, + */ + /* + * unique,active vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * unique,active vs. group,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * unique,released vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * unique,released vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * unique vs special groups section, + */ + /* + * unique,active vs. sgroup,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * unique,active vs. sgroup,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * unique,released vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * unique,released vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + +/* + * unique vs multi homed section, + */ + /* + * unique,active vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * unique,active vs. mhomed,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + } + }, + + /* + * unique,released vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + + /* + * unique,released vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + + /* + * unique,tombstone vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + +/* + * normal groups vs unique section, + */ + /* + * group,active vs. unique,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,active vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,released vs. unique,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,released vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,tombstone vs. unique,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,tombstone vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + +/* + * normal groups vs normal groups section, + */ + /* + * group,active vs. group,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,active vs. group,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * group,released vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * group,released vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * group,tombstone vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * group,tombstone vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + +/* + * normal groups vs special groups section, + */ + /* + * group,active vs. sgroup,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,active vs. sgroup,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,released vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * group,released vs. sgroup,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,tombstone vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * group,tombstone vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + +/* + * normal groups vs multi homed section, + */ + /* + * group,active vs. mhomed,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,active vs. mhomed,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,released vs. mhomed,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,released vs. mhomed,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * group,tombstone vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * group,tombstone vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + +/* + * special groups vs unique section, + */ + /* + * sgroup,active vs. unique,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * sgroup,active vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * sgroup,released vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * sgroup,released vs. unique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. unique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * special groups vs normal group section, + */ + /* + * sgroup,active vs. group,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * sgroup,active vs. group,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * sgroup,released vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,released vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. group,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. group,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * special groups (not active) vs special group section, + */ + /* + * sgroup,released vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,released vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. sgroup,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. sgroup,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * special groups vs multi homed section, + */ + /* + * sgroup,active vs. mhomed,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * sgroup,active vs. mhomed,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * sgroup,released vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,released vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * sgroup,tombstone vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * multi homed vs. unique section, + */ + /* + * mhomed,active vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,active vs. unique,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * mhomed,released vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,released vs. uinique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. unique,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. uinique,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * multi homed vs. normal group section, + */ + /* + * mhomed,active vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,active vs. group,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + } + }, + + /* + * mhomed,released vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * mhomed,released vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. group,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. group,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + +/* + * multi homed vs. special group section, + */ + /* + * mhomed,active vs. sgroup,active + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * mhomed,active vs. sgroup,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + } + }, + + /* + * mhomed,released vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,released vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. sgroup,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. sgroup,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }, + +/* + * multi homed vs. mlti homed section, + */ + /* + * mhomed,active vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * mhomed,active vs. mhomed,tombstone + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + } + }, + + /* + * mhomed,released vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + + /* + * mhomed,released vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_RELEASED, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = false + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. mhomed,active + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + } + }, + + /* + * mhomed,tombstone vs. mhomed,tombstone + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true, + } + }, +/* + * special group vs special group section, + */ + /* + * sgroup,active vs. sgroup,active same addresses + * => should be NOT replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4 vs. B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = false, + .sgroup_cleanup = true + } + }, + /* + * sgroup,active vs. sgroup,active same addresses + * => should be NOT replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4 vs. B:NULL", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + .sgroup_cleanup = true + } + }, + /* + * sgroup,active vs. sgroup,active subset addresses, special case... + * => should NOT be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4_X_3_4 vs. B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4), + .ips = addresses_A_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = false, + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->x, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, but owner changed + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4 vs. B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true, + .sgroup_cleanup = true + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, but owner changed + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4 vs. B:A_3_4_OWNER_B", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B), + .ips = addresses_A_3_4_OWNER_B, + .apply_expected = true, + .sgroup_cleanup = true + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, but owner changed + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4_OWNER_B vs. B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B), + .ips = addresses_A_3_4_OWNER_B, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true, + .sgroup_cleanup = true + } + }, + /* + * sgroup,active vs. sgroup,active different addresses + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .sgroup_merge = true, + .sgroup_cleanup = true, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .sgroup_merge = true, + .merge_owner = &ctx->b, + .sgroup_cleanup = false + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4_OWNER_B), + .ips = addresses_A_3_4_X_3_4_OWNER_B, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_X_3_4), + .ips = addresses_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .sgroup_merge = true, + .sgroup_cleanup = false + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->x, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4), + .ips = addresses_A_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B), + .ips = addresses_A_3_4_OWNER_B, + .sgroup_merge = true, + .merge_owner = &ctx->b, + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->x, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + } + }, + /* + * sgroup,active vs. sgroup,active partly different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_1_2), + .ips = addresses_B_3_4_X_1_2, + .sgroup_merge = true, + .sgroup_cleanup = false + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->x, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:A_3_4_B_3_4 vs. B:NULL => B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4_B_3_4), + .ips = addresses_A_3_4_B_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .sgroup_merge = true, + .merge_owner = &ctx->b, + .sgroup_cleanup = true + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true, + } + }, + /* + * sgroup,active vs. sgroup,active different addresses, special case... + * => should be merged + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .sgroup_merge = true, + .merge_owner = &ctx->b, + .sgroup_cleanup = true + } + }, + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->x, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = false, + }, + .r2 = { + .owner = &ctx->x, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true, + } + }, + + /* + * sgroup,active vs. sgroup,tombstone different no addresses, special + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:NULL => B:NULL", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = 0, + .ips = NULL, + .apply_expected = true, + } + }, + /* + * sgroup,active vs. sgroup,tombstone different addresses + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + .apply_expected = true, + } + }, + /* + * sgroup,active vs. sgroup,tombstone subset addresses + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:B_3_4 => B:B_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true, + } + }, + /* + * sgroup,active vs. sgroup,active same addresses + * => should be replaced + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .comment= "A:B_3_4_X_3_4 vs. B:B_3_4_X_3_4 => B:B_3_4_X_3_4", + .extra = true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + }, + .r2 = { + .owner = &ctx->b, + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4), + .ips = addresses_B_3_4_X_3_4, + .apply_expected = true, + } + }, + + /* + * This should be the last record in this array, + * we need to make sure the we leave a tombstoned unique entry + * owned by OWNER_A + */ + { + .line = __location__, + .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL), + .cleanup= true, + .r1 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + }, + .r2 = { + .owner = &ctx->a, + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_A_1), + .ips = addresses_A_1, + .apply_expected = true + } + }}; /* do not add entries here, this should be the last record! */ + + wins_name_r1 = &wins_name1; + wins_name_r2 = &wins_name2; + + torture_comment(tctx, "Test Replica Conflicts with different owners\n"); + + for(i=0; ret && i < ARRAY_SIZE(records); i++) { + + if (!records[i].extra && !records[i].cleanup) { + /* we should test the worst cases */ + if (records[i].r2.apply_expected && records[i].r1.ips==records[i].r2.ips) { + torture_comment(tctx, "(%s) Programmer error, invalid record[%u]: %s\n", + __location__, i, records[i].line); + return false; + } else if (!records[i].r2.apply_expected && records[i].r1.ips!=records[i].r2.ips) { + torture_comment(tctx, "(%s) Programmer error, invalid record[%u]: %s\n", + __location__, i, records[i].line); + return false; + } + } + + if (!records[i].cleanup) { + const char *expected; + const char *ips; + + if (records[i].r2.sgroup_merge) { + expected = "SGROUP_MERGE"; + } else if (records[i].r2.apply_expected) { + expected = "REPLACE"; + } else { + expected = "NOT REPLACE"; + } + + if (!records[i].r1.ips && !records[i].r2.ips) { + ips = "with no ip(s)"; + } else if (records[i].r1.ips==records[i].r2.ips) { + ips = "with same ip(s)"; + } else { + ips = "with different ip(s)"; + } + + torture_comment(tctx, "%s,%s%s vs. %s,%s%s %s => %s\n", + wrepl_name_type_string(records[i].r1.type), + wrepl_name_state_string(records[i].r1.state), + (records[i].r1.is_static?",static":""), + wrepl_name_type_string(records[i].r2.type), + wrepl_name_state_string(records[i].r2.state), + (records[i].r2.is_static?",static":""), + (records[i].comment?records[i].comment:ips), + expected); + } + + /* + * Setup R1 + */ + wins_name_r1->name = &records[i].name; + wins_name_r1->flags = WREPL_NAME_FLAGS(records[i].r1.type, + records[i].r1.state, + records[i].r1.node, + records[i].r1.is_static); + wins_name_r1->id = ++records[i].r1.owner->max_version; + if (wins_name_r1->flags & 2) { + wins_name_r1->addresses.addresses.num_ips = records[i].r1.num_ips; + wins_name_r1->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + records[i].r1.ips); + } else { + wins_name_r1->addresses.ip = records[i].r1.ips[0].ip; + } + wins_name_r1->unknown = "255.255.255.255"; + + /* now apply R1 */ + ret &= test_wrepl_update_one(tctx, ctx, records[i].r1.owner, wins_name_r1); + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner, + wins_name_r1, records[i].r1.apply_expected); + + /* + * Setup R2 + */ + wins_name_r2->name = &records[i].name; + wins_name_r2->flags = WREPL_NAME_FLAGS(records[i].r2.type, + records[i].r2.state, + records[i].r2.node, + records[i].r2.is_static); + wins_name_r2->id = ++records[i].r2.owner->max_version; + if (wins_name_r2->flags & 2) { + wins_name_r2->addresses.addresses.num_ips = records[i].r2.num_ips; + wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + records[i].r2.ips); + } else { + wins_name_r2->addresses.ip = records[i].r2.ips[0].ip; + } + wins_name_r2->unknown = "255.255.255.255"; + + /* now apply R2 */ + ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2); + if (records[i].r1.state == WREPL_STATE_RELEASED) { + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner, + wins_name_r1, false); + } else if (records[i].r2.sgroup_merge) { + ret &= test_wrepl_sgroup_merged(tctx, ctx, records[i].r2.merge_owner, + records[i].r1.owner, + records[i].r1.num_ips, records[i].r1.ips, + records[i].r2.owner, + records[i].r2.num_ips, records[i].r2.ips, + wins_name_r2); + } else if (records[i].r1.owner != records[i].r2.owner) { + bool _expected; + _expected = (records[i].r1.apply_expected && !records[i].r2.apply_expected); + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner, + wins_name_r1, _expected); + } + if (records[i].r2.state == WREPL_STATE_RELEASED) { + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, + wins_name_r2, false); + } else if (!records[i].r2.sgroup_merge) { + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, + wins_name_r2, records[i].r2.apply_expected); + } + + if (records[i].r2.sgroup_cleanup) { + if (!ret) { + torture_comment(tctx, "failed before sgroup_cleanup record[%u]: %s\n", i, records[i].line); + return ret; + } + + /* clean up the SGROUP record */ + wins_name_r1->name = &records[i].name; + wins_name_r1->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_ACTIVE, + WREPL_NODE_B, false); + wins_name_r1->id = ++records[i].r1.owner->max_version; + wins_name_r1->addresses.addresses.num_ips = 0; + wins_name_r1->addresses.addresses.ips = NULL; + wins_name_r1->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, records[i].r1.owner, wins_name_r1); + + /* here we test how names from an owner are deleted */ + if (records[i].r2.sgroup_merge && records[i].r2.num_ips) { + ret &= test_wrepl_sgroup_merged(tctx, ctx, NULL, + records[i].r2.owner, + records[i].r2.num_ips, records[i].r2.ips, + records[i].r1.owner, + 0, NULL, + wins_name_r2); + } + + /* clean up the SGROUP record */ + wins_name_r2->name = &records[i].name; + wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_ACTIVE, + WREPL_NODE_B, false); + wins_name_r2->id = ++records[i].r2.owner->max_version; + wins_name_r2->addresses.addresses.num_ips = 0; + wins_name_r2->addresses.addresses.ips = NULL; + wins_name_r2->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2); + + /* take ownership of the SGROUP record */ + wins_name_r2->name = &records[i].name; + wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_ACTIVE, + WREPL_NODE_B, false); + wins_name_r2->id = ++records[i].r2.owner->max_version; + wins_name_r2->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1); + wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + addresses_B_1); + wins_name_r2->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2); + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, wins_name_r2, true); + + /* overwrite the SGROUP record with unique,tombstone */ + wins_name_r2->name = &records[i].name; + wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_TOMBSTONE, + WREPL_NODE_B, false); + wins_name_r2->id = ++records[i].r2.owner->max_version; + wins_name_r2->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1); + wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + addresses_B_1); + wins_name_r2->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2); + ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, wins_name_r2, true); + + if (!ret) { + torture_comment(tctx, "failed in sgroup_cleanup record[%u]: %s\n", i, records[i].line); + return ret; + } + } + + /* the first one is a cleanup run */ + if (!ret && i == 0) ret = true; + + if (!ret) { + torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line); + return ret; + } + } + + return ret; +} + +static bool test_conflict_owned_released_vs_replica(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx) +{ + bool ret = true; + NTSTATUS status; + struct wrepl_wins_name wins_name_; + struct wrepl_wins_name *wins_name = &wins_name_; + struct nbt_name_register name_register_; + struct nbt_name_register *name_register = &name_register_; + struct nbt_name_release release_; + struct nbt_name_release *release = &release_; + uint32_t i; + struct { + const char *line; /* just better debugging */ + struct nbt_name name; + struct { + uint32_t nb_flags; + bool mhomed; + uint32_t num_ips; + const struct wrepl_ip *ips; + bool apply_expected; + } wins; + struct { + enum wrepl_name_type type; + enum wrepl_name_state state; + enum wrepl_name_node node; + bool is_static; + uint32_t num_ips; + const struct wrepl_ip *ips; + bool apply_expected; + } replica; + } records[] = { +/* + * unique vs. unique section + */ + /* + * unique,released vs. unique,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_UA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. unique,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_UA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,released vs. unique,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_UT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. unique,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_UT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * unique vs. group section + */ + /* + * unique,released vs. group,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_GA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. group,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_GA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,released vs. group,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_GT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. group,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_GT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * unique vs. special group section + */ + /* + * unique,released vs. sgroup,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_SA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. sgroup,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_SA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,released vs. sgroup,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_ST_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. sgroup,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_ST_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * unique vs. multi homed section + */ + /* + * unique,released vs. mhomed,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_MA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. mhomed,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_MA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,released vs. mhomed,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_MT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,released vs. mhomed,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_UR_MT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * group vs. unique section + */ + /* + * group,released vs. unique,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_UA_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. unique,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_UA_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * group,released vs. unique,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_UT_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. unique,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_UT_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * group vs. group section + */ + /* + * group,released vs. group,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_GA_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * group,released vs. group,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_GA_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * group,released vs. group,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_GT_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * group,released vs. group,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_GT_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * group vs. special group section + */ + /* + * group,released vs. sgroup,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_SA_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. sgroup,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_SA_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * group,released vs. sgroup,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_ST_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. sgroup,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_ST_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * group vs. multi homed section + */ + /* + * group,released vs. mhomed,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_MA_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. mhomed,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_MA_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * group,released vs. mhomed,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_MT_SI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,released vs. mhomed,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_GR_MT_DI", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * special group vs. unique section + */ + /* + * sgroup,released vs. unique,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_UA_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. unique,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_UA_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. unique,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_UT_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. unique,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_UT_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * special group vs. group section + */ + /* + * sgroup,released vs. group,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_GA_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. group,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_GA_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. group,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_GT_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. group,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_GT_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * special group vs. special group section + */ + /* + * sgroup,released vs. sgroup,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_SA_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. sgroup,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_SA_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. sgroup,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_ST_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. sgroup,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_ST_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * special group vs. multi homed section + */ + /* + * sgroup,released vs. mhomed,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_MA_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. mhomed,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_MA_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. mhomed,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_MT_SI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * sgroup,released vs. mhomed,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_SR_MT_DI", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * multi homed vs. unique section + */ + /* + * mhomed,released vs. unique,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_UA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. unique,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_UA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. unique,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_UT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. unique,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_UT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * multi homed vs. group section + */ + /* + * mhomed,released vs. group,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_GA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. group,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_GA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. group,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_GT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. group,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_GT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * multi homed vs. special group section + */ + /* + * mhomed,released vs. sgroup,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_SA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. sgroup,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_SA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. sgroup,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_ST_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. sgroup,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_ST_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, +/* + * multi homed vs. multi homed section + */ + /* + * mhomed,released vs. mhomed,active with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_MA_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. mhomed,active with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_MA_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. mhomed,tombstone with same ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_MT_SI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,released vs. mhomed,tombstone with different ip(s) + */ + { + .line = __location__, + .name = _NBT_NAME("_MR_MT_DI", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + }; + + torture_comment(tctx, "Test Replica records vs. owned released records\n"); + + for(i=0; ret && i < ARRAY_SIZE(records); i++) { + torture_comment(tctx, "%s => %s\n", nbt_name_string(ctx, &records[i].name), + (records[i].replica.apply_expected?"REPLACE":"NOT REPLACE")); + + /* + * Setup Register + */ + name_register->in.name = records[i].name; + name_register->in.dest_addr = ctx->address; + name_register->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + name_register->in.address = records[i].wins.ips[0].ip; + name_register->in.nb_flags = records[i].wins.nb_flags; + name_register->in.register_demand= false; + name_register->in.broadcast = false; + name_register->in.multi_homed = records[i].wins.mhomed; + name_register->in.ttl = 300000; + name_register->in.timeout = 70; + name_register->in.retries = 0; + + status = nbt_name_register(ctx->nbtsock, ctx, name_register); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_comment(tctx, "No response from %s for name register\n", ctx->address); + ret = false; + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Bad response from %s for name register - %s\n", + ctx->address, nt_errstr(status)); + ret = false; + } + CHECK_VALUE(tctx, name_register->out.rcode, 0); + CHECK_VALUE_STRING(tctx, name_register->out.reply_from, ctx->address); + CHECK_VALUE(tctx, name_register->out.name.type, records[i].name.type); + CHECK_VALUE_STRING(tctx, name_register->out.name.name, records[i].name.name); + CHECK_VALUE_STRING(tctx, name_register->out.name.scope, records[i].name.scope); + CHECK_VALUE_STRING(tctx, name_register->out.reply_addr, records[i].wins.ips[0].ip); + + /* release the record */ + release->in.name = records[i].name; + release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release->in.dest_addr = ctx->address; + release->in.address = records[i].wins.ips[0].ip; + release->in.nb_flags = records[i].wins.nb_flags; + release->in.broadcast = false; + release->in.timeout = 30; + release->in.retries = 0; + + status = nbt_name_release(ctx->nbtsock, ctx, release); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_comment(tctx, "No response from %s for name release\n", ctx->address); + return false; + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Bad response from %s for name query - %s\n", + ctx->address, nt_errstr(status)); + return false; + } + CHECK_VALUE(tctx, release->out.rcode, 0); + + /* + * Setup Replica + */ + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(records[i].replica.type, + records[i].replica.state, + records[i].replica.node, + records[i].replica.is_static); + wins_name->id = ++ctx->b.max_version; + if (wins_name->flags & 2) { + wins_name->addresses.addresses.num_ips = records[i].replica.num_ips; + wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + records[i].replica.ips); + } else { + wins_name->addresses.ip = records[i].replica.ips[0].ip; + } + wins_name->unknown = "255.255.255.255"; + + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, + records[i].replica.apply_expected); + + if (records[i].replica.apply_expected) { + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE, + WREPL_STATE_TOMBSTONE, + WREPL_NODE_B, false); + wins_name->id = ++ctx->b.max_version; + wins_name->addresses.ip = addresses_B_1[0].ip; + wins_name->unknown = "255.255.255.255"; + + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true); + } else { + release->in.name = records[i].name; + release->in.dest_addr = ctx->address; + release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release->in.address = records[i].wins.ips[0].ip; + release->in.nb_flags = records[i].wins.nb_flags; + release->in.broadcast = false; + release->in.timeout = 30; + release->in.retries = 0; + + status = nbt_name_release(ctx->nbtsock, ctx, release); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_comment(tctx, "No response from %s for name release\n", ctx->address); + return false; + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Bad response from %s for name query - %s\n", + ctx->address, nt_errstr(status)); + return false; + } + CHECK_VALUE(tctx, release->out.rcode, 0); + } + if (!ret) { + torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line); + return ret; + } + } + + return ret; +} + +struct test_conflict_owned_active_vs_replica_struct { + struct torture_context *tctx; + const char *line; /* just better debugging */ + const char *section; /* just better debugging */ + struct nbt_name name; + const char *comment; + bool skip; + struct { + uint32_t nb_flags; + bool mhomed; + uint32_t num_ips; + const struct wrepl_ip *ips; + bool apply_expected; + } wins; + struct { + uint32_t timeout; + bool positive; + bool expect_release; + bool late_release; + bool ret; + /* when num_ips == 0, then .wins.ips are used */ + uint32_t num_ips; + const struct wrepl_ip *ips; + } defend; + struct { + enum wrepl_name_type type; + enum wrepl_name_state state; + enum wrepl_name_node node; + bool is_static; + uint32_t num_ips; + const struct wrepl_ip *ips; + bool apply_expected; + bool mhomed_merge; + bool sgroup_merge; + } replica; +}; + +static void test_conflict_owned_active_vs_replica_handler(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *req_packet, + struct socket_address *src); + +static bool test_conflict_owned_active_vs_replica(struct torture_context *tctx, + struct test_wrepl_conflict_conn *ctx) +{ + bool ret = true; + NTSTATUS status; + struct wrepl_wins_name wins_name_; + struct wrepl_wins_name *wins_name = &wins_name_; + struct nbt_name_register name_register_; + struct nbt_name_register *name_register = &name_register_; + struct nbt_name_release release_; + struct nbt_name_release *release = &release_; + uint32_t i; + struct test_conflict_owned_active_vs_replica_struct records[] = { +/* + * unique vs. unique section + */ + /* + * unique,active vs. unique,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,active vs. unique,active with different ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_DI_P", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * unique,active vs. unique,active with different ip(s), positive response other ips + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_DI_O", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * unique,active vs. unique,active with different ip(s), negative response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_DI_N", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = false, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,active vs. unique,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * unique,active vs. unique,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * unique vs. group section + */ + /* + * unique,active vs. group,active with same ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_GA_SI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,active vs. group,active with different ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_GA_DI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,active vs. group,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_GT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * unique,active vs. group,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_GT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * unique vs. special group section + */ + /* + * unique,active vs. sgroup,active with same ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_SA_SI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,active vs. group,active with different ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_SA_DI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * unique,active vs. sgroup,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_ST_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * unique,active vs. sgroup,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_ST_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * unique vs. multi homed section + */ + /* + * unique,active vs. mhomed,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * unique,active vs. mhomed,active with superset ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_SP_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + .apply_expected = true + }, + }, + /* + * unique,active vs. mhomed,active with different ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_DI_P", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * unique,active vs. mhomed,active with different ip(s), positive response other ips + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_DI_O", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * unique,active vs. mhomed,active with different ip(s), negative response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_DI_N", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = false, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + }, + }, + /* + * unique,active vs. mhomed,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * unique,active vs. mhomed,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, +/* + * normal group vs. unique section + */ + /* + * group,active vs. unique,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_UA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. unique,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_UA_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * group,active vs. unique,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_UT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. unique,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_UT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * normal group vs. normal group section + */ + /* + * group,active vs. group,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_GA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * group,active vs. group,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_GA_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * group,active vs. group,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_GT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. group,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_GT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * normal group vs. special group section + */ + /* + * group,active vs. sgroup,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_SA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. sgroup,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_SA_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * group,active vs. sgroup,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_ST_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. sgroup,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_ST_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, +/* + * normal group vs. multi homed section + */ + /* + * group,active vs. mhomed,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_MA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. mhomed,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_MA_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * group,active vs. mhomed,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_MT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * group,active vs. mhomed,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_GA_MT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, +/* + * special group vs. unique section + */ + /* + * sgroup,active vs. unique,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_UA_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. unique,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_UA_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. unique,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_UT_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. unique,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_UT_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * special group vs. normal group section + */ + /* + * sgroup,active vs. group,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_GA_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. group,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_GA_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. group,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_GT_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. group,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_GT_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * special group vs. multi homed section + */ + /* + * sgroup,active vs. mhomed,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_MA_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. mhomed,active with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_MA_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. mhomed,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_MT_SI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. mhomed,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_MT_DI_U", 0x1C, NULL), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * multi homed vs. unique section + */ + /* + * mhomed,active vs. unique,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. unique,active with different ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UA_DI_P", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. unique,active with different ip(s), positive response other ips + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UA_DI_O", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. unique,active with different ip(s), negative response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UA_DI_N", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = false, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. unique,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. unique,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_UT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * multi homed vs. normal group section + */ + /* + * mhomed,active vs. group,active with same ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_GA_SI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. group,active with different ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_GA_DI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. group,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_GT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. group,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_GT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_GROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * multi homed vs. special group section + */ + /* + * mhomed,active vs. sgroup,active with same ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_SA_SI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. group,active with different ip(s), release expected + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_SA_DI_R", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .expect_release = true, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. sgroup,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_ST_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. sgroup,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_ST_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_1), + .ips = addresses_B_1, + .apply_expected = false + }, + }, +/* + * multi homed vs. multi homed section + */ + /* + * mhomed,active vs. mhomed,active with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. mhomed,active with superset ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SP_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. mhomed,active with different ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_DI_P", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. mhomed,active with different ip(s), positive response other ips + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_DI_O", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ARRAY_SIZE(addresses_A_3_4), + .ips = addresses_A_3_4, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. mhomed,active with different ip(s), negative response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_DI_N", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = false, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. mhomed,tombstone with same ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MT_SI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. mhomed,tombstone with different ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MT_DI_U", 0x00, NULL), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, +/* + * some more multi homed test, including merging + */ + /* + * mhomed,active vs. mhomed,active with superset ip(s), unchecked + */ + { + .tctx = tctx, + .line = __location__, + .section= "Test Replica vs. owned active: some more MHOMED combinations", + .name = _NBT_NAME("_MA_MA_SP_U", 0x00, NULL), + .comment= "C:MHOMED vs. B:ALL => B:ALL", + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. mhomed,active with same ips, unchecked + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SM_U", 0x00, NULL), + .comment= "C:MHOMED vs. B:MHOMED => B:MHOMED", + .skip = (ctx->addresses_mhomed_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + }, + /* + * mhomed,active vs. mhomed,active with subset ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SB_P", 0x00, NULL), + .comment= "C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED", + .skip = (ctx->addresses_mhomed_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .mhomed_merge = true + }, + }, + /* + * mhomed,active vs. mhomed,active with subset ip(s), positive response, with all addresses + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SB_A", 0x00, NULL), + .comment= "C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED", + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .mhomed_merge = true + }, + }, + /* + * mhomed,active vs. mhomed,active with subset ip(s), positive response, with replicas addresses + * TODO: check why the server sends a name release demand for one address? + * the release demand has no effect to the database record... + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SB_PRA", 0x00, NULL), + .comment= "C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED", + .skip = (ctx->addresses_all_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .late_release = true + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. mhomed,active with subset ip(s), positive response, with other addresses + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SB_O", 0x00, NULL), + .comment= "C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED", + .skip = (ctx->addresses_all_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + /* + * mhomed,active vs. mhomed,active with subset ip(s), negative response + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_MA_MA_SB_N", 0x00, NULL), + .comment= "C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST", + .skip = (ctx->addresses_mhomed_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = false + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + }, +/* + * some more multi homed and unique test, including merging + */ + /* + * mhomed,active vs. unique,active with subset ip(s), positive response + */ + { + .tctx = tctx, + .line = __location__, + .section= "Test Replica vs. owned active: some more UNIQUE,MHOMED combinations", + .name = _NBT_NAME("_MA_UA_SB_P", 0x00, NULL), + .comment= "C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED", + .skip = (ctx->addresses_all_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = true, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .mhomed_merge = true + }, + }, + /* + * unique,active vs. unique,active with different ip(s), positive response, with replicas address + * TODO: check why the server sends a name release demand for one address? + * the release demand has no effect to the database record... + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_DI_PRA", 0x00, NULL), + .comment= "C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST", + .skip = (ctx->addresses_all_num < 2), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ctx->addresses_best2_num, + .ips = ctx->addresses_best2, + .late_release = true + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best2_num, + .ips = ctx->addresses_best2, + .apply_expected = false, + }, + }, + /* + * unique,active vs. unique,active with different ip(s), positive response, with all addresses + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_UA_DI_A", 0x00, NULL), + .comment= "C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED", + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + }, + .replica= { + .type = WREPL_TYPE_UNIQUE, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best2_num, + .ips = ctx->addresses_best2, + .mhomed_merge = true, + }, + }, + /* + * unique,active vs. mhomed,active with different ip(s), positive response, with all addresses + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_UA_MA_DI_A", 0x00, NULL), + .comment= "C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED", + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = 0, + .mhomed = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = true + }, + .defend = { + .timeout = 10, + .positive = true, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + }, + .replica= { + .type = WREPL_TYPE_MHOMED, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best2_num, + .ips = ctx->addresses_best2, + .mhomed_merge = true, + }, + }, +/* + * special group vs. special group merging section + */ + /* + * sgroup,active vs. sgroup,active with different ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .section= "Test Replica vs. owned active: SGROUP vs. SGROUP tests", + .name = _NBT_NAME("_SA_SA_DI_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .sgroup_merge = true + }, + }, + /* + * sgroup,active vs. sgroup,active with same ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_SA_SI_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .sgroup_merge = true + }, + }, + /* + * sgroup,active vs. sgroup,active with superset ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_SA_SP_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + .sgroup_merge = true + }, + }, + /* + * sgroup,active vs. sgroup,active with subset ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_SA_SB_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_ACTIVE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .sgroup_merge = true + }, + }, + /* + * sgroup,active vs. sgroup,tombstone with different ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_ST_DI_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ARRAY_SIZE(addresses_B_3_4), + .ips = addresses_B_3_4, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. sgroup,tombstone with same ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_ST_SI_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. sgroup,tombstone with superset ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_ST_SP_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_all_num, + .ips = ctx->addresses_all, + .apply_expected = false + }, + }, + /* + * sgroup,active vs. sgroup,tombstone with subset ip(s) + */ + { + .tctx = tctx, + .line = __location__, + .name = _NBT_NAME("_SA_ST_SB_U", 0x1C, NULL), + .skip = (ctx->addresses_all_num < 3), + .wins = { + .nb_flags = NBT_NM_GROUP, + .mhomed = false, + .num_ips = ctx->addresses_mhomed_num, + .ips = ctx->addresses_mhomed, + .apply_expected = true + }, + .defend = { + .timeout = 0, + }, + .replica= { + .type = WREPL_TYPE_SGROUP, + .state = WREPL_STATE_TOMBSTONE, + .node = WREPL_NODE_B, + .is_static = false, + .num_ips = ctx->addresses_best_num, + .ips = ctx->addresses_best, + .apply_expected = false + }, + }, + }; + + if (!ctx->nbtsock_srv) { + torture_comment(tctx, "SKIP: Test Replica records vs. owned active records: not bound to port[%d]\n", + lpcfg_nbt_port(tctx->lp_ctx)); + return true; + } + + torture_comment(tctx, "Test Replica records vs. owned active records\n"); + + for(i=0; ret && i < ARRAY_SIZE(records); i++) { + struct timeval end; + struct test_conflict_owned_active_vs_replica_struct record = records[i]; + uint32_t j, count = 1; + const char *action; + + if (records[i].wins.mhomed || records[i].name.type == 0x1C) { + count = records[i].wins.num_ips; + } + + if (records[i].section) { + torture_comment(tctx, "%s\n", records[i].section); + } + + if (records[i].skip) { + torture_comment(tctx, "%s => SKIPPED\n", nbt_name_string(ctx, &records[i].name)); + continue; + } + + if (records[i].replica.mhomed_merge) { + action = "MHOMED_MERGE"; + } else if (records[i].replica.sgroup_merge) { + action = "SGROUP_MERGE"; + } else if (records[i].replica.apply_expected) { + action = "REPLACE"; + } else { + action = "NOT REPLACE"; + } + + torture_comment(tctx, "%s%s%s => %s\n", + nbt_name_string(ctx, &records[i].name), + (records[i].comment?": ":""), + (records[i].comment?records[i].comment:""), + action); + + /* Prepare for multi homed registration */ + ZERO_STRUCT(records[i].defend); + records[i].defend.timeout = 10; + records[i].defend.positive = true; + nbt_set_incoming_handler(ctx->nbtsock_srv, + test_conflict_owned_active_vs_replica_handler, + &records[i]); + if (ctx->nbtsock_srv2) { + nbt_set_incoming_handler(ctx->nbtsock_srv2, + test_conflict_owned_active_vs_replica_handler, + &records[i]); + } + + /* + * Setup Register + */ + for (j=0; j < count; j++) { + struct nbt_name_request *req; + + name_register->in.name = records[i].name; + name_register->in.dest_addr = ctx->address; + name_register->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + name_register->in.address = records[i].wins.ips[j].ip; + name_register->in.nb_flags = records[i].wins.nb_flags; + name_register->in.register_demand= false; + name_register->in.broadcast = false; + name_register->in.multi_homed = records[i].wins.mhomed; + name_register->in.ttl = 300000; + name_register->in.timeout = 70; + name_register->in.retries = 0; + + req = nbt_name_register_send(ctx->nbtsock, name_register); + + /* push the request on the wire */ + tevent_loop_once(ctx->nbtsock->event_ctx); + + /* + * if we register multiple addresses, + * the server will do name queries to see if the old addresses + * are still alive + */ + if (records[i].wins.mhomed && j > 0) { + end = timeval_current_ofs(records[i].defend.timeout,0); + records[i].defend.ret = true; + while (records[i].defend.timeout > 0) { + tevent_loop_once(ctx->nbtsock_srv->event_ctx); + if (timeval_expired(&end)) break; + } + ret &= records[i].defend.ret; + } + + status = nbt_name_register_recv(req, ctx, name_register); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_comment(tctx, "No response from %s for name register\n", ctx->address); + ret = false; + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Bad response from %s for name register - %s\n", + ctx->address, nt_errstr(status)); + ret = false; + } + CHECK_VALUE(tctx, name_register->out.rcode, 0); + CHECK_VALUE_STRING(tctx, name_register->out.reply_from, ctx->address); + CHECK_VALUE(tctx, name_register->out.name.type, records[i].name.type); + CHECK_VALUE_STRING(tctx, name_register->out.name.name, records[i].name.name); + CHECK_VALUE_STRING(tctx, name_register->out.name.scope, records[i].name.scope); + CHECK_VALUE_STRING(tctx, name_register->out.reply_addr, records[i].wins.ips[j].ip); + } + + /* Prepare for the current test */ + records[i].defend = record.defend; + nbt_set_incoming_handler(ctx->nbtsock_srv, + test_conflict_owned_active_vs_replica_handler, + &records[i]); + if (ctx->nbtsock_srv2) { + nbt_set_incoming_handler(ctx->nbtsock_srv2, + test_conflict_owned_active_vs_replica_handler, + &records[i]); + } + + /* + * Setup Replica + */ + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(records[i].replica.type, + records[i].replica.state, + records[i].replica.node, + records[i].replica.is_static); + wins_name->id = ++ctx->b.max_version; + if (wins_name->flags & 2) { + wins_name->addresses.addresses.num_ips = records[i].replica.num_ips; + wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + records[i].replica.ips); + } else { + wins_name->addresses.ip = records[i].replica.ips[0].ip; + } + wins_name->unknown = "255.255.255.255"; + + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + + /* + * wait for the name query, which is handled in + * test_conflict_owned_active_vs_replica_handler() + */ + end = timeval_current_ofs(records[i].defend.timeout,0); + records[i].defend.ret = true; + while (records[i].defend.timeout > 0) { + tevent_loop_once(ctx->nbtsock_srv->event_ctx); + if (timeval_expired(&end)) break; + } + ret &= records[i].defend.ret; + + if (records[i].defend.late_release) { + records[i].defend = record.defend; + records[i].defend.expect_release = true; + /* + * wait for the name release demand, which is handled in + * test_conflict_owned_active_vs_replica_handler() + */ + end = timeval_current_ofs(records[i].defend.timeout,0); + records[i].defend.ret = true; + while (records[i].defend.timeout > 0) { + tevent_loop_once(ctx->nbtsock_srv->event_ctx); + if (timeval_expired(&end)) break; + } + ret &= records[i].defend.ret; + } + + if (records[i].replica.mhomed_merge) { + ret &= test_wrepl_mhomed_merged(tctx, ctx, &ctx->c, + records[i].wins.num_ips, records[i].wins.ips, + &ctx->b, + records[i].replica.num_ips, records[i].replica.ips, + wins_name); + } else if (records[i].replica.sgroup_merge) { + ret &= test_wrepl_sgroup_merged(tctx, ctx, NULL, + &ctx->c, + records[i].wins.num_ips, records[i].wins.ips, + &ctx->b, + records[i].replica.num_ips, records[i].replica.ips, + wins_name); + } else { + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, + records[i].replica.apply_expected); + } + + if (records[i].replica.apply_expected || + records[i].replica.mhomed_merge) { + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE, + WREPL_STATE_TOMBSTONE, + WREPL_NODE_B, false); + wins_name->id = ++ctx->b.max_version; + wins_name->addresses.ip = addresses_B_1[0].ip; + wins_name->unknown = "255.255.255.255"; + + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true); + } else { + for (j=0; j < count; j++) { + struct nbt_name_socket *nbtsock = ctx->nbtsock; + + if (ctx->myaddr2 && strcmp(records[i].wins.ips[j].ip, ctx->myaddr2->addr) == 0) { + nbtsock = ctx->nbtsock2; + } + + release->in.name = records[i].name; + release->in.dest_addr = ctx->address; + release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx); + release->in.address = records[i].wins.ips[j].ip; + release->in.nb_flags = records[i].wins.nb_flags; + release->in.broadcast = false; + release->in.timeout = 30; + release->in.retries = 0; + + status = nbt_name_release(nbtsock, ctx, release); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + torture_comment(tctx, "No response from %s for name release\n", ctx->address); + return false; + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Bad response from %s for name query - %s\n", + ctx->address, nt_errstr(status)); + return false; + } + CHECK_VALUE(tctx, release->out.rcode, 0); + } + + if (records[i].replica.sgroup_merge) { + /* clean up the SGROUP record */ + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_ACTIVE, + WREPL_NODE_B, false); + wins_name->id = ++ctx->b.max_version; + wins_name->addresses.addresses.num_ips = 0; + wins_name->addresses.addresses.ips = NULL; + wins_name->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + + /* take ownership of the SGROUP record */ + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP, + WREPL_STATE_ACTIVE, + WREPL_NODE_B, false); + wins_name->id = ++ctx->b.max_version; + wins_name->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1); + wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip, + addresses_B_1); + wins_name->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true); + + /* overwrite the SGROUP record with unique,tombstone */ + wins_name->name = &records[i].name; + wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE, + WREPL_STATE_TOMBSTONE, + WREPL_NODE_B, false); + wins_name->id = ++ctx->b.max_version; + wins_name->addresses.ip = addresses_A_1[0].ip; + wins_name->unknown = "255.255.255.255"; + ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name); + ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true); + } + } + + if (!ret) { + torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line); + return ret; + } + } + + return ret; +} + +#define __NBT_LABEL_CAT1__(a,b) a##b +#define __NBT_LABEL_CAT2__(a,b) __NBT_LABEL_CAT1__(a,b) +#define _NBT_LABEL __NBT_LABEL_CAT2__(_label_, __LINE__) + +#define _NBT_ASSERT(v, correct) do { \ + bool _ret = true; \ + torture_assert_int_equal_goto(rec->tctx, v, correct, \ + _ret, _NBT_LABEL, "Invalid int value"); \ +_NBT_LABEL: \ + if (!_ret) { \ + return; \ + } \ +} while (0) + +#define _NBT_ASSERT_STRING(v, correct) do { \ + bool _ret = true; \ + torture_assert_str_equal_goto(rec->tctx, v, correct, \ + _ret, _NBT_LABEL, "Invalid string value"); \ +_NBT_LABEL: \ + if (!_ret) { \ + return; \ + } \ +} while (0) + +static void test_conflict_owned_active_vs_replica_handler_query(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *req_packet, + struct socket_address *src) +{ + struct nbt_name *name; + struct nbt_name_packet *rep_packet; + struct test_conflict_owned_active_vs_replica_struct *rec = + (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data; + + _NBT_ASSERT(req_packet->qdcount, 1); + _NBT_ASSERT(req_packet->questions[0].question_type, NBT_QTYPE_NETBIOS); + _NBT_ASSERT(req_packet->questions[0].question_class, NBT_QCLASS_IP); + + name = &req_packet->questions[0].name; + + _NBT_ASSERT_STRING(name->name, rec->name.name); + _NBT_ASSERT(name->type, rec->name.type); + _NBT_ASSERT_STRING(name->scope, rec->name.scope); + + _NBT_ASSERT(rec->defend.expect_release, false); + + rep_packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (rep_packet == NULL) return; + + rep_packet->name_trn_id = req_packet->name_trn_id; + rep_packet->ancount = 1; + + rep_packet->answers = talloc_array(rep_packet, struct nbt_res_rec, 1); + if (rep_packet->answers == NULL) return; + + rep_packet->answers[0].name = *name; + rep_packet->answers[0].rr_class = NBT_QCLASS_IP; + rep_packet->answers[0].ttl = 0; + + if (rec->defend.positive) { + uint32_t i, num_ips; + const struct wrepl_ip *ips; + + if (rec->defend.num_ips > 0) { + num_ips = rec->defend.num_ips; + ips = rec->defend.ips; + } else { + num_ips = rec->wins.num_ips; + ips = rec->wins.ips; + } + + /* send a positive reply */ + rep_packet->operation = + NBT_FLAG_REPLY | + NBT_OPCODE_QUERY | + NBT_FLAG_AUTHORITATIVE | + NBT_FLAG_RECURSION_DESIRED | + NBT_FLAG_RECURSION_AVAIL; + + rep_packet->answers[0].rr_type = NBT_QTYPE_NETBIOS; + + rep_packet->answers[0].rdata.netbios.length = num_ips*6; + rep_packet->answers[0].rdata.netbios.addresses = + talloc_array(rep_packet->answers, struct nbt_rdata_address, num_ips); + if (rep_packet->answers[0].rdata.netbios.addresses == NULL) return; + + for (i=0; i < num_ips; i++) { + struct nbt_rdata_address *addr = + &rep_packet->answers[0].rdata.netbios.addresses[i]; + addr->nb_flags = rec->wins.nb_flags; + addr->ipaddr = ips[i].ip; + } + DEBUG(2,("Sending positive name query reply for %s to %s:%d\n", + nbt_name_string(rep_packet, name), src->addr, src->port)); + } else { + /* send a negative reply */ + rep_packet->operation = + NBT_FLAG_REPLY | + NBT_OPCODE_QUERY | + NBT_FLAG_AUTHORITATIVE | + NBT_RCODE_NAM; + + rep_packet->answers[0].rr_type = NBT_QTYPE_NULL; + + ZERO_STRUCT(rep_packet->answers[0].rdata); + + DEBUG(2,("Sending negative name query reply for %s to %s:%d\n", + nbt_name_string(rep_packet, name), src->addr, src->port)); + } + + nbt_name_reply_send(nbtsock, src, rep_packet); + talloc_free(rep_packet); + + /* make sure we push the reply to the wire */ + while (nbtsock->send_queue) { + tevent_loop_once(nbtsock->event_ctx); + } + smb_msleep(1000); + + rec->defend.timeout = 0; + rec->defend.ret = true; +} + +static void test_conflict_owned_active_vs_replica_handler_release( + struct nbt_name_socket *nbtsock, + struct nbt_name_packet *req_packet, + struct socket_address *src) +{ + struct nbt_name *name; + struct nbt_name_packet *rep_packet; + struct test_conflict_owned_active_vs_replica_struct *rec = + (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data; + + _NBT_ASSERT(req_packet->qdcount, 1); + _NBT_ASSERT(req_packet->questions[0].question_type, NBT_QTYPE_NETBIOS); + _NBT_ASSERT(req_packet->questions[0].question_class, NBT_QCLASS_IP); + + name = &req_packet->questions[0].name; + + _NBT_ASSERT_STRING(name->name, rec->name.name); + _NBT_ASSERT(name->type, rec->name.type); + _NBT_ASSERT_STRING(name->scope, rec->name.scope); + + _NBT_ASSERT(rec->defend.expect_release, true); + + rep_packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (rep_packet == NULL) return; + + rep_packet->name_trn_id = req_packet->name_trn_id; + rep_packet->ancount = 1; + rep_packet->operation = + NBT_FLAG_REPLY | + NBT_OPCODE_RELEASE | + NBT_FLAG_AUTHORITATIVE; + + rep_packet->answers = talloc_array(rep_packet, struct nbt_res_rec, 1); + if (rep_packet->answers == NULL) return; + + rep_packet->answers[0].name = *name; + rep_packet->answers[0].rr_type = NBT_QTYPE_NETBIOS; + rep_packet->answers[0].rr_class = NBT_QCLASS_IP; + rep_packet->answers[0].ttl = req_packet->additional[0].ttl; + rep_packet->answers[0].rdata = req_packet->additional[0].rdata; + + DEBUG(2,("Sending name release reply for %s to %s:%d\n", + nbt_name_string(rep_packet, name), src->addr, src->port)); + + nbt_name_reply_send(nbtsock, src, rep_packet); + talloc_free(rep_packet); + + /* make sure we push the reply to the wire */ + while (nbtsock->send_queue) { + tevent_loop_once(nbtsock->event_ctx); + } + smb_msleep(1000); + + rec->defend.timeout = 0; + rec->defend.ret = true; +} + +static void test_conflict_owned_active_vs_replica_handler(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *req_packet, + struct socket_address *src) +{ + struct test_conflict_owned_active_vs_replica_struct *rec = + (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data; + struct nbt_name *name = &req_packet->questions[0].name; + + if (req_packet->operation & NBT_FLAG_BROADCAST) { + torture_comment(rec->tctx, + "%s: incoming packet name[%s] flags[0x%08X] from[%s]\n", + __location__, + nbt_name_string(rec->tctx, name), + req_packet->operation, + src->addr); + return; + } + + rec->defend.ret = false; + + switch (req_packet->operation & NBT_OPCODE) { + case NBT_OPCODE_QUERY: + test_conflict_owned_active_vs_replica_handler_query(nbtsock, req_packet, src); + break; + case NBT_OPCODE_RELEASE: + test_conflict_owned_active_vs_replica_handler_release(nbtsock, req_packet, src); + break; + default: + torture_comment(rec->tctx, + "%s: unexpected packet name[%s] flags[0x%08X] from[%s]\n", + __location__, + nbt_name_string(rec->tctx, name), + req_packet->operation, + src->addr); + _NBT_ASSERT((req_packet->operation & NBT_OPCODE), NBT_OPCODE_QUERY); + break; + } +} + +/* + test WINS replication replica conflicts operations +*/ +static bool torture_nbt_winsreplication_replica(struct torture_context *tctx) +{ + bool ret = true; + struct test_wrepl_conflict_conn *ctx; + + const char *address; + struct nbt_name name; + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + ctx = test_create_conflict_ctx(tctx, address); + if (!ctx) return false; + + ret &= test_conflict_same_owner(tctx, ctx); + ret &= test_conflict_different_owner(tctx, ctx); + + return ret; +} + +/* + test WINS replication owned conflicts operations +*/ +static bool torture_nbt_winsreplication_owned(struct torture_context *tctx) +{ + const char *address; + struct nbt_name name; + bool ret = true; + struct test_wrepl_conflict_conn *ctx; + + if (torture_setting_bool(tctx, "quick", false)) + torture_skip(tctx, + "skip NBT-WINSREPLICATION-OWNED test in quick test mode\n"); + + if (!torture_nbt_get_name(tctx, &name, &address)) + return false; + + ctx = test_create_conflict_ctx(tctx, address); + torture_assert(tctx, ctx != NULL, "Creating context failed"); + + ret &= test_conflict_owned_released_vs_replica(tctx, ctx); + ret &= test_conflict_owned_active_vs_replica(tctx, ctx); + + return ret; +} + +/* + test simple WINS replication operations +*/ +struct torture_suite *torture_nbt_winsreplication(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create( + mem_ctx, "winsreplication"); + struct torture_tcase *tcase; + + tcase = torture_suite_add_simple_test(suite, "assoc_ctx1", + test_assoc_ctx1); + tcase->tests->dangerous = true; + + torture_suite_add_simple_test(suite, "assoc_ctx2", test_assoc_ctx2); + + torture_suite_add_simple_test(suite, "wins_replication", + test_wins_replication); + + torture_suite_add_simple_test(suite, "replica", + torture_nbt_winsreplication_replica); + + torture_suite_add_simple_test(suite, "owned", + torture_nbt_winsreplication_owned); + + return suite; +} diff --git a/source4/torture/ndr/README b/source4/torture/ndr/README new file mode 100644 index 0000000..c7c127d --- /dev/null +++ b/source4/torture/ndr/README @@ -0,0 +1,21 @@ +use + hexdump -v -e '12/1 "0x%02x, " "\n"' infile|outfile + +to import ndr dumps + + +Or use gdb: + +(gdb) b dump_printer +Breakpoint 1 at 0x49c92f: file ../source3/utils/net_printing.c, line 158. +(gdb) cond 1 strcmp(key_name, "s0bc") == 0 +(gdb) run +Breakpoint 1, dump_printer (mem_ctx=0x700a20, key_name=0x11fb8f9 "s0bc", data=0x18f93d0 "H\032", length=1284, do_string_conversion=true) at ../source3/utils/net_printing.c:158 +158 printf("found printer: %s\n", key_name); + +-> Now use x/bx + +(gdb) x/1284bx data + +This prints data as hex values. 1284 is the length in byte (see the length +argument of the function). The b indicates byte. diff --git a/source4/torture/ndr/atsvc.c b/source4/torture/ndr/atsvc.c new file mode 100644 index 0000000..0fdbe49 --- /dev/null +++ b/source4/torture/ndr/atsvc.c @@ -0,0 +1,215 @@ +/* + Unix SMB/CIFS implementation. + test suite for atsvc ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_atsvc.h" +#include "torture/ndr/proto.h" + +static const uint8_t jobenum_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool jobenum_in_check(struct torture_context *tctx, + struct atsvc_JobEnum *r) +{ + torture_assert(tctx, r->in.servername != NULL, "servername ptr"); + torture_assert_str_equal(tctx, r->in.servername, "WIN2KDC1", "servername"); + torture_assert_int_equal(tctx, r->in.ctr->entries_read, 0, "ctr entries read"); + torture_assert(tctx, r->in.ctr->first_entry == NULL, "ctr entries first_entry"); + torture_assert_int_equal(tctx, r->in.preferred_max_len, -1, "preferred max len"); + torture_assert(tctx, r->in.resume_handle != NULL, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->in.resume_handle, 0, "resume handle"); + return true; +} + +static const uint8_t jobenum_out_data[] = { + 0x07, 0x00, 0x00, 0x00, 0x28, 0x14, 0x0a, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x13, 0x00, 0x00, 0x40, 0x18, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, + 0x30, 0x18, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0xc0, 0xe4, 0x0a, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x20, 0x18, 0x0a, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x13, 0x00, 0x00, 0x10, 0x18, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, + 0x00, 0x18, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, 0xc0, 0xe4, 0x0a, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0xf0, 0x17, 0x0a, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x13, 0x00, 0x00, 0xe0, 0x17, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, + 0x6f, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x61, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x2e, 0x00, + 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, + 0x6f, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x2e, 0x00, + 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, + 0x6f, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xac, 0x34, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool jobenum_out_check(struct torture_context *tctx, + struct atsvc_JobEnum *r) +{ + torture_assert_int_equal(tctx, r->out.ctr->entries_read, 7, "entries read"); + torture_assert(tctx, r->out.ctr->first_entry != NULL, "first entry"); + torture_assert_int_equal(tctx, r->out.ctr->first_entry[0].job_id, 1, "job id"); + torture_assert_int_equal(tctx, r->out.ctr->first_entry[0].job_time, 84600000, "job time"); + torture_assert_int_equal(tctx, r->out.ctr->first_entry[0].days_of_week, 0x2, "days of week"); + torture_assert_int_equal(tctx, r->out.ctr->first_entry[0].flags, 0x13, "flags"); + torture_assert_str_equal(tctx, r->out.ctr->first_entry[0].command, "foo.exe", "command"); + torture_assert(tctx, r->out.total_entries != NULL, "total entries ptr"); + torture_assert_int_equal(tctx, *r->out.total_entries, 7, "total entries"); + torture_assert(tctx, r->out.resume_handle, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->out.resume_handle, 0, "resume handle"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t jobadd_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x2e, 0x00, + 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00 +}; + +static bool jobadd_in_check(struct torture_context *tctx, + struct atsvc_JobAdd *r) +{ + torture_assert_str_equal(tctx, r->in.servername, "WIN2KDC1", "servername"); + torture_assert_int_equal(tctx, r->in.job_info->job_time, 84600000, "time"); + torture_assert_int_equal(tctx, r->in.job_info->days_of_month, 0, "days of month"); + torture_assert_int_equal(tctx, r->in.job_info->days_of_week, 0x2, "days of week"); + torture_assert_int_equal(tctx, r->in.job_info->flags, 17, "flags"); + torture_assert_str_equal(tctx, r->in.job_info->command, "foo.exe", "command"); + + return true; +} + +static const uint8_t jobadd_out_data[] = { + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool jobadd_out_check(struct torture_context *tctx, + struct atsvc_JobAdd *r) +{ + torture_assert_int_equal(tctx, *r->out.job_id, 14, "job id"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t jobdel_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00 +}; + +static bool jobdel_in_check(struct torture_context *tctx, + struct atsvc_JobDel *r) +{ + torture_assert_str_equal(tctx, r->in.servername, "WIN2KDC1", "servername"); + torture_assert_int_equal(tctx, r->in.min_job_id, 14, "min job id"); + torture_assert_int_equal(tctx, r->in.max_job_id, 14, "max job id"); + return true; +} + +static const uint8_t jobdel_out_data[] = { + 0xde, 0x0e, 0x00, 0x00 +}; + +static bool jobdel_out_check(struct torture_context *tctx, + struct atsvc_JobDel *r) +{ + /* FIXME: Check for unknown code 0x00000ede */ + return true; +} + +static const uint8_t jobgetinfo_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00 +}; + +static bool jobgetinfo_in_check(struct torture_context *tctx, + struct atsvc_JobGetInfo *r) +{ + torture_assert_str_equal(tctx, r->in.servername, "WIN2KDC1", "servername"); + torture_assert_int_equal(tctx, r->in.job_id, 1, "job id"); + return true; +} + +static const uint8_t jobgetinfo_out_data[] = { + 0x88, 0xe2, 0x09, 0x00, 0xc0, 0xe4, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x13, 0x09, 0x00, 0x98, 0xe2, 0x09, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x00, 0x6f, 0x00, + 0x6f, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool jobgetinfo_out_check(struct torture_context *tctx, + struct atsvc_JobGetInfo *r) +{ + torture_assert(tctx, *r->out.job_info != NULL, "job info"); + torture_assert_int_equal(tctx, (*r->out.job_info)->job_time, 84600000, "time"); + torture_assert_int_equal(tctx, (*r->out.job_info)->days_of_month, 0, "days of month"); + torture_assert_int_equal(tctx, (*r->out.job_info)->days_of_week, 0x2, "days of week"); + torture_assert_int_equal(tctx, (*r->out.job_info)->flags, 0x13, "flags"); + torture_assert_str_equal(tctx, (*r->out.job_info)->command, "foo.exe", "command"); + return true; +} + +struct torture_suite *ndr_atsvc_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "atsvc"); + + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobEnum, jobenum_in_data, NDR_IN, jobenum_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobEnum, jobenum_out_data, NDR_OUT, jobenum_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobAdd, jobadd_in_data, NDR_IN, jobadd_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobAdd, jobadd_out_data, NDR_OUT, jobadd_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobDel, jobdel_in_data, NDR_IN, jobdel_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobDel, jobdel_out_data, NDR_OUT, jobdel_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobGetInfo, jobgetinfo_in_data, NDR_IN, jobgetinfo_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, atsvc_JobGetInfo, jobgetinfo_out_data, NDR_OUT, jobgetinfo_out_check ); + + return suite; +} + diff --git a/source4/torture/ndr/backupkey.c b/source4/torture/ndr/backupkey.c new file mode 100644 index 0000000..1be9229 --- /dev/null +++ b/source4/torture/ndr/backupkey.c @@ -0,0 +1,163 @@ +/* + Unix SMB/CIFS implementation. + Test suite for ndr on backupkey + + Copyright (C) Matthieu Patou 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_backupkey.h" +#include "torture/ndr/proto.h" + + +static const uint8_t exported_rsa_ndr[] = { +0x02, 0x00, 0x00, 0x00, 0x94, 0x04, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, +0x00, 0xa4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x32, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, +0x21, 0x5d, 0x57, 0xa5, 0x84, 0xfc, 0xf4, 0x89, 0x47, 0x27, 0xfe, 0x71, 0xc1, 0x7b, 0x27, 0xb1, +0xfd, 0xe4, 0x1d, 0x7d, 0x0f, 0xe8, 0xdd, 0xc1, 0xe2, 0x95, 0x4e, 0x4a, 0xdb, 0xc7, 0x27, 0x31, +0xbd, 0x22, 0xd7, 0xfe, 0x42, 0x12, 0x94, 0x17, 0x5e, 0x94, 0x0e, 0xc6, 0xa5, 0xef, 0xf8, 0x20, +0x0a, 0x0f, 0xc7, 0xbf, 0x79, 0x1d, 0x12, 0x98, 0x7a, 0x21, 0xfc, 0x37, 0x9a, 0xe6, 0xec, 0x02, +0xf6, 0xdf, 0x08, 0x49, 0x62, 0xd2, 0xee, 0x37, 0xe4, 0x1c, 0x95, 0x05, 0xd2, 0xdf, 0x8b, 0x85, +0x80, 0x5f, 0xcb, 0x3f, 0x70, 0xb9, 0xf1, 0xf3, 0x4f, 0x8f, 0x6d, 0x73, 0xc9, 0x15, 0x2c, 0x97, +0xb2, 0x2c, 0x17, 0x6d, 0x8b, 0xe7, 0x9a, 0x58, 0x2c, 0xcb, 0xd0, 0x06, 0x5f, 0x48, 0x49, 0xf8, +0x80, 0x13, 0x25, 0x05, 0x2a, 0x5a, 0x71, 0x01, 0x39, 0x36, 0x89, 0x80, 0x70, 0xdb, 0x57, 0x1a, +0xe3, 0xd7, 0xcc, 0xbd, 0x9d, 0x1f, 0x1d, 0x92, 0x60, 0x63, 0x78, 0x57, 0xd0, 0x36, 0x42, 0x07, +0x7c, 0xdc, 0x58, 0x32, 0x6c, 0xa6, 0xfd, 0xc0, 0x85, 0x19, 0x5f, 0x32, 0x7b, 0xd6, 0x40, 0xa9, +0xf5, 0x1a, 0x9f, 0xec, 0x7a, 0x59, 0x10, 0x71, 0xaa, 0x22, 0x39, 0x34, 0xe1, 0xd3, 0x5b, 0x0f, +0x39, 0xd8, 0x57, 0xba, 0x59, 0x5f, 0xf3, 0xdd, 0x4d, 0x36, 0xde, 0xdc, 0x2c, 0xd3, 0x30, 0x6b, +0x55, 0xaa, 0x5a, 0x51, 0xf6, 0xef, 0x42, 0x5c, 0x01, 0x26, 0x2b, 0x42, 0xd3, 0xf4, 0x9c, 0x6b, +0x56, 0xb0, 0xd3, 0x80, 0x77, 0xb5, 0x80, 0xf3, 0x89, 0xbf, 0xc3, 0xd7, 0x71, 0x2e, 0x47, 0x3e, +0x86, 0x84, 0xec, 0x9f, 0xa0, 0x38, 0xbb, 0xe9, 0xce, 0x34, 0x7d, 0x9e, 0xf7, 0xf5, 0xe8, 0xfd, +0x90, 0x5b, 0xc1, 0x97, 0x2b, 0x08, 0x55, 0x4a, 0x57, 0x69, 0xfc, 0xd7, 0x26, 0x32, 0xd7, 0xaa, +0x7d, 0x66, 0x4f, 0x4d, 0x46, 0xcb, 0xc9, 0x83, 0x92, 0xd9, 0x56, 0xac, 0xb0, 0x5c, 0x0a, 0xd9, +0xeb, 0x38, 0xae, 0x24, 0x9a, 0xb0, 0x7d, 0x3c, 0x56, 0x0b, 0xbf, 0xca, 0xa9, 0xbc, 0x75, 0xad, +0x27, 0x2f, 0x9d, 0x16, 0xb5, 0xe5, 0xf3, 0xac, 0x5e, 0x0d, 0xe1, 0x9f, 0x67, 0xb9, 0x8d, 0xeb, +0x8a, 0xea, 0x2a, 0x00, 0xa5, 0x09, 0x35, 0x7b, 0xcc, 0xeb, 0x00, 0xdb, 0x46, 0x49, 0xac, 0x3c, +0x00, 0x7a, 0x1e, 0x31, 0x21, 0x66, 0x7b, 0xe6, 0x10, 0x04, 0x2b, 0x39, 0x21, 0xa9, 0xe3, 0xa0, +0x81, 0x9e, 0xeb, 0xbe, 0x04, 0xe6, 0xd4, 0x23, 0xdc, 0xca, 0x01, 0xd3, 0xfa, 0x73, 0xba, 0x75, +0x67, 0x61, 0x7d, 0x95, 0x12, 0x14, 0x4d, 0x1e, 0x08, 0x60, 0x0f, 0xbb, 0x06, 0xcf, 0xdc, 0x1c, +0x07, 0x3c, 0x61, 0xd9, 0x54, 0xc8, 0xca, 0x31, 0x1c, 0x5d, 0xc4, 0xf1, 0x01, 0x59, 0xfd, 0xdb, +0x75, 0x7e, 0xcc, 0xd0, 0xe7, 0x0a, 0x9a, 0x36, 0x1e, 0xc5, 0xf9, 0x95, 0x83, 0x4d, 0x3e, 0x47, +0x2e, 0x70, 0x12, 0x4e, 0x0e, 0x07, 0xe4, 0x56, 0x6f, 0xe7, 0xed, 0xbd, 0xe4, 0x48, 0x50, 0x0b, +0xa6, 0x49, 0x80, 0x1c, 0x85, 0xe7, 0x92, 0x02, 0x4d, 0xfa, 0xe3, 0x6d, 0x1f, 0xc1, 0xf7, 0xf9, +0xd0, 0x37, 0x68, 0x33, 0x85, 0x20, 0x71, 0x9c, 0xc2, 0xaa, 0x58, 0x47, 0xae, 0x39, 0x1b, 0x20, +0x3f, 0xc1, 0x90, 0x7c, 0x3d, 0xee, 0xc9, 0x9e, 0x1b, 0x07, 0xd3, 0x0a, 0x13, 0xd1, 0xca, 0x53, +0xd0, 0x2a, 0xf6, 0x2c, 0xa9, 0xd8, 0xef, 0xe3, 0xe0, 0x24, 0x3f, 0xf9, 0x13, 0xe3, 0xf1, 0xff, +0x7b, 0x47, 0x59, 0x09, 0xd5, 0x6d, 0x52, 0x95, 0x87, 0xcb, 0x44, 0x6e, 0xe8, 0xaa, 0x0c, 0xe4, +0xd1, 0xb3, 0xc9, 0xd8, 0xc0, 0xcf, 0x2b, 0x53, 0xf7, 0x8e, 0x93, 0xe9, 0xd8, 0x42, 0xce, 0xc6, +0x01, 0x67, 0x73, 0x44, 0x5a, 0x65, 0x36, 0x96, 0x9f, 0xd1, 0xfc, 0x03, 0x9b, 0xd1, 0x0d, 0x02, +0xc7, 0x1e, 0xe4, 0x93, 0xa6, 0x00, 0x6e, 0xfe, 0x6f, 0x8e, 0xf1, 0x93, 0x97, 0xdf, 0x28, 0xee, +0x3e, 0x07, 0xa1, 0x38, 0xe1, 0x1b, 0xa8, 0x77, 0x78, 0x0d, 0x86, 0x39, 0x20, 0xcb, 0x71, 0xd8, +0x2a, 0x63, 0x63, 0x00, 0xdf, 0x6e, 0xea, 0x44, 0xf1, 0xdc, 0x2e, 0xf0, 0x81, 0x41, 0x92, 0x55, +0xce, 0x2d, 0x7c, 0xc4, 0x7f, 0x94, 0xef, 0xc9, 0xe4, 0x26, 0x15, 0x7e, 0x59, 0x9b, 0xd0, 0x10, +0x64, 0xa6, 0x2e, 0xf7, 0x71, 0xcc, 0x5b, 0xcd, 0xa2, 0xf3, 0xe7, 0xe5, 0x56, 0xd0, 0x9d, 0xd0, +0xaf, 0x31, 0xfc, 0x08, 0xd6, 0xff, 0x87, 0xca, 0x27, 0xa9, 0xf0, 0xf1, 0xf8, 0x2d, 0x8d, 0x1c, +0xce, 0x48, 0xc9, 0x60, 0x50, 0x58, 0x56, 0x31, 0x6f, 0xa0, 0xbf, 0x8d, 0xf5, 0xcd, 0x3e, 0x0c, +0x11, 0xfc, 0x5c, 0x0b, 0xe3, 0x77, 0x9e, 0x44, 0x1e, 0x1d, 0xda, 0x67, 0x8a, 0x40, 0xf2, 0x89, +0x94, 0xd5, 0xec, 0xc9, 0xf6, 0xe8, 0xc5, 0x81, 0xd9, 0x28, 0x8b, 0x3f, 0x13, 0xc5, 0x0e, 0x3d, +0x7f, 0x81, 0x97, 0xac, 0x52, 0xee, 0xbb, 0xcb, 0xbb, 0x3d, 0x86, 0x35, 0x1f, 0x31, 0x1a, 0x97, +0x54, 0xf5, 0x04, 0xb4, 0x24, 0x21, 0x4e, 0xea, 0x05, 0xf3, 0x6d, 0x08, 0x20, 0xb5, 0xf7, 0x37, +0xf6, 0xd3, 0xff, 0x57, 0x7e, 0x85, 0xf1, 0x66, 0xba, 0xed, 0xf1, 0xe9, 0xbd, 0x5b, 0x91, 0xb2, +0x89, 0xe8, 0xdb, 0xac, 0x5b, 0x58, 0xa9, 0x0f, 0x03, 0x18, 0x38, 0x55, 0x80, 0x19, 0x04, 0x0d, +0xa8, 0x91, 0x8b, 0x3c, 0x65, 0x23, 0x78, 0xbc, 0x0e, 0x5b, 0xc5, 0x80, 0x6e, 0xad, 0x1f, 0x97, +0x28, 0xe5, 0x57, 0xff, 0xc9, 0x06, 0x7d, 0xa8, 0xbe, 0x65, 0xfc, 0xcd, 0x99, 0x04, 0x3a, 0x36, +0xe3, 0xe8, 0x41, 0xd5, 0x9b, 0x13, 0x98, 0xf6, 0x76, 0xfb, 0x23, 0x6b, 0x9f, 0x2b, 0xdb, 0x06, +0x25, 0xf3, 0x72, 0x33, 0x35, 0x92, 0x51, 0xb6, 0x49, 0x98, 0xee, 0x48, 0xc8, 0xad, 0x7b, 0x87, +0x4a, 0x3d, 0x86, 0x69, 0x1b, 0xd3, 0x15, 0xe3, 0x6c, 0xe9, 0x83, 0x73, 0x3b, 0x0f, 0x0d, 0x0e, +0xe2, 0x9c, 0xfe, 0xe6, 0xc0, 0x4d, 0xb9, 0xe4, 0x89, 0x56, 0x9d, 0xc0, 0x7a, 0x0c, 0xed, 0x1a, +0x70, 0x1f, 0x2c, 0x73, 0x05, 0x22, 0x19, 0x2c, 0x70, 0x94, 0x73, 0x3d, 0x91, 0xee, 0x2d, 0xff, +0x9c, 0x50, 0x94, 0x7b, 0x85, 0xaa, 0x42, 0xc0, 0xc9, 0xbf, 0xdc, 0xc5, 0x29, 0xaf, 0xca, 0x93, +0x43, 0xcc, 0xc8, 0x0c, 0x3e, 0x91, 0xce, 0xcd, 0x93, 0xe6, 0x0f, 0x76, 0xcc, 0x56, 0x7a, 0x44, +0x7c, 0x9d, 0x6b, 0xb6, 0x2d, 0xf7, 0x01, 0x3a, 0x72, 0xfb, 0x85, 0x2a, 0x37, 0xf2, 0x33, 0x0c, +0xc1, 0x2a, 0xb4, 0x6b, 0x4f, 0xdf, 0xcd, 0x78, 0xf8, 0x18, 0xd6, 0x1e, 0xb9, 0x2e, 0x17, 0xf5, +0xcb, 0x0e, 0xca, 0xc3, 0xf0, 0x5b, 0x2d, 0x61, 0x7b, 0xef, 0x37, 0xd7, 0x35, 0xf7, 0x90, 0x30, +0x64, 0x46, 0x43, 0x94, 0x56, 0x5b, 0xd1, 0x10, 0x10, 0xae, 0xa4, 0x20, 0xce, 0xb3, 0x91, 0x31, +0xbc, 0x06, 0xf2, 0xbc, 0xa0, 0x66, 0x52, 0xb5, 0xd3, 0x51, 0x6e, 0x24, 0x63, 0x3d, 0xaa, 0xa9, +0xa9, 0x8c, 0x74, 0xf3, 0x09, 0xbf, 0x01, 0x3f, 0x48, 0x0e, 0x4a, 0x87, 0x3d, 0x91, 0x96, 0x33, +0x17, 0x4d, 0x43, 0xe5, 0x71, 0x2c, 0x94, 0x64, 0x39, 0xe9, 0xdb, 0xdb, 0x05, 0x5f, 0x07, 0x38, +0xa3, 0x36, 0x7b, 0x79, 0x9a, 0x74, 0xf7, 0x0e, 0x15, 0x9f, 0x49, 0x65, 0x2d, 0xf5, 0x85, 0x6c, +0xc8, 0xbc, 0x42, 0x88, 0x93, 0xd9, 0x40, 0xfa, 0xbf, 0x14, 0x66, 0x68, 0x9e, 0x05, 0x64, 0x38, +0x4b, 0xb5, 0x97, 0x2c, 0x48, 0x90, 0x09, 0x8e, 0x60, 0xc0, 0x56, 0xd6, 0x44, 0x2f, 0x60, 0xe8, +0x0f, 0xa5, 0xf3, 0x95, 0xca, 0x9a, 0x09, 0x05, 0x8a, 0x3d, 0xaf, 0x01, 0x71, 0x66, 0x68, 0xa5, +0x06, 0x6e, 0x95, 0x33, 0x46, 0x9d, 0x45, 0xcb, 0x2c, 0xdd, 0x05, 0x8d, 0xbb, 0x8f, 0xa7, 0x5b, +0xec, 0x0b, 0x17, 0x54, 0xe3, 0xd0, 0x7d, 0xb9, 0x23, 0x77, 0xf3, 0xc6, 0x3e, 0x69, 0x2a, 0xf9, +0xe9, 0xcf, 0x83, 0xc4, 0x09, 0xa5, 0x2a, 0xd9, 0xb3, 0x4e, 0x3f, 0x16, 0x7d, 0xf7, 0x8f, 0xb3, +0xd0, 0x64, 0x2b, 0xc3, 0x0e, 0xb1, 0xf3, 0xdb, 0xe6, 0x4a, 0x7e, 0xf9, 0x3b, 0xc9, 0xca, 0x14, +0x9d, 0xf2, 0x61, 0xc3, 0x59, 0x05, 0xcf, 0x0d, 0x13, 0xcf, 0x2e, 0xa9, 0xa6, 0x59, 0x30, 0xa6, +0xef, 0x3a, 0x43, 0xbb, 0x63, 0x6f, 0x31, 0x7a, 0xfa, 0x38, 0x0d, 0xb1, 0x17, 0x4d, 0xc2, 0xa4, +0x30, 0x82, 0x03, 0x00, 0x30, 0x82, 0x01, 0xec, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0xbd, +0x76, 0xdf, 0x42, 0x47, 0x0a, 0x00, 0x8d, 0x47, 0x3e, 0x74, 0x3f, 0xa1, 0xdc, 0x8b, 0xbd, 0x30, +0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, 0x2d, 0x31, 0x2b, 0x30, 0x29, +0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x22, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x38, 0x00, 0x72, +0x00, 0x32, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x77, 0x00, 0x73, 0x00, 0x2e, +0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x00, 0x00, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, +0x34, 0x32, 0x38, 0x31, 0x31, 0x34, 0x31, 0x35, 0x34, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, +0x32, 0x38, 0x31, 0x31, 0x34, 0x31, 0x35, 0x34, 0x5a, 0x30, 0x2d, 0x31, 0x2b, 0x30, 0x29, 0x06, +0x03, 0x55, 0x04, 0x03, 0x13, 0x22, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x38, 0x00, 0x72, 0x00, +0x32, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x77, 0x00, 0x73, 0x00, 0x2e, 0x00, +0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x00, 0x00, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, +0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, +0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0xd7, 0x32, 0x26, 0xd7, 0xfc, 0x69, +0x57, 0x4a, 0x55, 0x08, 0x2b, 0x97, 0xc1, 0x5b, 0x90, 0xfd, 0xe8, 0xf5, 0xf7, 0x9e, 0x7d, 0x34, +0xce, 0xe9, 0xbb, 0x38, 0xa0, 0x9f, 0xec, 0x84, 0x86, 0x3e, 0x47, 0x2e, 0x71, 0xd7, 0xc3, 0xbf, +0x89, 0xf3, 0x80, 0xb5, 0x77, 0x80, 0xd3, 0xb0, 0x56, 0x6b, 0x9c, 0xf4, 0xd3, 0x42, 0x2b, 0x26, +0x01, 0x5c, 0x42, 0xef, 0xf6, 0x51, 0x5a, 0xaa, 0x55, 0x6b, 0x30, 0xd3, 0x2c, 0xdc, 0xde, 0x36, +0x4d, 0xdd, 0xf3, 0x5f, 0x59, 0xba, 0x57, 0xd8, 0x39, 0x0f, 0x5b, 0xd3, 0xe1, 0x34, 0x39, 0x22, +0xaa, 0x71, 0x10, 0x59, 0x7a, 0xec, 0x9f, 0x1a, 0xf5, 0xa9, 0x40, 0xd6, 0x7b, 0x32, 0x5f, 0x19, +0x85, 0xc0, 0xfd, 0xa6, 0x6c, 0x32, 0x58, 0xdc, 0x7c, 0x07, 0x42, 0x36, 0xd0, 0x57, 0x78, 0x63, +0x60, 0x92, 0x1d, 0x1f, 0x9d, 0xbd, 0xcc, 0xd7, 0xe3, 0x1a, 0x57, 0xdb, 0x70, 0x80, 0x89, 0x36, +0x39, 0x01, 0x71, 0x5a, 0x2a, 0x05, 0x25, 0x13, 0x80, 0xf8, 0x49, 0x48, 0x5f, 0x06, 0xd0, 0xcb, +0x2c, 0x58, 0x9a, 0xe7, 0x8b, 0x6d, 0x17, 0x2c, 0xb2, 0x97, 0x2c, 0x15, 0xc9, 0x73, 0x6d, 0x8f, +0x4f, 0xf3, 0xf1, 0xb9, 0x70, 0x3f, 0xcb, 0x5f, 0x80, 0x85, 0x8b, 0xdf, 0xd2, 0x05, 0x95, 0x1c, +0xe4, 0x37, 0xee, 0xd2, 0x62, 0x49, 0x08, 0xdf, 0xf6, 0x02, 0xec, 0xe6, 0x9a, 0x37, 0xfc, 0x21, +0x7a, 0x98, 0x12, 0x1d, 0x79, 0xbf, 0xc7, 0x0f, 0x0a, 0x20, 0xf8, 0xef, 0xa5, 0xc6, 0x0e, 0x94, +0x5e, 0x17, 0x94, 0x12, 0x42, 0xfe, 0xd7, 0x22, 0xbd, 0x31, 0x27, 0xc7, 0xdb, 0x4a, 0x4e, 0x95, +0xe2, 0xc1, 0xdd, 0xe8, 0x0f, 0x7d, 0x1d, 0xe4, 0xfd, 0xb1, 0x27, 0x7b, 0xc1, 0x71, 0xfe, 0x27, +0x47, 0x89, 0xf4, 0xfc, 0x84, 0xa5, 0x57, 0x5d, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, 0x81, 0x11, +0x00, 0xbd, 0x8b, 0xdc, 0xa1, 0x3f, 0x74, 0x3e, 0x47, 0x8d, 0x00, 0x0a, 0x47, 0x42, 0xdf, 0x76, +0xbd, 0x82, 0x11, 0x00, 0xbd, 0x8b, 0xdc, 0xa1, 0x3f, 0x74, 0x3e, 0x47, 0x8d, 0x00, 0x0a, 0x47, +0x42, 0xdf, 0x76, 0xbd, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, +0x82, 0x01, 0x01, 0x00, 0xa7, 0xb0, 0x66, 0x75, 0x14, 0x7e, 0x7d, 0xb5, 0x31, 0xec, 0xb2, 0xeb, +0x90, 0x80, 0x95, 0x25, 0x59, 0x0f, 0xe4, 0x15, 0x86, 0x2d, 0x9d, 0xd7, 0x35, 0xe9, 0x22, 0x74, +0xe7, 0x85, 0x36, 0x19, 0x4f, 0x27, 0x5c, 0x17, 0x63, 0x7b, 0x2a, 0xfe, 0x59, 0xe9, 0x76, 0x77, +0xd0, 0xc9, 0x40, 0x78, 0x7c, 0x31, 0x62, 0x1e, 0x87, 0x1b, 0xc1, 0x19, 0xef, 0x6f, 0x15, 0xe6, +0xce, 0x74, 0x84, 0x6d, 0xd6, 0x3b, 0x57, 0xd9, 0xa9, 0x13, 0xf6, 0x7d, 0x84, 0xe7, 0x8f, 0xc6, +0x01, 0x5f, 0xcf, 0xc4, 0x95, 0xc9, 0xde, 0x97, 0x17, 0x43, 0x12, 0x70, 0x27, 0xf9, 0xc4, 0xd7, +0xe1, 0x05, 0xbb, 0x63, 0x87, 0x5f, 0xdc, 0x20, 0xbd, 0xd1, 0xde, 0xd6, 0x2d, 0x9f, 0x3f, 0x5d, +0x0a, 0x27, 0x40, 0x11, 0x5f, 0x5d, 0x54, 0xa7, 0x28, 0xf9, 0x03, 0x2e, 0x84, 0x8d, 0x48, 0x60, +0xa1, 0x71, 0xa3, 0x46, 0x69, 0xdb, 0x88, 0x7b, 0xc1, 0xb6, 0x08, 0x2d, 0xdf, 0x25, 0x9d, 0x32, +0x76, 0x49, 0x0b, 0xba, 0xab, 0xdd, 0xc3, 0x00, 0x76, 0x8a, 0x94, 0xd2, 0x25, 0x43, 0xf0, 0xa9, +0x98, 0x65, 0x94, 0xc7, 0xdd, 0x7c, 0xd4, 0xe2, 0xe8, 0x33, 0xe2, 0x9a, 0xe9, 0x75, 0xf0, 0x0f, +0x61, 0x86, 0xee, 0x0e, 0xf7, 0x39, 0x6b, 0x30, 0x63, 0xe5, 0x46, 0xd4, 0x1c, 0x83, 0xa1, 0x28, +0x79, 0x76, 0x81, 0x48, 0x38, 0x72, 0xbc, 0x3f, 0x25, 0x53, 0x31, 0xaa, 0x02, 0xd1, 0x9b, 0x03, +0xa2, 0x5c, 0x94, 0x21, 0xb3, 0x8e, 0xdf, 0x2a, 0xa5, 0x4c, 0x65, 0xa2, 0xf9, 0xac, 0x38, 0x7a, +0xf9, 0x45, 0xb3, 0xd5, 0xda, 0xe5, 0xb9, 0x56, 0x9e, 0x47, 0xd5, 0x06, 0xe6, 0xca, 0xd7, 0x6e, +0x06, 0xdb, 0x6e, 0xa7, 0x7b, 0x4b, 0x13, 0x40, 0x3c, 0x12, 0x76, 0x99, 0x65, 0xb4, 0x54, 0xa1, +0xd8, 0x21, 0x5c, 0x27 +}; + +struct torture_suite *ndr_backupkey_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "backupkey"); + + torture_suite_add_ndr_pull_validate_test(suite, + bkrp_exported_RSA_key_pair, + exported_rsa_ndr, + NULL); + + return suite; +} diff --git a/source4/torture/ndr/cabinet.c b/source4/torture/ndr/cabinet.c new file mode 100644 index 0000000..5b93108 --- /dev/null +++ b/source4/torture/ndr/cabinet.c @@ -0,0 +1,4335 @@ +/* + Unix SMB/CIFS implementation. + test suite for Windows Cabinet files + + Copyright (C) Guenther Deschner 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_cab.h" +#include "torture/ndr/proto.h" + +static const uint8_t cab_file_plain_data[] = { + 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x49, + 0x09, 0x21, 0x20, 0x00, 0x62, 0x6c, 0x6f, 0x62, + 0x2d, 0x41, 0x2d, 0x33, 0x32, 0x37, 0x36, 0x38, + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41 +}; + +static bool cab_file_plain_check(struct torture_context *tctx, + struct cab_file *r) +{ + DATA_BLOB blob; + + torture_assert_str_equal(tctx, r->cfheader.signature, "MSCF", "signature"); + torture_assert_int_equal(tctx, r->cfheader.reserved1, 0, "reserved1"); + torture_assert_int_equal(tctx, r->cfheader.cbCabinet, 0x8051, "cbCabinet"); + torture_assert_int_equal(tctx, r->cfheader.reserved2, 0, "reserved2"); + torture_assert_int_equal(tctx, r->cfheader.coffFiles, 44, "coffFiles"); + torture_assert_int_equal(tctx, r->cfheader.reserved3, 0, "reserved3"); + torture_assert_int_equal(tctx, r->cfheader.versionMinor, 3, "versionMinor"); + torture_assert_int_equal(tctx, r->cfheader.versionMajor, 1, "versionMajor"); + torture_assert_int_equal(tctx, r->cfheader.cFolders, 1, "cFolders"); + torture_assert_int_equal(tctx, r->cfheader.cFiles, 1, "cFiles"); + torture_assert_int_equal(tctx, r->cfheader.flags, 0, "flags"); + torture_assert_int_equal(tctx, r->cfheader.setID, 0, "setID"); + torture_assert_int_equal(tctx, r->cfheader.iCabinet, 0, "iCabinet"); + + torture_assert_int_equal(tctx, r->cffolders[0].coffCabStart, 0x49, "coffCabStart"); + torture_assert_int_equal(tctx, r->cffolders[0].cCFData, 1, "cCFData"); + torture_assert_int_equal(tctx, r->cffolders[0].typeCompress, CF_COMPRESS_NONE, "typeCompress"); + + torture_assert_int_equal(tctx, r->cffiles[0].cbFile, 0x8000, "cbFile"); + torture_assert_int_equal(tctx, r->cffiles[0].uoffFolderStart, 0, "uoffFolderStart"); + torture_assert_int_equal(tctx, r->cffiles[0].iFolder, 0, "iFolder"); + torture_assert_int_equal(tctx, r->cffiles[0].date.date, 0x4936, "date"); + torture_assert_int_equal(tctx, r->cffiles[0].time.time, 0x2109, "time"); + torture_assert_int_equal(tctx, r->cffiles[0].attribs, 0x0020, "attribs"); + torture_assert_str_equal(tctx, r->cffiles[0].szName, "blob-A-32768", "szName"); + + torture_assert_int_equal(tctx, r->cfdata[0].csum, 0x80008000, "csum"); + torture_assert_int_equal(tctx, r->cfdata[0].cbData, 0x8000, "cbData"); + torture_assert_int_equal(tctx, r->cfdata[0].cbUncomp, 0x8000, "cbUncomp"); + + blob = data_blob(NULL, r->cfdata[0].cbUncomp); + memset(blob.data, 'A', blob.length); + + torture_assert_data_blob_equal(tctx, r->cfdata[0].ab, blob, "ab"); + + data_blob_free(&blob); + + return true; +} + +static const uint8_t cab_file_MSZIP_data[] = { + 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x49, + 0x09, 0x21, 0x20, 0x00, 0x62, 0x6c, 0x6f, 0x62, + 0x2d, 0x41, 0x2d, 0x33, 0x32, 0x37, 0x36, 0x38, + 0x00, 0x16, 0x6e, 0xdb, 0x5e, 0x31, 0x00, 0x00, + 0x80, 0x43, 0x4b, 0xed, 0xc1, 0x81, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x20, 0xb6, 0xfd, 0xa5, 0x16, + 0xa9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1a +}; + +static bool cab_file_MSZIP_check(struct torture_context *tctx, + struct cab_file *r) +{ + DATA_BLOB blob; + + torture_assert_str_equal(tctx, r->cfheader.signature, "MSCF", "signature"); + torture_assert_int_equal(tctx, r->cfheader.reserved1, 0, "reserved1"); + torture_assert_int_equal(tctx, r->cfheader.cbCabinet, 130, "cbCabinet"); + torture_assert_int_equal(tctx, r->cfheader.reserved2, 0, "reserved2"); + torture_assert_int_equal(tctx, r->cfheader.coffFiles, 44, "coffFiles"); + torture_assert_int_equal(tctx, r->cfheader.reserved3, 0, "reserved3"); + torture_assert_int_equal(tctx, r->cfheader.versionMinor, 3, "versionMinor"); + torture_assert_int_equal(tctx, r->cfheader.versionMajor, 1, "versionMajor"); + torture_assert_int_equal(tctx, r->cfheader.cFolders, 1, "cFolders"); + torture_assert_int_equal(tctx, r->cfheader.cFiles, 1, "cFiles"); + torture_assert_int_equal(tctx, r->cfheader.flags, 0, "flags"); + torture_assert_int_equal(tctx, r->cfheader.setID, 0, "setID"); + torture_assert_int_equal(tctx, r->cfheader.iCabinet, 0, "iCabinet"); + + torture_assert_int_equal(tctx, r->cffolders[0].coffCabStart, 0x49, "coffCabStart"); + torture_assert_int_equal(tctx, r->cffolders[0].cCFData, 1, "cCFData"); + torture_assert_int_equal(tctx, r->cffolders[0].typeCompress, CF_COMPRESS_MSZIP, "typeCompress"); + + torture_assert_int_equal(tctx, r->cffiles[0].cbFile, 0x8000, "cbFile"); + torture_assert_int_equal(tctx, r->cffiles[0].uoffFolderStart, 0, "uoffFolderStart"); + torture_assert_int_equal(tctx, r->cffiles[0].iFolder, 0, "iFolder"); + torture_assert_int_equal(tctx, r->cffiles[0].date.date, 0x4936, "date"); + torture_assert_int_equal(tctx, r->cffiles[0].time.time, 0x2109, "time"); + torture_assert_int_equal(tctx, r->cffiles[0].attribs, 0x0020, "attribs"); + torture_assert_str_equal(tctx, r->cffiles[0].szName, "blob-A-32768", "szName"); + + torture_assert_int_equal(tctx, r->cfdata[0].csum, 0x5EDB6E16, "csum"); + torture_assert_int_equal(tctx, r->cfdata[0].cbData, 0x31, "cbData"); + torture_assert_int_equal(tctx, r->cfdata[0].cbUncomp, 0x8000, "cbUncomp"); + + blob = data_blob(NULL, r->cfdata[0].cbUncomp); + memset(blob.data, 'A', blob.length); + + torture_assert_data_blob_equal(tctx, r->cfdata[0].ab, blob, "ab"); + + data_blob_free(&blob); + + return true; +} + +static const uint8_t cab_file_LZX_data[] = { + 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x12, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x49, + 0x09, 0x21, 0x20, 0x00, 0x62, 0x6c, 0x6f, 0x62, + 0x2d, 0x41, 0x2d, 0x33, 0x32, 0x37, 0x36, 0x38, + 0x00, 0xa2, 0x60, 0x8d, 0x04, 0x58, 0x00, 0x00, + 0x80, 0x5b, 0x80, 0x80, 0x8d, 0x08, 0x10, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x07, 0x31, 0xd9, 0xfc, 0xdf, 0xf7, 0x20, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, + 0x00, 0xe5, 0x10, 0xdf, 0x67, 0xf7, 0x7d, 0x88, + 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x1f, 0xc4, 0xdd, 0x7f, 0x7c, 0x9f, 0x3f, + 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff +}; + +static bool cab_file_LZX_check(struct torture_context *tctx, + struct cab_file *r) +{ + DATA_BLOB blob; + + torture_assert_str_equal(tctx, r->cfheader.signature, "MSCF", "signature"); + torture_assert_int_equal(tctx, r->cfheader.reserved1, 0, "reserved1"); + torture_assert_int_equal(tctx, r->cfheader.cbCabinet, 0xA9, "cbCabinet"); + torture_assert_int_equal(tctx, r->cfheader.reserved2, 0, "reserved2"); + torture_assert_int_equal(tctx, r->cfheader.coffFiles, 44, "coffFiles"); + torture_assert_int_equal(tctx, r->cfheader.reserved3, 0, "reserved3"); + torture_assert_int_equal(tctx, r->cfheader.versionMinor, 3, "versionMinor"); + torture_assert_int_equal(tctx, r->cfheader.versionMajor, 1, "versionMajor"); + torture_assert_int_equal(tctx, r->cfheader.cFolders, 1, "cFolders"); + torture_assert_int_equal(tctx, r->cfheader.cFiles, 1, "cFiles"); + torture_assert_int_equal(tctx, r->cfheader.flags, 0, "flags"); + torture_assert_int_equal(tctx, r->cfheader.setID, 0, "setID"); + torture_assert_int_equal(tctx, r->cfheader.iCabinet, 0, "iCabinet"); + + torture_assert_int_equal(tctx, r->cffolders[0].coffCabStart, 0x49, "coffCabStart"); + torture_assert_int_equal(tctx, r->cffolders[0].cCFData, 1, "cCFData"); + torture_assert_int_equal(tctx, r->cffolders[0].typeCompress, 4611, "typeCompress"); + + torture_assert_int_equal(tctx, r->cffiles[0].cbFile, 0x8000, "cbFile"); + torture_assert_int_equal(tctx, r->cffiles[0].uoffFolderStart, 0, "uoffFolderStart"); + torture_assert_int_equal(tctx, r->cffiles[0].iFolder, 0, "iFolder"); + torture_assert_int_equal(tctx, r->cffiles[0].date.date, 0x4936, "date"); + torture_assert_int_equal(tctx, r->cffiles[0].time.time, 0x2109, "time"); + torture_assert_int_equal(tctx, r->cffiles[0].attribs, 0x0020, "attribs"); + torture_assert_str_equal(tctx, r->cffiles[0].szName, "blob-A-32768", "szName"); + + torture_assert_int_equal(tctx, r->cfdata[0].csum, 0x48D60A2, "csum"); + torture_assert_int_equal(tctx, r->cfdata[0].cbData, 0x58, "cbData"); + torture_assert_int_equal(tctx, r->cfdata[0].cbUncomp, 0x8000, "cbUncomp"); + + blob = data_blob(NULL, r->cfdata[0].cbUncomp); + memset(blob.data, 'A', blob.length); +#if 0 + /* once we have LZX compression support we can enable this test */ + torture_assert_data_blob_equal(tctx, r->cfdata[0].ab, blob, "ab"); +#endif + data_blob_free(&blob); + + return true; +} + +struct torture_suite *ndr_cabinet_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "cabinet"); + + torture_suite_add_ndr_pull_test(suite, cab_file, cab_file_plain_data, cab_file_plain_check); + torture_suite_add_ndr_pull_test(suite, cab_file, cab_file_MSZIP_data, cab_file_MSZIP_check); + torture_suite_add_ndr_pull_test(suite, cab_file, cab_file_LZX_data, cab_file_LZX_check); + + torture_suite_add_ndr_pull_validate_test(suite, cab_file, cab_file_plain_data, cab_file_plain_check); + + /* + * we cannot validate, as libz' compression routines currently create a + * slightly different result + */ + + /* torture_suite_add_ndr_pull_validate_test(suite, cab_file, cab_file_MSZIP_data, cab_file_MSZIP_check); */ + + return suite; +} diff --git a/source4/torture/ndr/charset.c b/source4/torture/ndr/charset.c new file mode 100644 index 0000000..7062ce1 --- /dev/null +++ b/source4/torture/ndr/charset.c @@ -0,0 +1,91 @@ +/* + Unix SMB/CIFS implementation. + test suite for charset ndr operations + + Copyright (C) Guenther Deschner 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "torture/ndr/proto.h" + +static bool test_ndr_push_charset(struct torture_context *tctx) +{ + const char *strs[] = { + NULL, + "", + "test" + }; + int i; + + struct ndr_push *ndr; + + ndr = talloc_zero(tctx, struct ndr_push); + + for (i = 0; i < ARRAY_SIZE(strs); i++) { + + enum ndr_err_code expected_ndr_err = NDR_ERR_SUCCESS; + + if (strs[i] == NULL) { + expected_ndr_err = NDR_ERR_INVALID_POINTER; + } + + torture_assert_ndr_err_equal(tctx, + ndr_push_charset(ndr, NDR_SCALARS, strs[i], 256, 2, CH_UTF16LE), + expected_ndr_err, + "failed to push charset"); + } + + return true; +} + +static bool test_ndr_push_charset_to_null(struct torture_context *tctx) +{ + const char *strs[] = { + NULL, + "", + "test" + }; + int i; + + struct ndr_push *ndr; + + ndr = talloc_zero(tctx, struct ndr_push); + + + for (i = 0; i < ARRAY_SIZE(strs); i++) { + + torture_assert_ndr_success(tctx, + ndr_push_charset_to_null(ndr, NDR_SCALARS, strs[i], 256, 2, CH_UTF16LE), + "failed to push charset to null"); + } + + return true; +} + + +struct torture_suite *ndr_charset_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "charset"); + + suite->description = talloc_strdup(suite, "NDR - charset focused push/pull tests"); + + torture_suite_add_simple_test(suite, "push", test_ndr_push_charset); + torture_suite_add_simple_test(suite, "push_to_null", test_ndr_push_charset_to_null); + + return suite; +} + diff --git a/source4/torture/ndr/clusapi.c b/source4/torture/ndr/clusapi.c new file mode 100644 index 0000000..db6a27c --- /dev/null +++ b/source4/torture/ndr/clusapi.c @@ -0,0 +1,390 @@ +/* + Unix SMB/CIFS implementation. + test suite for clusapi ndr operations + + Copyright (C) Guenther Deschner 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_clusapi.h" +#include "torture/ndr/proto.h" +#include "param/param.h" +#include "libcli/registry/util_reg.h" + +static const uint8_t clusapi_PROPERTY_LIST_data[] = { + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x69, 0x00, 0x78, 0x00, 0x51, 0x00, 0x75, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x76, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x51, 0x00, + 0x75, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x53, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x53, 0x00, + 0x74, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x56, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x75, 0x00, + 0x6d, 0x00, 0x65, 0x00, 0x73, 0x00, 0x52, 0x00, 0x6f, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x43, 0x00, 0x6c, 0x00, 0x75, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x53, 0x00, 0x74, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x73, 0x00, 0x44, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x69, 0x00, 0x63, 0x00, 0x57, 0x00, 0x65, 0x00, 0x69, 0x00, 0x67, 0x00, + 0x68, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x41, 0x00, 0x64, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x41, 0x00, 0x63, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x50, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool clusapi_PROPERTY_LIST_check(struct torture_context *tctx, + struct clusapi_PROPERTY_LIST *r) +{ + DATA_BLOB blob_dword_null = data_blob_talloc_zero(tctx, 4); + DATA_BLOB blob_dword_one = data_blob(NULL, 4); + const char *str; + + SIVAL(blob_dword_one.data, 0, 1); + + torture_assert_int_equal(tctx, r->propertyCount, 6, "propertyCount"); + + torture_assert_int_equal(tctx, r->propertyValues[0].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[0].size, 20, "size"); + torture_assert_str_equal(tctx, r->propertyValues[0].buffer, "FixQuorum", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[0].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[0].PropertyValues.Buffer, blob_dword_null, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[0].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[1].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[1].size, 28, "size"); + torture_assert_str_equal(tctx, r->propertyValues[1].buffer, "PreventQuorum", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[1].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[1].PropertyValues.Buffer, blob_dword_null, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[1].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[2].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[2].size, 62, "size"); + torture_assert_str_equal(tctx, r->propertyValues[2].buffer, "IgnorePersistentStateOnStartup", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[2].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[2].PropertyValues.Buffer, blob_dword_null, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[2].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[3].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[3].size, 36, "size"); + torture_assert_str_equal(tctx, r->propertyValues[3].buffer, "SharedVolumesRoot", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[3].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_SZ, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Size, 36, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Buffer.length, 36, "PropertyValues.Buffer.length"); + pull_reg_sz(tctx, &r->propertyValues[3].PropertyValues.Buffer, &str); + torture_assert_str_equal(tctx, str, "C:\\ClusterStorage", "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[3].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[4].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[4].size, 42, "size"); + torture_assert_str_equal(tctx, r->propertyValues[4].buffer, "WitnessDynamicWeight", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[4].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[4].PropertyValues.Buffer, blob_dword_one, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[4].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[5].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[5].size, 34, "size"); + torture_assert_str_equal(tctx, r->propertyValues[5].buffer, "AdminAccessPoint", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[5].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[5].PropertyValues.Buffer, blob_dword_one, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[5].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + data_blob_free(&blob_dword_null); + data_blob_free(&blob_dword_one); + + return true; +} + +static const uint8_t clusapi_PROPERTY_LIST_data2[] = { + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x4e, 0x00, 0x61, 0x00, + 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, + 0x48, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x80, 0x25, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x6f, 0x00, + 0x64, 0x00, 0x65, 0x00, 0x4c, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x80, 0x25, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x6a, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x4d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x56, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x42, 0x00, 0x75, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x64, 0x00, 0x4e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x53, 0x00, 0x44, 0x00, 0x56, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, + 0x49, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, + 0x63, 0x00, 0x65, 0x00, 0x49, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x44, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x75, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x6f, 0x00, + 0x64, 0x00, 0x65, 0x00, 0x44, 0x00, 0x72, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x54, 0x00, 0x61, 0x00, 0x72, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x61, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x57, 0x00, 0x65, 0x00, 0x69, 0x00, + 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x65, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x73, 0x00, 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x51, 0x00, 0x75, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool clusapi_PROPERTY_LIST_check2(struct torture_context *tctx, + struct clusapi_PROPERTY_LIST *r) +{ + DATA_BLOB blob_dword_null = data_blob_talloc_zero(tctx, 4); + DATA_BLOB blob_dword_one = data_blob(NULL, 4); + DATA_BLOB blob_dword = data_blob(NULL, 4); + const char *str; + + SIVAL(blob_dword_one.data, 0, 1); + + torture_assert_int_equal(tctx, r->propertyCount, 12, "propertyCount"); + + torture_assert_int_equal(tctx, r->propertyValues[0].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[0].size, 18, "size"); + torture_assert_str_equal(tctx, r->propertyValues[0].buffer, "NodeName", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[0].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_SZ, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Size, 12, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Buffer.length, 12, "PropertyValues.Buffer.length"); + pull_reg_sz(tctx, &r->propertyValues[0].PropertyValues.Buffer, &str); + torture_assert_str_equal(tctx, str, "node1", "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[0].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[0].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[1].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[1].size, 38, "size"); + torture_assert_str_equal(tctx, r->propertyValues[1].buffer, "NodeHighestVersion", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[1].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0x00082580); + torture_assert_data_blob_equal(tctx, r->propertyValues[1].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[1].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[1].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[2].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[2].size, 36, "size"); + torture_assert_str_equal(tctx, r->propertyValues[2].buffer, "NodeLowestVersion", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[2].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0x00082580); + torture_assert_data_blob_equal(tctx, r->propertyValues[2].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[2].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[2].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[3].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[3].size, 26, "size"); + torture_assert_str_equal(tctx, r->propertyValues[3].buffer, "MajorVersion", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[3].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0x06); + torture_assert_data_blob_equal(tctx, r->propertyValues[3].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[3].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[3].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[4].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[4].size, 26, "size"); + torture_assert_str_equal(tctx, r->propertyValues[4].buffer, "MinorVersion", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[4].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0x03); + torture_assert_data_blob_equal(tctx, r->propertyValues[4].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[4].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[4].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[5].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[5].size, 24, "size"); + torture_assert_str_equal(tctx, r->propertyValues[5].buffer, "BuildNumber", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[5].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0x00002580); + torture_assert_data_blob_equal(tctx, r->propertyValues[5].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[5].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[5].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[6].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[6].size, 22, "size"); + torture_assert_str_equal(tctx, r->propertyValues[6].buffer, "CSDVersion", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[6].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[6].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_SZ, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[6].PropertyValues.Size, 2, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[6].PropertyValues.Buffer.length, 2, "PropertyValues.Buffer.length"); + pull_reg_sz(tctx, &r->propertyValues[6].PropertyValues.Buffer, &str); + torture_assert_str_equal(tctx, str, "", "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[6].PropertyValues.Padding.length, 2, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[6].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[7].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[7].size, 30, "size"); + torture_assert_str_equal(tctx, r->propertyValues[7].buffer, "NodeInstanceID", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[7].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[7].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_SZ, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[7].PropertyValues.Size, 74, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[7].PropertyValues.Buffer.length, 74, "PropertyValues.Buffer.length"); + pull_reg_sz(tctx, &r->propertyValues[7].PropertyValues.Buffer, &str); + torture_assert_str_equal(tctx, str, "00000000-0000-0000-0000-000000000002", "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[7].PropertyValues.Padding.length, 2, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[7].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[8].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[8].size, 32, "size"); + torture_assert_str_equal(tctx, r->propertyValues[8].buffer, "NodeDrainStatus", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[8].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[8].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[8].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[8].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[8].PropertyValues.Buffer, blob_dword_null, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[8].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[8].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[9].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[9].size, 32, "size"); + torture_assert_str_equal(tctx, r->propertyValues[9].buffer, "NodeDrainTarget", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[9].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[9].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[9].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[9].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + SIVAL(blob_dword.data, 0, 0xffffffff); + torture_assert_data_blob_equal(tctx, r->propertyValues[9].PropertyValues.Buffer, blob_dword, "PropertyValues.Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[9].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[9].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[10].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[10].size, 28, "size"); + torture_assert_str_equal(tctx, r->propertyValues[10].buffer, "DynamicWeight", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[10].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[10].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[10].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[10].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[10].PropertyValues.Buffer, blob_dword_one, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[10].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[10].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + torture_assert_int_equal(tctx, r->propertyValues[11].syntax_name, CLUSPROP_SYNTAX_NAME, "syntax_name"); + torture_assert_int_equal(tctx, r->propertyValues[11].size, 38, "size"); + torture_assert_str_equal(tctx, r->propertyValues[11].buffer, "NeedsPreventQuorum", "buffer"); + torture_assert_int_equal(tctx, r->propertyValues[11].padding.length, 0, "padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[11].PropertyValues.Syntax, CLUSPROP_SYNTAX_LIST_VALUE_DWORD, "PropertyValues.Syntax"); + torture_assert_int_equal(tctx, r->propertyValues[11].PropertyValues.Size, 4, "PropertyValues.Size"); + torture_assert_int_equal(tctx, r->propertyValues[11].PropertyValues.Buffer.length, 4, "PropertyValues.Buffer.length"); + torture_assert_data_blob_equal(tctx, r->propertyValues[11].PropertyValues.Buffer, blob_dword_null, "Buffer"); + torture_assert_int_equal(tctx, r->propertyValues[11].PropertyValues.Padding.length, 0, "PropertyValues.Padding.length"); + torture_assert_int_equal(tctx, r->propertyValues[11].end_mark, CLUSPROP_SYNTAX_ENDMARK, "end_mark"); + + data_blob_free(&blob_dword_null); + data_blob_free(&blob_dword_one); + data_blob_free(&blob_dword); + + return true; +} + +struct torture_suite *ndr_clusapi_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "clusapi"); + + torture_suite_add_ndr_pull_validate_test(suite, + clusapi_PROPERTY_LIST, + clusapi_PROPERTY_LIST_data, + clusapi_PROPERTY_LIST_check); + + torture_suite_add_ndr_pull_validate_test(suite, + clusapi_PROPERTY_LIST, + clusapi_PROPERTY_LIST_data2, + clusapi_PROPERTY_LIST_check2); + + return suite; +} diff --git a/source4/torture/ndr/dcerpc.c b/source4/torture/ndr/dcerpc.c new file mode 100644 index 0000000..459817d --- /dev/null +++ b/source4/torture/ndr/dcerpc.c @@ -0,0 +1,148 @@ +/* + Unix SMB/CIFS implementation. + test suite for dcerpc ndr operations + + Copyright (C) Stefan Metzmacher 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_dcerpc.h" +#include "torture/ndr/proto.h" + +/* + * ncacn_packet: struct ncacn_packet + * rpc_vers : 0x05 (5) + * rpc_vers_minor : 0x00 (0) + * ptype : DCERPC_PKT_CO_CANCEL (18) + * pfc_flags : 0x06 (6) + * 0: DCERPC_PFC_FLAG_FIRST + * 1: DCERPC_PFC_FLAG_LAST + * 1: DCERPC_PFC_FLAG_PENDING_CANCEL_OR_HDR_SIGNING + * 0: DCERPC_PFC_FLAG_CONC_MPX + * 0: DCERPC_PFC_FLAG_DID_NOT_EXECUTE + * 0: DCERPC_PFC_FLAG_MAYBE + * 0: DCERPC_PFC_FLAG_OBJECT_UUID + * drep: ARRAY(4) + * [0] : 0x10 (16) + * [1] : 0x00 (0) + * [2] : 0x00 (0) + * [3] : 0x00 (0) + * frag_length : 0x0010 (16) + * auth_length : 0x0000 (0) + * call_id : 0x00000001 (1) + * u : union dcerpc_payload(case 18) + * co_cancel: struct dcerpc_co_cancel + * auth_info : DATA_BLOB length=0 + */ +static const uint8_t ncacn_packet_co_cancel_data[] = { + 0x05, 0x00, 0x12, 0x06, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +}; + +static bool ncacn_packet_co_cancel_check(struct torture_context *tctx, + struct ncacn_packet *pkt) +{ + torture_assert_int_equal(tctx, pkt->rpc_vers, 5, "rpc_vers"); + torture_assert_int_equal(tctx, pkt->rpc_vers_minor, 0, "rpc_vers_minor"); + torture_assert_int_equal(tctx, pkt->ptype, DCERPC_PKT_CO_CANCEL, "ptype"); + torture_assert_int_equal(tctx, pkt->pfc_flags, + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_PENDING_CANCEL_OR_HDR_SIGNING, + "pfc_flags"); + torture_assert_int_equal(tctx, pkt->drep[0], DCERPC_DREP_LE, "drep[0]"); + torture_assert_int_equal(tctx, pkt->drep[1], 0, "drep[1]"); + torture_assert_int_equal(tctx, pkt->drep[2], 0, "drep[2]"); + torture_assert_int_equal(tctx, pkt->drep[3], 0, "drep[3]"); + torture_assert_int_equal(tctx, pkt->frag_length, 16, "frag_length"); + torture_assert_int_equal(tctx, pkt->auth_length, 0, "auth_length"); + torture_assert_int_equal(tctx, pkt->call_id, 1, "call_id"); + torture_assert_int_equal(tctx, pkt->u.co_cancel.auth_info.length, 0, + "co_cancel.auth_info.length"); + return true; +} + +/* + * ncacn_packet: struct ncacn_packet + * rpc_vers : 0x05 (5) + * rpc_vers_minor : 0x00 (0) + * ptype : DCERPC_PKT_ORPHANED (19) + * pfc_flags : 0x03 (3) + * 1: DCERPC_PFC_FLAG_FIRST + * 1: DCERPC_PFC_FLAG_LAST + * 0: DCERPC_PFC_FLAG_PENDING_CANCEL_OR_HDR_SIGNING + * 0: DCERPC_PFC_FLAG_CONC_MPX + * 0: DCERPC_PFC_FLAG_DID_NOT_EXECUTE + * 0: DCERPC_PFC_FLAG_MAYBE + * 0: DCERPC_PFC_FLAG_OBJECT_UUID + * drep: ARRAY(4) + * [0] : 0x10 (16) + * [1] : 0x00 (0) + * [2] : 0x00 (0) + * [3] : 0x00 (0) + * frag_length : 0x0010 (16) + * auth_length : 0x0000 (0) + * call_id : 0x00000008 (8) + * u : union dcerpc_payload(case 19) + * orphaned: struct dcerpc_orphaned + * auth_info : DATA_BLOB length=0 + */ +static const uint8_t ncacn_packet_orphaned_data[] = { + 0x05, 0x00, 0x13, 0x03, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, +}; + +static bool ncacn_packet_orphaned_check(struct torture_context *tctx, + struct ncacn_packet *pkt) +{ + torture_assert_int_equal(tctx, pkt->rpc_vers, 5, "rpc_vers"); + torture_assert_int_equal(tctx, pkt->rpc_vers_minor, 0, "rpc_vers_minor"); + torture_assert_int_equal(tctx, pkt->ptype, DCERPC_PKT_ORPHANED, "ptype"); + torture_assert_int_equal(tctx, pkt->pfc_flags, + DCERPC_PFC_FLAG_FIRST|DCERPC_PFC_FLAG_LAST, + "pfc_flags"); + torture_assert_int_equal(tctx, pkt->drep[0], DCERPC_DREP_LE, "drep[0]"); + torture_assert_int_equal(tctx, pkt->drep[1], 0, "drep[1]"); + torture_assert_int_equal(tctx, pkt->drep[2], 0, "drep[2]"); + torture_assert_int_equal(tctx, pkt->drep[3], 0, "drep[3]"); + torture_assert_int_equal(tctx, pkt->frag_length, 16, "frag_length"); + torture_assert_int_equal(tctx, pkt->auth_length, 0, "auth_length"); + torture_assert_int_equal(tctx, pkt->call_id, 8, "call_id"); + torture_assert_int_equal(tctx, pkt->u.orphaned.auth_info.length, 0, + "orphaned.auth_info.length"); + return true; +} + +struct torture_suite *ndr_dcerpc_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dcerpc"); + struct torture_suite *co_cancel = torture_suite_create(ctx, "co_cancel"); + struct torture_suite *orphaned = torture_suite_create(ctx, "orphaned"); + + torture_suite_add_suite(suite, co_cancel); + torture_suite_add_ndr_pull_validate_test(co_cancel, + ncacn_packet, + ncacn_packet_co_cancel_data, + ncacn_packet_co_cancel_check); + + torture_suite_add_suite(suite, orphaned); + torture_suite_add_ndr_pull_validate_test(orphaned, + ncacn_packet, + ncacn_packet_orphaned_data, + ncacn_packet_orphaned_check); + + return suite; +} diff --git a/source4/torture/ndr/dfs.c b/source4/torture/ndr/dfs.c new file mode 100644 index 0000000..ac80b40 --- /dev/null +++ b/source4/torture/ndr/dfs.c @@ -0,0 +1,115 @@ +/* + Unix SMB/CIFS implementation. + test suite for dfs ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_dfs.h" +#include "torture/ndr/proto.h" + +static const uint8_t getmanagerversion_out_data[] = { + 0x04, 0x00, 0x00, 0x00 +}; + +static bool getmanagerversion_out_check(struct torture_context *tctx, + struct dfs_GetManagerVersion *r) +{ + torture_assert_int_equal(tctx, *r->out.version, 4, "version"); + return true; +} + +static const uint8_t enumex_in_data300[] = { + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x38, 0xf5, 0x07, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, + 0x40, 0xf5, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa8, 0xf5, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool enumex_in_check300(struct torture_context *tctx, + struct dfs_EnumEx *r) +{ + torture_assert_str_equal(tctx, r->in.dfs_name, "w2k3dc", "dfs name"); + torture_assert_int_equal(tctx, r->in.level, 300, "level"); + torture_assert(tctx, r->in.total != NULL, "total ptr"); + torture_assert_int_equal(tctx, *r->in.total, 0, "total"); + torture_assert_int_equal(tctx, r->in.bufsize, -1, "buf size"); + torture_assert(tctx, r->in.info != NULL, "info ptr"); + torture_assert_int_equal(tctx, r->in.info->level, 300, "info level"); + torture_assert(tctx, r->in.info->e.info300->s == NULL, "info data ptr"); + return true; +} + + +static const uint8_t enumex_out_data300[] = { + 0x00, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x33, 0x00, 0x44, 0x00, 0x43, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x33, 0x00, 0x44, 0x00, 0x43, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x33, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x5c, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool enumex_out_check300(struct torture_context *tctx, + struct dfs_EnumEx *r) +{ + torture_assert_werr_ok(tctx, r->out.result, "return code"); + torture_assert(tctx, r->out.total != NULL, "total ptr"); + torture_assert_int_equal(tctx, *r->out.total, 3, "total"); + torture_assert(tctx, r->out.info != NULL, "info ptr"); + torture_assert_int_equal(tctx, r->out.info->level, 300, "info level"); + torture_assert(tctx, r->out.info->e.info300 != NULL, "info data ptr"); + torture_assert_int_equal(tctx, r->out.info->e.info300->count, 3, "info enum array"); + torture_assert_str_equal(tctx, r->out.info->e.info300->s[0].dom_root, "\\W2K3DC\\standaloneroot", "info enum array 0"); + torture_assert_int_equal(tctx, r->out.info->e.info300->s[0].flavor, 256, "info enum flavor 0"); + torture_assert_str_equal(tctx, r->out.info->e.info300->s[1].dom_root, "\\W2K3DC\\standaloneroot2", "info enum array 1"); + torture_assert_int_equal(tctx, r->out.info->e.info300->s[1].flavor, 256, "info enum flavor 1"); + torture_assert_str_equal(tctx, r->out.info->e.info300->s[2].dom_root, "\\W2K3DOM\\testdomainroot", "info enum array 2"); + torture_assert_int_equal(tctx, r->out.info->e.info300->s[2].flavor, 512, "info enum flavor 2"); + return true; +} + +struct torture_suite *ndr_dfs_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dfs"); + + torture_suite_add_ndr_pull_fn_test(suite, dfs_GetManagerVersion, getmanagerversion_out_data, NDR_OUT, getmanagerversion_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, dfs_EnumEx, enumex_in_data300, NDR_IN, enumex_in_check300 ); + torture_suite_add_ndr_pull_fn_test(suite, dfs_EnumEx, enumex_out_data300, NDR_OUT, enumex_out_check300 ); + + return suite; +} + diff --git a/source4/torture/ndr/dfsblob.c b/source4/torture/ndr/dfsblob.c new file mode 100644 index 0000000..81db322 --- /dev/null +++ b/source4/torture/ndr/dfsblob.c @@ -0,0 +1,85 @@ +/* + Unix SMB/CIFS implementation. + + Test DFS blobs. + + Copyright (C) Matthieu Patou 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_dfsblobs.h" +#include "torture/ndr/proto.h" +#include "librpc/gen_ndr/dfsblobs.h" + +DATA_BLOB blob; +static const uint8_t dfs_get_ref_in[] = { + 0x03, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x33, 0x00, 0x00, 0x00 }; + +static const uint8_t dfs_get_ref_out[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x58, 0x02, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5c, 0x00, 0x6d, 0x00, 0x73, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, + 0x2e, 0x00, 0x74, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x5c, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x61, 0x00, 0x64, 0x00, + 0x76, 0x00, 0x7a, 0x00, 0x30, 0x00, 0x31, 0x00, + 0x2e, 0x00, 0x6d, 0x00, 0x73, 0x00, 0x77, 0x00, + 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x2e, 0x00, + 0x74, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00}; + +static const uint8_t dfs_get_ref_out2[] = { + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x58, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x58, 0x02, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x57, 0x00, + 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x77, 0x00, + 0x32, 0x00, 0x6b, 0x00, 0x38, 0x00, 0x72, 0x00, + 0x32, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x77, 0x00, 0x73, 0x00, 0x2e, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x00, 0x00 +}; +static bool dfs_referral_out_check(struct torture_context *tctx, struct dfs_referral_resp *r) +{ + torture_assert_str_equal(tctx, + r->referral_entries[0].referral.v3.referrals.r2.special_name, + "\\msw2k3.tst", "Special name"); + ndr_push_struct_blob(&blob, tctx, r, (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp); + torture_assert_int_equal(tctx, blob.data[blob.length-2], 0, "expanded names not null terminated"); + torture_assert_int_equal(tctx, blob.data[blob.length-1], 0, "expanded names not null terminated"); + return true; +} + +struct torture_suite *ndr_dfsblob_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dfsblob"); + + torture_suite_add_ndr_pull_test(suite, dfs_GetDFSReferral_in, dfs_get_ref_in, NULL); + + torture_suite_add_ndr_pull_test(suite, dfs_referral_resp, dfs_get_ref_out2, NULL); + + torture_suite_add_ndr_pull_test(suite, dfs_referral_resp, dfs_get_ref_out,dfs_referral_out_check); + + return suite; +} diff --git a/source4/torture/ndr/dnsp.c b/source4/torture/ndr/dnsp.c new file mode 100644 index 0000000..3fc58c9 --- /dev/null +++ b/source4/torture/ndr/dnsp.c @@ -0,0 +1,389 @@ +/* + Unix SMB/CIFS implementation. + test suite for dnsp ndr operations + + Copyright (C) Stefan Metzmacher 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_dnsp.h" +#include "torture/ndr/proto.h" +#include "lib/util/base64.h" + +/* + * base64_decode_data_blob_talloc() => dump_data() gives: + * + * [0000] 0C 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ........ ........ + * [0010] 81 00 00 00 02 00 00 00 AC 1F 63 21 AC 1F 63 2C ........ ..c!..c, + * [0020] 00 00 00 00 + */ +static const char *dnsp_dnsProperty_ip4_array_b64 = + "DAAAAAAAAAAAAAAAAQAAAIEAAAACAAAArB9jIawfYywAAAAA"; + +static bool dnsp_dnsProperty_ip4_array_check(struct torture_context *tctx, + struct dnsp_DnsProperty *r) +{ + /* + * NDR_PRINT_DEBUG(dnsp_DnsProperty, r); gave: + * + * r: struct dnsp_DnsProperty + * wDataLength : 0x0000000c (12) + * namelength : 0x00000000 (0) + * flag : 0x00000000 (0) + * version : 0x00000001 (1) + * id : DSPROPERTY_ZONE_MASTER_SERVERS (129) + * data : union dnsPropertyData(case 129) + * master_servers: struct dnsp_ip4_array + * addrCount : 0x00000002 (2) + * addrArray: ARRAY(2) + * addrArray : 0x21631fac (560144300) + * addrArray : 0x2c631fac (744693676) + * name : 0x00000000 (0) + * + */ + + torture_assert_int_equal(tctx, r->wDataLength, 12, "wDataLength"); + torture_assert_int_equal(tctx, r->namelength, 0, "namelength"); + torture_assert_int_equal(tctx, r->flag, 0, "flag"); + torture_assert_int_equal(tctx, r->version, 1, "version"); + torture_assert_int_equal(tctx, r->id, DSPROPERTY_ZONE_MASTER_SERVERS, "id"); + torture_assert_int_equal(tctx, r->data.master_servers.addrCount, 2, "addrCount"); + /* + * This should be an array of [flag(NDR_BIG_ENDIAN)] ipv4address + * instead of uint32! + * 0x21631fac is 172.31.99.33 + * 0x2c631fac is 172.31.99.44 + */ + torture_assert_int_equal(tctx, r->data.master_servers.addrArray[0], 0x21631fac, "addrArray[0]"); + torture_assert_int_equal(tctx, r->data.master_servers.addrArray[1], 0x2c631fac, "addrArray[1]"); + torture_assert_int_equal(tctx, r->name, 0, "name"); + + return true; +} + +/* + * base64_decode_data_blob_talloc() => dump_data() gives: + * + * [0000] E0 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ........ ........ + * [0010] 91 00 00 00 03 00 00 00 03 00 00 00 00 00 00 00 ........ ........ + * [0020] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [0030] 00 00 00 00 02 00 00 35 AC 1F 63 21 00 00 00 00 .......5 ..c!.... + * [0040] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [0050] 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [0060] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [0070] 00 00 00 00 02 00 00 35 AC 1F 63 2C 00 00 00 00 .......5 ..c,.... + * [0080] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [0090] 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [00A0] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [00B0] 00 00 00 00 17 00 00 35 00 00 00 00 FD 3A AA A3 .......5 .....:.. + * [00C0] EE 87 FF 09 02 00 00 FF FE 99 FF FF 00 00 00 00 ........ ........ + * [00D0] 00 00 00 00 1C 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [00E0] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + * [00F0] 00 00 00 00 00 00 00 00 ........ + */ +static const char *dnsp_dnsProperty_addr_array_b64 = + "4AAAAAAAAAAAAAAAAQAAAJEAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAIAADWsH2MhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAACAAA1rB9jLAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwAANQAAAAD9Oqqj" + "7of/CQIAAP/+mf//AAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAA=DAAAAAAAAAAAAAAAAQAAAIEAAAACAAAArB9jIawfYywAAAAA"; + +static bool dnsp_dnsProperty_addr_array_check(struct torture_context *tctx, + struct dnsp_DnsProperty *r) +{ + const struct dnsp_dns_addr_array *da = NULL; + const struct dnsp_dns_addr *a0 = NULL; + const struct dnsp_dns_addr *a1 = NULL; + const struct dnsp_dns_addr *a2 = NULL; + + /* + * NDR_PRINT_DEBUG(dnsp_DnsProperty, r); gave: + * + * r: struct dnsp_DnsProperty + * wDataLength : 0x000000e0 (224) + * namelength : 0x00000000 (0) + * flag : 0x00000000 (0) + * version : 0x00000001 (1) + * id : DSPROPERTY_ZONE_MASTER_SERVERS_DA (145) + * data : union dnsPropertyData(case 145) + * z_master_servers: struct dnsp_dns_addr_array + * MaxCount : 0x00000003 (3) + * AddrCount : 0x00000003 (3) + * Tag : 0x00000000 (0) + * Family : 0x0000 (0) + * Reserved0 : 0x0000 (0) + * Flags : 0x00000000 (0) + * MatchFlag : 0x00000000 (0) + * Reserved1 : 0x00000000 (0) + * Reserved2 : 0x00000000 (0) + * AddrArray: ARRAY(3) + * AddrArray: struct dnsp_dns_addr + * family : 0x0002 (2) + * port : 0x0035 (53) + * ipv4 : 172.31.99.33 + * ipv6 : 0000:0000:0000:0000:0000:0000:0000:0000 + * pad: ARRAY(8) + * [0] : 0x00 (0) + * [1] : 0x00 (0) + * [2] : 0x00 (0) + * [3] : 0x00 (0) + * [4] : 0x00 (0) + * [5] : 0x00 (0) + * [6] : 0x00 (0) + * [7] : 0x00 (0) + * unused: ARRAY(8) + * unused : 0x00000010 (16) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * AddrArray: struct dnsp_dns_addr + * family : 0x0002 (2) + * port : 0x0035 (53) + * ipv4 : 172.31.99.44 + * ipv6 : 0000:0000:0000:0000:0000:0000:0000:0000 + * pad: ARRAY(8) + * [0] : 0x00 (0) + * [1] : 0x00 (0) + * [2] : 0x00 (0) + * [3] : 0x00 (0) + * [4] : 0x00 (0) + * [5] : 0x00 (0) + * [6] : 0x00 (0) + * [7] : 0x00 (0) + * unused: ARRAY(8) + * unused : 0x00000010 (16) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * AddrArray: struct dnsp_dns_addr + * family : 0x0017 (23) + * port : 0x0035 (53) + * ipv4 : 0.0.0.0 + * ipv6 : fd3a:aaa3:ee87:ff09:0200:00ff:fe99:ffff + * pad: ARRAY(8) + * [0] : 0x00 (0) + * [1] : 0x00 (0) + * [2] : 0x00 (0) + * [3] : 0x00 (0) + * [4] : 0x00 (0) + * [5] : 0x00 (0) + * [6] : 0x00 (0) + * [7] : 0x00 (0) + * unused: ARRAY(8) + * unused : 0x0000001c (28) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * unused : 0x00000000 (0) + * name : 0x00000000 (0) + * + */ + + torture_assert_int_equal(tctx, r->wDataLength, 224, "wDataLength"); + torture_assert_int_equal(tctx, r->namelength, 0, "namelength"); + torture_assert_int_equal(tctx, r->flag, 0, "flag"); + torture_assert_int_equal(tctx, r->version, 1, "version"); + torture_assert_int_equal(tctx, r->id, DSPROPERTY_ZONE_MASTER_SERVERS_DA, "id"); + da = &r->data.z_master_servers; + torture_assert_int_equal(tctx, da->MaxCount, 3, "MaxCount"); + torture_assert_int_equal(tctx, da->AddrCount, 3, "AddrCount"); + torture_assert_int_equal(tctx, da->Tag, 0, "Tag"); + torture_assert_int_equal(tctx, da->Family, 0, "Family"); + torture_assert_int_equal(tctx, da->Reserved0, 0, "Reserved0"); + torture_assert_int_equal(tctx, da->Flags, 0, "Flags"); + torture_assert_int_equal(tctx, da->MatchFlag, 0, "MatchFlag"); + torture_assert_int_equal(tctx, da->Reserved1, 0, "Reserved1"); + torture_assert_int_equal(tctx, da->Reserved2, 0, "Reserved2"); + a0 = &da->AddrArray[0]; + torture_assert_int_equal(tctx, a0->family, 2, "family v4"); + torture_assert_int_equal(tctx, a0->port, 53, "port"); + torture_assert_str_equal(tctx, a0->ipv4, "172.31.99.33", "ipv4"); + torture_assert_str_equal(tctx, a0->ipv6, + "0000:0000:0000:0000:0000:0000:0000:0000", + "ipv6 (zero)"); + torture_assert_int_equal(tctx, a0->pad[0], 0, "pad[0]"); + torture_assert_int_equal(tctx, a0->pad[1], 0, "pad[1]"); + torture_assert_int_equal(tctx, a0->pad[2], 0, "pad[2]"); + torture_assert_int_equal(tctx, a0->pad[3], 0, "pad[3]"); + torture_assert_int_equal(tctx, a0->pad[4], 0, "pad[4]"); + torture_assert_int_equal(tctx, a0->pad[5], 0, "pad[5]"); + torture_assert_int_equal(tctx, a0->pad[6], 0, "pad[6]"); + torture_assert_int_equal(tctx, a0->pad[7], 0, "pad[7]"); + torture_assert_int_equal(tctx, a0->unused[0], 16, "unused[0]"); + torture_assert_int_equal(tctx, a0->unused[1], 0, "unused[1]"); + torture_assert_int_equal(tctx, a0->unused[2], 0, "unused[2]"); + torture_assert_int_equal(tctx, a0->unused[3], 0, "unused[3]"); + torture_assert_int_equal(tctx, a0->unused[4], 0, "unused[4]"); + torture_assert_int_equal(tctx, a0->unused[5], 0, "unused[5]"); + torture_assert_int_equal(tctx, a0->unused[6], 0, "unused[6]"); + torture_assert_int_equal(tctx, a0->unused[7], 0, "unused[7]"); + a1 = &da->AddrArray[1]; + torture_assert_int_equal(tctx, a1->family, 2, "family v4"); + torture_assert_int_equal(tctx, a1->port, 53, "port"); + torture_assert_str_equal(tctx, a1->ipv4, "172.31.99.44", "ipv4"); + torture_assert_str_equal(tctx, a1->ipv6, + "0000:0000:0000:0000:0000:0000:0000:0000", + "ipv6 (zero)"); + torture_assert_int_equal(tctx, a1->pad[0], 0, "pad[0]"); + torture_assert_int_equal(tctx, a1->pad[1], 0, "pad[1]"); + torture_assert_int_equal(tctx, a1->pad[2], 0, "pad[2]"); + torture_assert_int_equal(tctx, a1->pad[3], 0, "pad[3]"); + torture_assert_int_equal(tctx, a1->pad[4], 0, "pad[4]"); + torture_assert_int_equal(tctx, a1->pad[5], 0, "pad[5]"); + torture_assert_int_equal(tctx, a1->pad[6], 0, "pad[6]"); + torture_assert_int_equal(tctx, a1->pad[7], 0, "pad[7]"); + torture_assert_int_equal(tctx, a1->unused[0], 16, "unused[0]"); + torture_assert_int_equal(tctx, a1->unused[1], 0, "unused[1]"); + torture_assert_int_equal(tctx, a1->unused[2], 0, "unused[2]"); + torture_assert_int_equal(tctx, a1->unused[3], 0, "unused[3]"); + torture_assert_int_equal(tctx, a1->unused[4], 0, "unused[4]"); + torture_assert_int_equal(tctx, a1->unused[5], 0, "unused[5]"); + torture_assert_int_equal(tctx, a1->unused[6], 0, "unused[6]"); + torture_assert_int_equal(tctx, a1->unused[7], 0, "unused[7]"); + a2 = &da->AddrArray[2]; + torture_assert_int_equal(tctx, a2->family, 23, "family v6"); + torture_assert_int_equal(tctx, a2->port, 53, "port"); + torture_assert_str_equal(tctx, a2->ipv4, "0.0.0.0", "ipv4 (zero)"); + torture_assert_str_equal(tctx, a2->ipv6, + "fd3a:aaa3:ee87:ff09:0200:00ff:fe99:ffff", + "ipv6"); + torture_assert_int_equal(tctx, a2->pad[0], 0, "pad[0]"); + torture_assert_int_equal(tctx, a2->pad[1], 0, "pad[1]"); + torture_assert_int_equal(tctx, a2->pad[2], 0, "pad[2]"); + torture_assert_int_equal(tctx, a2->pad[3], 0, "pad[3]"); + torture_assert_int_equal(tctx, a2->pad[4], 0, "pad[4]"); + torture_assert_int_equal(tctx, a2->pad[5], 0, "pad[5]"); + torture_assert_int_equal(tctx, a2->pad[6], 0, "pad[6]"); + torture_assert_int_equal(tctx, a2->pad[7], 0, "pad[7]"); + torture_assert_int_equal(tctx, a2->unused[0], 28, "unused[0]"); + torture_assert_int_equal(tctx, a2->unused[1], 0, "unused[1]"); + torture_assert_int_equal(tctx, a2->unused[2], 0, "unused[2]"); + torture_assert_int_equal(tctx, a2->unused[3], 0, "unused[3]"); + torture_assert_int_equal(tctx, a2->unused[4], 0, "unused[4]"); + torture_assert_int_equal(tctx, a2->unused[5], 0, "unused[5]"); + torture_assert_int_equal(tctx, a2->unused[6], 0, "unused[6]"); + torture_assert_int_equal(tctx, a2->unused[7], 0, "unused[7]"); + torture_assert_int_equal(tctx, r->name, 0, "name"); + + return true; +} + +/* + * base64_decode_data_blob_talloc() => dump_data() gives: + * + * [0000] 26 00 00 00 01 EE C4 71 00 00 00 00 01 00 00 00 &......q ........ + * [0010] 80 00 00 00 77 00 32 00 6B 00 33 00 2D 00 31 00 ....w.2. k.3.-.1. + * [0020] 39 00 31 00 2E 00 77 00 32 00 6B 00 33 00 2E 00 9.1...w. 2.k.3... + * [0030] 62 00 61 00 73 00 65 00 00 00 C4 71 EC F3 b.a.s.e. ...q.. + */ +static const char *dnsp_dnsProperty_deleted_by_b64 = + "JgAAAAHuxHEAAAAAAQAAAIAAAAB3ADIAawAzAC0AMQA5ADEALgB3ADIAawAzAC4A" + "YgBhAHMAZQAAAMRx7PM="; + +static bool dnsp_dnsProperty_deleted_by_check(struct torture_context *tctx, + struct dnsp_DnsProperty *r) +{ + /* + * NDR_PRINT_DEBUG(dnsp_DnsProperty, r); gave: + * + * r: struct dnsp_DnsProperty + * wDataLength : 0x00000026 (38) + * namelength : 0x71c4ee01 (1908731393) + * flag : 0x00000000 (0) + * version : 0x00000001 (1) + * id : DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME (128) + * data : union dnsPropertyData(case 128) + * deleted_by_hostname : 'w2k3-191.w2k3.base' + * name : 0xf3ec71c4 (4092359108) + * + * Note Windows 2003 didn't seem to initialize namelength and name + * both are 'Not Used. The value MUST be ignored ...' + */ + + torture_assert_int_equal(tctx, r->wDataLength, 38, "wDataLength"); + torture_assert_int_equal(tctx, r->namelength, 1908731393, "namelength (random)"); + torture_assert_int_equal(tctx, r->flag, 0, "flag"); + torture_assert_int_equal(tctx, r->version, 1, "version"); + torture_assert_int_equal(tctx, r->id, DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME, "id"); + torture_assert_str_equal(tctx, r->data.deleted_by_hostname, "w2k3-191.w2k3.base", "hostname"); + torture_assert_int_equal(tctx, r->name, 0xf3ec71c4, "name (random)"); + + return true; +} + +/* + * Copy of dnsp_dnsProperty_deleted_by_b64 with wDataLength set to 0 + * and no data in the data element. + * This is a reproducer for https://bugzilla.samba.org/show_bug.cgi?id=14206 + * The dns_property_id was retained so once parsed this structure referenced + * memory past it's end. + * + * [0000] 00 00 00 00 01 EE C4 71 00 00 00 00 01 00 00 00 &......q ........ + * [0010] 80 00 00 00 77 00 32 00 6B 00 33 00 2D 00 31 00 ....w.2. k.3.-.1. + * [0020] 39 00 31 00 2E 00 77 00 32 00 6B 00 33 00 2E 00 9.1...w. 2.k.3... + * [0030] 62 00 61 00 73 00 65 00 00 00 C4 71 EC F3 b.a.s.e. ...q.. + */ +static const uint8_t dnsp_dnsProperty_deleted_by_zero_wDataLength[] = { + 0x00, 0x00, 0x00, 0x00, 0x01, 0xEE, 0xC4, 0x71, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xC4, 0x71, 0xEC, 0xF3 }; + +struct torture_suite *ndr_dnsp_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "dnsp"); + + torture_suite_add_ndr_pull_validate_test_b64( + suite, + dnsp_DnsProperty, + "ZONE_MASTER_SERVERS", + dnsp_dnsProperty_ip4_array_b64, + dnsp_dnsProperty_ip4_array_check); + + torture_suite_add_ndr_pull_validate_test_b64( + suite, + dnsp_DnsProperty, + "ZONE_MASTER_SERVERS_DA", + dnsp_dnsProperty_addr_array_b64, + dnsp_dnsProperty_addr_array_check); + + torture_suite_add_ndr_pull_validate_test_b64( + suite, + dnsp_DnsProperty, + "DELETED_FROM_HOSTNAME", + dnsp_dnsProperty_deleted_by_b64, + dnsp_dnsProperty_deleted_by_check); + + torture_suite_add_ndr_pull_invalid_data_test( + suite, + dnsp_DnsProperty, + dnsp_dnsProperty_deleted_by_zero_wDataLength, + NDR_ERR_BUFSIZE); + + return suite; +} diff --git a/source4/torture/ndr/drsblobs.c b/source4/torture/ndr/drsblobs.c new file mode 100644 index 0000000..0ef2d95 --- /dev/null +++ b/source4/torture/ndr/drsblobs.c @@ -0,0 +1,558 @@ +/* + Unix SMB/CIFS implementation. + test suite for drsblobs ndr operations + + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "torture/ndr/proto.h" +#include "lib/util/base64.h" + +static const uint8_t forest_trust_info_data_out[] = { + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0xca, 0xca, 0x01, 0x00, 0xaf, 0xd5, 0x9b, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x66, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xca, 0xca, 0x01, + 0x00, 0xaf, 0xd5, 0x9b, 0x02, 0x18, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x68, 0x4a, 0x64, + 0x28, 0xac, 0x88, 0xa2, 0x74, 0x17, 0x3e, 0x2d, 0x8f, 0x07, 0x00, 0x00, + 0x00, 0x66, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x02, 0x00, 0x00, 0x00, + 0x46, 0x32 +}; + +static bool forest_trust_info_check_out(struct torture_context *tctx, + struct ForestTrustInfo *r) +{ + torture_assert_int_equal(tctx, r->version, 1, "version"); + torture_assert_int_equal(tctx, r->count, 2, "count"); + torture_assert_int_equal(tctx, r->records[0].record_size, 0x00000018, "record size"); + torture_assert_int_equal(tctx, r->records[0].record.flags, 0, "record flags"); + torture_assert_u64_equal(tctx, r->records[0].record.timestamp, 0x9BD5AF0001CACA3EULL, "record timestamp"); + torture_assert_int_equal(tctx, r->records[0].record.type, FOREST_TRUST_TOP_LEVEL_NAME, "record type"); + torture_assert_int_equal(tctx, r->records[0].record.data.name.size, 7, "record name size"); + torture_assert_str_equal(tctx, r->records[0].record.data.name.string, "f2.test", "record name string"); + torture_assert_int_equal(tctx, r->records[1].record_size, 0x0000003a, "record size"); + torture_assert_int_equal(tctx, r->records[1].record.flags, 0, "record flags"); + torture_assert_u64_equal(tctx, r->records[1].record.timestamp, 0x9BD5AF0001CACA3EULL, "record timestamp"); + torture_assert_int_equal(tctx, r->records[1].record.type, FOREST_TRUST_DOMAIN_INFO, "record type"); + torture_assert_int_equal(tctx, r->records[1].record.data.info.sid_size, 0x00000018, "record info sid_size"); + torture_assert_sid_equal(tctx, &r->records[1].record.data.info.sid, dom_sid_parse_talloc(tctx, "S-1-5-21-677661288-1956808876-2402106903"), "record info sid"); + torture_assert_int_equal(tctx, r->records[1].record.data.info.dns_name.size, 7, "record name size"); + torture_assert_str_equal(tctx, r->records[1].record.data.info.dns_name.string, "f2.test", "record info dns_name string"); + torture_assert_int_equal(tctx, r->records[1].record.data.info.netbios_name.size, 2, "record info netbios_name size"); + torture_assert_str_equal(tctx, r->records[1].record.data.info.netbios_name.string, "F2", "record info netbios_name string"); + + return true; +} + +static const uint8_t trust_domain_passwords_in[] = { + 0x34, 0x1f, 0x6e, 0xcd, 0x5f, 0x14, 0x99, 0xf9, 0xd8, 0x34, 0x9f, 0x1d, + 0x1c, 0xcf, 0x1f, 0x02, 0xb8, 0x30, 0xcc, 0x77, 0x21, 0xc1, 0xf3, 0xe2, + 0xcf, 0x32, 0xe7, 0xf7, 0x86, 0x49, 0x28, 0xa0, 0x57, 0x81, 0xa0, 0x72, + 0x95, 0xd5, 0xa7, 0x49, 0xd7, 0xe7, 0x6f, 0xd1, 0x56, 0x91, 0x44, 0xb7, + 0xe2, 0x4e, 0x48, 0xd2, 0x3e, 0x39, 0xfe, 0x79, 0xd9, 0x1d, 0x4a, 0x92, + 0xc7, 0xbb, 0xe3, 0x65, 0x38, 0x28, 0xb3, 0xb5, 0x6d, 0x1a, 0xfc, 0xf9, + 0xd1, 0xe9, 0xc0, 0x8a, 0x52, 0x5a, 0x86, 0xc1, 0x60, 0x45, 0x85, 0xaa, + 0x20, 0xd8, 0xb4, 0x1f, 0x67, 0x6e, 0xe9, 0xc8, 0xed, 0x52, 0x08, 0x65, + 0xd2, 0x5a, 0x3c, 0xb8, 0xdd, 0x5d, 0xef, 0x59, 0x54, 0x90, 0x75, 0x35, + 0x23, 0x12, 0x92, 0xac, 0xf1, 0x76, 0xb0, 0x16, 0x3d, 0xd8, 0xea, 0x96, + 0xd1, 0xd5, 0x27, 0x37, 0xbe, 0xb8, 0x30, 0x60, 0xab, 0xda, 0x21, 0xc1, + 0x61, 0x66, 0x85, 0xbc, 0x4b, 0xf2, 0x0d, 0x8d, 0x28, 0x1f, 0x02, 0x1c, + 0xcf, 0x39, 0x99, 0x14, 0x6b, 0x1a, 0x59, 0x66, 0x8d, 0x9f, 0x6f, 0x5a, + 0x3d, 0x3d, 0xb4, 0x1e, 0x7d, 0xf4, 0xc3, 0xc6, 0xed, 0xed, 0x87, 0xb1, + 0x35, 0x08, 0xf7, 0x7f, 0x61, 0x00, 0x4b, 0x0d, 0xa7, 0xb7, 0x59, 0x53, + 0xc3, 0x97, 0x55, 0xf9, 0x86, 0x2e, 0x29, 0x6d, 0x00, 0x38, 0xde, 0xe1, + 0x80, 0x37, 0xf4, 0xcb, 0x8d, 0x0d, 0x0d, 0x1f, 0x1c, 0x99, 0xf1, 0x24, + 0x61, 0x14, 0x6b, 0x1a, 0xd9, 0x31, 0xc8, 0x6e, 0xd6, 0x98, 0xab, 0xdb, + 0xb8, 0xaf, 0x99, 0xf9, 0xf4, 0x4c, 0xfe, 0x4b, 0x0d, 0xbb, 0x4f, 0xcc, + 0x63, 0xd3, 0xf0, 0x0c, 0xd1, 0xd6, 0x11, 0x30, 0x68, 0x45, 0x98, 0x07, + 0x44, 0x80, 0xca, 0xa4, 0x7b, 0x10, 0x5b, 0x0b, 0x33, 0xb6, 0x8c, 0xa4, + 0x8f, 0xf1, 0x2b, 0xbf, 0xa2, 0x22, 0xd7, 0xcc, 0x92, 0x34, 0x0b, 0xa0, + 0x05, 0xdf, 0x2f, 0xe3, 0x66, 0x0d, 0x9f, 0x09, 0x84, 0xdb, 0x0b, 0x3a, + 0xfa, 0xd5, 0xa7, 0x1c, 0x46, 0x01, 0xa0, 0x3c, 0x02, 0x8a, 0x6d, 0xec, + 0x97, 0xc1, 0x7c, 0x2f, 0x7e, 0x5d, 0x55, 0x27, 0x37, 0x59, 0x31, 0x64, + 0xe9, 0xc9, 0xd6, 0xfd, 0x8f, 0xf9, 0x59, 0xe7, 0x11, 0x30, 0x4c, 0x76, + 0xb0, 0xe7, 0xee, 0xe9, 0xf7, 0xa2, 0x0f, 0x71, 0x90, 0xdb, 0x1d, 0xb0, + 0xfb, 0xa3, 0x25, 0xf8, 0x0a, 0x6c, 0x69, 0x5c, 0x21, 0xa6, 0xfb, 0x90, + 0x5b, 0x9d, 0x14, 0xe4, 0xea, 0x32, 0xe8, 0xe0, 0x2b, 0x5a, 0x99, 0x0b, + 0xbb, 0x7e, 0x14, 0x6b, 0x36, 0x42, 0x41, 0x0f, 0x44, 0x2f, 0x7f, 0xcf, + 0x1f, 0x89, 0x04, 0xdc, 0x07, 0x2a, 0x57, 0xdf, 0xdd, 0x42, 0x78, 0xf0, + 0x8d, 0x9f, 0x02, 0x1d, 0xaf, 0xff, 0x4f, 0xb2, 0x1e, 0xb4, 0x0b, 0xb2, + 0x4d, 0x7a, 0xdc, 0xf3, 0x7e, 0x81, 0xbb, 0x6b, 0x2e, 0x29, 0x24, 0x61, + 0x93, 0x1e, 0x20, 0x57, 0x66, 0x20, 0xcf, 0x4d, 0x67, 0x76, 0xb0, 0x7b, + 0xd9, 0x9d, 0x30, 0x95, 0xba, 0xb0, 0xcc, 0xf6, 0xcc, 0xff, 0xea, 0x32, + 0x55, 0x15, 0xcd, 0xdf, 0xaf, 0xf6, 0x16, 0xd1, 0x1f, 0x6f, 0xb7, 0xda, + 0x1a, 0x75, 0xc7, 0x4f, 0xb1, 0xeb, 0x1a, 0xe2, 0x17, 0x8b, 0xe8, 0x5f, + 0x41, 0x74, 0x5f, 0x41, 0xe0, 0x46, 0x08, 0x9a, 0xc6, 0x81, 0x19, 0x26, + 0xcd, 0x60, 0xb2, 0x3a, 0x7a, 0x39, 0x2b, 0xee, 0x83, 0x8e, 0xdb, 0x83, + 0x6b, 0x48, 0x24, 0x73, 0x91, 0x31, 0x64, 0xce, 0x2e, 0x43, 0x32, 0xb2, + 0xcd, 0x60, 0x98, 0x87, 0xa9, 0x9b, 0xc3, 0x60, 0x7c, 0xa7, 0x52, 0x3e, + 0xb8, 0x28, 0x4d, 0xcd, 0x5f, 0xaf, 0xe3, 0xe6, 0xa0, 0x06, 0x93, 0xfb, + 0xd9, 0xd4, 0x2b, 0x52, 0xed, 0xec, 0x97, 0x3a, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4c, 0x6b, 0x41, 0xb6, + 0x7c, 0x16, 0xcb, 0x01, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0xb6, 0xe4, 0x4e, 0xa0, 0xf4, 0xed, 0x90, 0x9d, 0x67, 0xff, 0xda, 0xda, + 0xc7, 0xe5, 0xaf, 0xc7, 0x4d, 0xc1, 0x58, 0xaf, 0x5f, 0x06, 0x5c, 0xe9, + 0x4c, 0x5a, 0x02, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x4c, 0x6b, 0x41, 0xb6, 0x7c, 0x16, 0xcb, 0x01, + 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xb6, 0xe4, 0x4e, 0xa0, + 0xf4, 0xed, 0x90, 0x9d, 0x67, 0xff, 0xda, 0xda, 0xc7, 0xe5, 0xaf, 0xc7, + 0x4d, 0xc1, 0x58, 0xaf, 0x5f, 0x06, 0x5c, 0xe9, 0x4c, 0x5a, 0x02, 0xfd, + 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00 +}; + +/* these are taken from the trust objects of a w2k8r2 forest, with a + * trust relationship between the forest parent and a child domain + */ +static const char *trustAuthIncoming = +"AQAAAAwAAAAcAQAASuQ+RXJdzAECAAAAAAEAAMOWL6UVfVKiJOUsGcT03H" +"jHxr2ACsMMOV5ynM617Tp7idNC+c4egdqk4S9YEpvR2YvHmdZdymL6F7QKm8OkXazYZF2r/gZ/bI+" +"jkWbsn4O8qyAc3OUKQRZwBbf+lxBW+vM4O3ZpUjz5BSKCcFQgM+MY91yVU8Nji3HNnvGnDquobFAZ" +"hxjL+S1l5+QZgkfyfv5mQScGRbU1Lar1xg9G3JznUb7S6pvrBO2nwK8g+KZBfJy5UeULigDH4IWo/" +"JmtaEGkKE2uiKIjdsEQd/uwnkouW26XzRc0ulfJnPFftGnT9KIcShPf7DLj/tstmQAAceRMFHJTY3" +"PmxoowoK8HUyBK5D5Fcl3MAQIAAAAAAQAAw5YvpRV9UqIk5SwZxPTceMfGvYAKwww5XnKczrXtOnu" +"J00L5zh6B2qThL1gSm9HZi8eZ1l3KYvoXtAqbw6RdrNhkXav+Bn9sj6ORZuyfg7yrIBzc5QpBFnAF" +"t/6XEFb68zg7dmlSPPkFIoJwVCAz4xj3XJVTw2OLcc2e8acOq6hsUBmHGMv5LWXn5BmCR/J+/mZBJ" +"wZFtTUtqvXGD0bcnOdRvtLqm+sE7afAryD4pkF8nLlR5QuKAMfghaj8ma1oQaQoTa6IoiN2wRB3+7" +"CeSi5bbpfNFzS6V8mc8V+0adP0ohxKE9/sMuP+2y2ZAABx5EwUclNjc+bGijCgrwdTIA=="; + +static const char *trustAuthOutgoing = +"AQAAAAwAAAAcAQAASuQ+RXJdzAECAAAAAAEAAMOWL6UVfVKiJOUsGcT03H" +"jHxr2ACsMMOV5ynM617Tp7idNC+c4egdqk4S9YEpvR2YvHmdZdymL6F7QKm8OkXazYZF2r/gZ/bI+" +"jkWbsn4O8qyAc3OUKQRZwBbf+lxBW+vM4O3ZpUjz5BSKCcFQgM+MY91yVU8Nji3HNnvGnDquobFAZ" +"hxjL+S1l5+QZgkfyfv5mQScGRbU1Lar1xg9G3JznUb7S6pvrBO2nwK8g+KZBfJy5UeULigDH4IWo/" +"JmtaEGkKE2uiKIjdsEQd/uwnkouW26XzRc0ulfJnPFftGnT9KIcShPf7DLj/tstmQAAceRMFHJTY3" +"PmxoowoK8HUyBK5D5Fcl3MAQIAAAAAAQAAw5YvpRV9UqIk5SwZxPTceMfGvYAKwww5XnKczrXtOnu" +"J00L5zh6B2qThL1gSm9HZi8eZ1l3KYvoXtAqbw6RdrNhkXav+Bn9sj6ORZuyfg7yrIBzc5QpBFnAF" +"t/6XEFb68zg7dmlSPPkFIoJwVCAz4xj3XJVTw2OLcc2e8acOq6hsUBmHGMv5LWXn5BmCR/J+/mZBJ" +"wZFtTUtqvXGD0bcnOdRvtLqm+sE7afAryD4pkF8nLlR5QuKAMfghaj8ma1oQaQoTa6IoiN2wRB3+7" +"CeSi5bbpfNFzS6V8mc8V+0adP0ohxKE9/sMuP+2y2ZAABx5EwUclNjc+bGijCgrwdTIA=="; + + +static bool trust_domain_passwords_check_in(struct torture_context *tctx, + struct trustDomainPasswords *r) +{ + /* torture_assert_mem_equal(tctx, r->confounder, trust_domain_passwords_in, 512, "confounder mismatch"); */ + + torture_assert_int_equal(tctx, r->outgoing.count, 1, "outgoing count mismatch"); + torture_assert_int_equal(tctx, r->outgoing.current_offset, 0x0000000c, "outgoing current offset mismatch"); + torture_assert_int_equal(tctx, r->outgoing.previous_offset, 0x00000038, "outgoing previous offset mismatch"); + + torture_assert_int_equal(tctx, r->outgoing.current.count, 1, "outgoing current count mismatch"); + torture_assert_int_equal(tctx, r->outgoing.current.array[0].LastUpdateTime, 0xB6416B4C, "outgoing current last update time mismatch"); + torture_assert_int_equal(tctx, r->outgoing.current.array[0].AuthType, TRUST_AUTH_TYPE_CLEAR, "outgoing current auth type mismatch"); + torture_assert_int_equal(tctx, r->outgoing.current.array[0].AuthInfo.clear.size, 0x0000001c, "outgoing current auth info size mismatch"); + torture_assert_mem_equal(tctx, r->outgoing.current.array[0].AuthInfo.clear.password, trust_domain_passwords_in+512+12+8+4+4, 0x0000001c, "outgoing current auth info password mismatch"); + + torture_assert_int_equal(tctx, r->outgoing.previous.count, 0, "outgoing previous count mismatch"); + + torture_assert_int_equal(tctx, r->incoming.count, 1, "incoming count mismatch"); + torture_assert_int_equal(tctx, r->incoming.current_offset, 0x0000000c, "incoming current offset mismatch"); + torture_assert_int_equal(tctx, r->incoming.previous_offset, 0x00000038, "incoming previous offset mismatch"); + + torture_assert_int_equal(tctx, r->incoming.current.count, 1, "incoming current count mismatch"); + torture_assert_int_equal(tctx, r->incoming.current.array[0].LastUpdateTime, 0xB6416B4C, "incoming current last update time mismatch"); + torture_assert_int_equal(tctx, r->incoming.current.array[0].AuthType, TRUST_AUTH_TYPE_CLEAR, "incoming current auth type mismatch"); + torture_assert_int_equal(tctx, r->incoming.current.array[0].AuthInfo.clear.size, 0x0000001c, "incoming current auth info size mismatch"); + torture_assert_mem_equal(tctx, r->incoming.current.array[0].AuthInfo.clear.password, trust_domain_passwords_in+512+12+8+4+4+0x0000001c+12+8+4+4, 0x0000001c, "incoming current auth info password mismatch"); + + torture_assert_int_equal(tctx, r->incoming.previous.count, 0, "incoming previous count mismatch"); + + torture_assert_int_equal(tctx, r->outgoing_size, 0x00000038, "outgoing size mismatch"); + torture_assert_int_equal(tctx, r->incoming_size, 0x00000038, "incoming size mismatch"); + + return true; +} + +static const uint8_t supplementalCredentials_empty1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool supplementalCredentials_empty1_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert(tctx, r->sub.prefix == NULL, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, 0, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 0, "num_packages"); + torture_assert_int_equal(tctx, r->unknown3, 0, "unknown3"); + + return true; +} + +static const uint8_t supplementalCredentials_empty2[] = { + 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00 /* was 0x30 */ + /* + * I've changed the last byte as Samba sets it to 0x00 + * and it's random on Windows. + */ +}; + +static bool supplementalCredentials_empty2_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0x62, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert_str_equal(tctx, r->sub.prefix, SUPPLEMENTAL_CREDENTIALS_PREFIX, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 0, "num_packages"); + torture_assert_int_equal(tctx, r->unknown3, 0x00, "unknown3"); /* This is typically not initialized */ + + return true; +} + +static const char *alpha13_supplementalCredentials = + "AAAAAPgFAAAAAAAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAg" + "ACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAI" + "AAgACAAIAAgACAAUAADACAANAEBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUAcgBvAHMAMDMwMD" + "AwMDAwMjAwMDAwMDNFMDAzRTAwNEMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMwMDAwMDAwODAwMDA" + "wMDhBMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDgwMDAwMDA5MjAwMDAwMDAwMDAwMDAw" + "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA0MTAwNEMwMDUwMDA0ODAwNDEwMDMxMDAzM" + "zAwMkUwMDUzMDA0MTAwNEQwMDQyMDA0MTAwMkUwMDQzMDA0RjAwNTIwMDUwMDA0MTAwNjQwMDZEMD" + "A2OTAwNkUwMDY5MDA3MzAwNzQwMDcyMDA2MTAwNzQwMDZGMDA3MjAwMkMwREQ2QzRFQzJGMDhDQjJ" + "DMERENkM0RUMyRjA4Q0IQAEAAAgBQAGEAYwBrAGEAZwBlAHMANEIwMDY1MDA3MjAwNjIwMDY1MDA3" + "MjAwNkYwMDczMDAwMDAwNTcwMDQ0MDA2OTAwNjcwMDY1MDA3MzAwNzQwMB4AwAMBAFAAcgBpAG0AY" + "QByAHkAOgBXAEQAaQBnAGUAcwB0ADMxMDAwMTFEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQ1OE" + "FFNDY1NjY0NkVENUIxRTZCRUQ3NjBGMzZCMUM1RDBEMzRDNDE2MERBOEQ0QzM4M0U1OTQxMzM3MDl" + "COUQyMDYzMTUxQTI3ODBFODkzMDQ1OTg3N0IyRURGMUU0MDQ1OEFFNDY1NjY0NkVENUIxRTZCRUQ3" + "NjBGMzZCMUMzOTAyNDNBNENBQUUxNDUzMEVERDMwRTUyRjg1OTREQkEzNDBEMUExQzEyNkQ5QUVDN" + "kI3MDE3QzEzRTFEODY0NzNDQTk4RUZCOUI2OTRBODFEMjUyNkIwNzc1ODYzNUQ2MUE2MEMxNjIyMD" + "kxRDE2RDY4NEE1RTk2QzI0QkIwOENBMzQzNjI3M0E3Mzk0NTA1QkZEOUI1NTMzRUMwOUE3M0MyMDF" + "EQTA5RTVBREZEOUMwMzExRTZEMUJBNEIzNEEzN0FFODMyMUE5RTZFREMyQzA5NERBMDcwRUI4NTgz" + "QTYxQTYwQzE2MjIwOTFEMTZENjg0QTVFOTZDMjRCQjA4RTgzRENFNUNBOTJERkI4ODNFQTYwNUM4M" + "jc1OTVGRDE0QjBBM0M2RkRCOTQ2QjM5MkYxMDgzNjEyM0NGQjVFQThFNEZFNjAxNzBFRTA4OTQ2N0" + "MyNUJEMEY0OTAxMDc3MzYyNTk4RUFGRUI5MjAzNEJFMjEyRDVDNTM5MTdBQzE0RTRDM0RFRTcwQjh" + "BRDU2QUQ5NUMwNkNGNzU3M0VDOTY5MzlGOTYzNDQzNkFDQzY4QjYzRUFEM0ZDRDQ0QUM2RjY5QzND" + "MTdDQjlBODA5MDA5M0M0NDQyOERDQTg0ODNERTU5M0JDQUM5NThDNUE0Rjg4NTVDODY5QjAzMTlFR" + "jVCMUM3OTkyQTc5Q0I4N0I3NjFBMEU0QjUzQkYzNTQ0REQxNTQ0OEUwMTAzREJBMkMyRjZDMUVDNE" + "IwMjU5REZCODdFQTNDMDVDRjNEMUY2QzIwNkFDQkZGNTZDOUVGRDI3QUY2NTBDRjJGQjgxRThERTA" + "1QzVGQzE5QjE0QkE4M0UwN0ZCRkM0MUM5RENFQTlFNDY1RTBEODVDNDg2MUZCQTBGQzQyNDI0REEx" + "OTU4Mzk5REE3QTY3MTE3RUM5NTUxQTI1QzBFMzg1OEM2OUZFREFGRjUwRjUwQ0RFQzA0MTdFMUQ0M" + "UJFRjlBNzM5QzM0QzBDOTk3NzI5MERGRTIyNzJCQzVDOTMyMTVGMzkwRUE4QzYxRTIzQ0UwMDBDNg" + "A="; + +static bool alpha13_supplementalCredentials_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0x5F8, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert_str_equal(tctx, r->sub.prefix, SUPPLEMENTAL_CREDENTIALS_PREFIX, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 3, "num_packages"); + torture_assert_str_equal(tctx, r->sub.packages[0].name, "Primary:Kerberos", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[1].name, "Packages", "name of package 1"); + torture_assert_str_equal(tctx, r->sub.packages[2].name, "Primary:WDigest", "name of package 2"); + torture_assert_int_equal(tctx, r->unknown3, 0x00, "unknown3"); /* This is typically not initialized */ + + return true; +} + +static const char *release_4_1_0rc3_supplementalCredentials = + "AAAAALgIAAAAAAAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAg" + "ACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAI" + "AAgACAAIAAgACAAUAAEADYAEAIBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUAcgBvAHMALQBOAG" + "UAdwBlAHIALQBLAGUAeQBzADA0MDAwMDAwMDQwMDAwMDAwMDAwMDAwMDUwMDA1MDAwNzgwMDAwMDA" + "wMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDEyMDAwMDAwMjAwMDAwMDBDODAwMDAwMDAw" + "MDAwMDAwMDAwMDAwMDAwMDEwMDAwMDExMDAwMDAwMTAwMDAwMDBFODAwMDAwMDAwMDAwMDAwMDAwM" + "DAwMDAwMDEwMDAwMDAzMDAwMDAwMDgwMDAwMDBGODAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMD" + "AwMDAxMDAwMDAwMDgwMDAwMDAwMDAxMDAwMDUyMDA0NTAwNEMwMDQ1MDA0MTAwNTMwMDQ1MDAyRDA" + "wMzQwMDJEMDAzMTAwMkQwMDMwMDA1MjAwNDMwMDMzMDAyRTAwNTMwMDQxMDA0RDAwNDIwMDQxMDAy" + "RTAwNDMwMDRGMDA1MjAwNTAwMDQxMDA2NDAwNkQwMDY5MDA2RTAwNjkwMDczMDA3NDAwNzIwMDYxM" + "DA3NDAwNkYwMDcyMDA2MTQzNDMxNERDMjZFNzM0MTBERkQ2OUVENDc1Mjk1QTU1RjJGREUyNEQ2Qj" + "FEMEMzMzk4QkY2NDI3OUI4REMwNjg0Nzc5ODgzNkUwOTE1NTMwMjYwMDlCMkUzMzBDQjBBNzFBOTQ" + "xRjdGOEY3OTYyQTcxQTk0MUY3RjhGNzk2MiAAWAEBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUA" + "cgBvAHMAMDMwMDAwMDAwMjAwMDAwMDUwMDA1MDAwNEMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMwM" + "DAwMDAwODAwMDAwMDlDMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDgwMDAwMDBBNDAwMD" + "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1MjAwNDUwMDRDMDA0NTA" + "wNDEwMDUzMDA0NTAwMkQwMDM0MDAyRDAwMzEwMDJEMDAzMDAwNTIwMDQzMDAzMzAwMkUwMDUzMDA0" + "MTAwNEQwMDQyMDA0MTAwMkUwMDQzMDA0RjAwNTIwMDUwMDA0MTAwNjQwMDZEMDA2OTAwNkUwMDY5M" + "DA3MzAwNzQwMDcyMDA2MTAwNzQwMDZGMDA3MjAwQTcxQTk0MUY3RjhGNzk2MkE3MUE5NDFGN0Y4Rj" + "c5NjIQAJAAAgBQAGEAYwBrAGEAZwBlAHMANEIwMDY1MDA3MjAwNjIwMDY1MDA3MjAwNkYwMDczMDA" + "yRDAwNEUwMDY1MDA3NzAwNjUwMDcyMDAyRDAwNEIwMDY1MDA3OTAwNzMwMDAwMDA0QjAwNjUwMDcy" + "MDA2MjAwNjUwMDcyMDA2RjAwNzMwMDAwMDA1NzAwNDQwMDY5MDA2NzAwNjUwMDczMDA3NDAwHgDAA" + "wEAUAByAGkAbQBhAHIAeQA6AFcARABpAGcAZQBzAHQAMzEwMDAxMUQwMDAwMDAwMDAwMDAwMDAwMD" + "AwMDAwMDBFNDUwOUQ2MERDRDZERkIxOTlBMDY5QjU4NUUyOTdCMEU0RTgwMzc4QUQxMDhFQjdENUJ" + "COUQwNjBDMEVFRURBOUNEMzhGQTk3RjBERUJGNkRCMTkxNDA4RkIwQTQ2OThFNDUwOUQ2MERDRDZE" + "RkIxOTlBMDY5QjU4NUUyOTdCMDg2NjhCREI1QjM4ODg1M0Y5NDc0OTI0RjQzRkYzMEY0NDBFREJEN" + "UU3MUU0Mjg4QjNFRkYyRUFEQUQyQjcwMTNBRTEwODQ0MjlCQTc4RTUwRkYyMTAxRkFEQzEwMEI1Mz" + "NGODYwNzYyQzc1OTU0MTNEMENCRUNEODNDODJDRUE3Njk3MjgxQjI5QTU5M0U3MzRFQUQzMEZBMkE" + "3N0EzQkVERjA4MjEzMDNGMTUwOThFMUM1RkMzNjhDQzY2MTZEMTI1MDU4NzQ4RTUyRkMzM0YzQ0ZC" + "MUE0NUIzMzNEMUJDM0Y4NjA3NjJDNzU5NTQxM0QwQ0JFQ0Q4M0M4MkNFQTczNDk4MDNFN0FEODUyN" + "zEyMTlBNzEyMEQ4MkE4NjA2RDlERUQxMDA2NDk2MTkyQjZEQTM0RkQxMDdGOThDMjdDOTUyQzMwND" + "Q0MUFDODcwMTYwODdGNDU0ODUwMTRCQ0Q1QTNDNUU4NjBFQ0Y5RTQzMzJCODI1OTUyOTJFODYxNkF" + "DREU1RUJERjFFMkIzNDZCNTcyRUE2RjM4MkQyOTJCNkE4MDk3NzY1RDMyMTI0M0Y4QjFCRjAzNEFB" + "MjZGNEI3ODYwRUJGMzY4NDc5MTExRjQzRkMyRTVFQkUzQkNGRkE3N0RDOTdEQTJBQ0I0ODQ1NjIwQ" + "zg3QkNFMTYwQkE3RTEyOTFFQ0MwODdFQkE4Qzg1QkNDQjc4MzVGRTYyRUU4RTA0QTBBNzQwOENDQT" + "MxRkVDRjdFQTQ0MjI4QjJCRjVFQjg5MEQ2QjBEODgwNzVEMzhFREYxQzc5NEY1MDgxNUE2MzcxNTM" + "5QURCQTEyNkFDODc0Q0EyNzNBMzgwRTM0NjRFQkZERDE4MTgzRDY1MDlDOTJEQzVCQzhCNTg4M0Iy" + "QTlGRTcyMUQ0RkQ0MEQ3QkI0QzlENjcxOTYxNTRFRTQ4QkIzMDkxNEE3QkREODcyOTMyMjc1M0JDR" + "Dk2QkI5QzY1MzdFQjc1ODg3MUZDQzhGMEUxQjkyRTgyNEIxQTBDMjU1NjE2QURCMzYyMDc5NTQ5OT" + "Q5MUJCRTY5NTA0AA=="; + +static bool release_4_1_0rc3_supplementalCredentials_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0x8b8, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert_str_equal(tctx, r->sub.prefix, SUPPLEMENTAL_CREDENTIALS_PREFIX, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 4, "num_packages"); + torture_assert_str_equal(tctx, r->sub.packages[0].name, "Primary:Kerberos-Newer-Keys", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[1].name, "Primary:Kerberos", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[2].name, "Packages", "name of package 1"); + torture_assert_str_equal(tctx, r->sub.packages[3].name, "Primary:WDigest", "name of package 2"); + torture_assert_int_equal(tctx, r->unknown3, 0x00, "unknown3"); /* This is typically not initialized */ + + return true; +} + +static const char *release_4_5_0pre_GPG_supplementalCredentials = + "AAAAADAQAAAAAAAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAg" + "ACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAI" + "AAgACAAIAAgACAAUAAFADYAdAQBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUAcgBvAHMALQBOAG" + "UAdwBlAHIALQBLAGUAeQBzADA0MDAwMDAwMDQwMDAwMDAwNDAwMDQwMDQyMDA0MjAwMzgwMTAwMDA" + "wMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDEyMDAwMDAwMjAwMDAwMDA3QTAxMDAwMDAw" + "MDAwMDAwMDAwMDAwMDAwMDEwMDAwMDExMDAwMDAwMTAwMDAwMDA5QTAxMDAwMDAwMDAwMDAwMDAwM" + "DAwMDAwMDEwMDAwMDAzMDAwMDAwMDgwMDAwMDBBQTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMD" + "AwMDAxMDAwMDAwMDgwMDAwMDBCMjAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDEyMDAwMDA" + "wMjAwMDAwMDBCQTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDExMDAwMDAwMTAwMDAwMDBE" + "QTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAzMDAwMDAwMDgwMDAwMDBFQTAxMDAwMDAwM" + "DAwMDAwMDAwMDAwMDAwMDEwMDAwMDAxMDAwMDAwMDgwMDAwMDBGMjAxMDAwMDAwMDAwMDAwMDAwMD" + "AwMDAwMDEwMDAwMDEyMDAwMDAwMjAwMDAwMDBGQTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDA" + "wMDExMDAwMDAwMTAwMDAwMDAxQTAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAzMDAwMDAw" + "MDgwMDAwMDAyQTAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAxMDAwMDAwMDgwMDAwMDAzM" + "jAyMDAwMDQxMDA0NDAwNDQwMDRGMDA0RDAwMkUwMDUzMDA0MTAwNEQwMDQyMDA0MTAwMkUwMDQ1MD" + "A1ODAwNDEwMDREMDA1MDAwNEMwMDQ1MDAyRTAwNDMwMDRGMDA0RDAwNzAwMDZGMDA3MzAwNjkwMDc" + "4MDA3NTAwNzMwMDY1MDA3MjAwMzEwMDM3NkI0RjI5OEM2QTJBNjJGNUJFQjMyOEZDQjUxMUFFREIz" + "NTI3NzY1NzQ2MDkzMzcyMTZDODk5MUQ0NUM3RTI4REZDMDA2Q0MzNzlFNzEyRkU0QTIxRTNBMDg1N" + "zM5MDM0QzgyNjBFMUY0NTRBN0MzNEM4MjYwRTFGNDU0QTdDODBDNDA1QjgxMkM2OEFCN0Q2QjZGRT" + "ZFRjRCQTM1N0ZBMTA1NjRDRjZFOUVFRUY2MUZEQUNGOEREM0Y3RkI0MDAzQjhBM0U1Qzk3NzgxOUF" + "BNkM0NzRFQTJDRkQxRjlFMkE4NjQwQUU5NDhBQzhGMTJBODY0MEFFOTQ4QUM4RjFEMDlDMjJGMTU0" + "MDZBQjk1QTUzRUQ5NkYxREFFRkJCNEY4NzUwMDQ4OEE1NzE0OEJFRTIzMkZGMjE4Q0Y3REE1MkZBQ" + "jYyQTEwQzk5NzUwMkYzNEQ1MEQzOTZEODgwODA0MEYxNkI3Q0VDN0ZDRTAxNDBGMTZCN0NFQzdGQ0" + "UwMSAArAEBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUAcgBvAHMAMDMwMDAwMDAwMjAwMDIwMDQ" + "yMDA0MjAwNzQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMwMDAwMDAwODAwMDAwMEI2MDAwMDAwMDAw" + "MDAwMDAwMDAwMDAwMDAxMDAwMDAwMDgwMDAwMDBCRTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwM" + "DAwMDA4MDAwMDAwQzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwODAwMDAwMENFMDAwMD" + "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQxMDA0NDAwNDQwMDRGMDA" + "0RDAwMkUwMDUzMDA0MTAwNEQwMDQyMDA0MTAwMkUwMDQ1MDA1ODAwNDEwMDREMDA1MDAwNEMwMDQ1" + "MDAyRTAwNDMwMDRGMDA0RDAwNzAwMDZGMDA3MzAwNjkwMDc4MDA3NTAwNzMwMDY1MDA3MjAwMzEwM" + "DM0QzgyNjBFMUY0NTRBN0MzNEM4MjYwRTFGNDU0QTdDMkE4NjQwQUU5NDhBQzhGMTJBODY0MEFFOT" + "Q4QUM4RjEeAMADAQBQAHIAaQBtAGEAcgB5ADoAVwBEAGkAZwBlAHMAdAAzMTAwMDExRDAwMDAwMDA" + "wMDAwMDAwMDAwMDAwMDAwMENFMERENjk2NEU0ODNDN0YxRTIzMjk0RjRGMTMwMUJGRjI3Rjg5NTA1" + "MEEwMEVCODZBMTgwNzMyMkM1MzQzMUYxMEY2OTU2MUIyQUJFRjg2ODIyMjE2RTc5RTFGN0VGMUNFM" + "ERENjk2NEU0ODNDN0YxRTIzMjk0RjRGMTMwMUJGRjI3Rjg5NTA1MEEwMEVCODZBMTgwNzMyMkM1Mz" + "QzMUY0MURCMkRENzJCQzRERTgzRkUzMTBFQTEzRjQwNTE0RkNFMERENjk2NEU0ODNDN0YxRTIzMjk" + "0RjRGMTMwMUJGMUY2NjY3RjcyN0I2RjA4RERBOUFENjUyODdGMjVGNEQxRjY2NjdGNzI3QjZGMDhE" + "REE5QUQ2NTI4N0YyNUY0RDMwNDUyNUJCNTQwNDQ2NjE4OTRFM0YyRDQ4RUI5MTAyMzhGOTZFNDVBM" + "Tk2RkU5MjczQjREOUQxRUVEM0ExM0UxRjY2NjdGNzI3QjZGMDhEREE5QUQ2NTI4N0YyNUY0RDE0Qj" + "cyMDdGQUNGMEM2NDE5REMxRjNGMDIyMUYyREVBMzhGOTZFNDVBMTk2RkU5MjczQjREOUQxRUVEM0E" + "xM0VFMUJGMUI3NzY3NkZCNDcwODMwREYwMDM4RTIyRUY0QUUxQkYxQjc3Njc2RkI0NzA4MzBERjAw" + "MzhFMjJFRjRBRDVEQjFFOEJDNDE3QUEwMjlCNUU5MDFGQTA4OTQ0MjcxM0Q2ODUyNkE2MUVENDE1N" + "kQ5REY3OTA1MkUyQzZDQTExMUY4NTc4MkZFODRCNUQ2RDlDMzJBMDYxNkY3Q0U0QzkxMjUzQzU1Mz" + "QxN0MzMTY5MjM4Qzg1MjlDMkY3RjE0NzMzMUUyRDg5N0UyRDlBRjlDMzhGRDI2RjIwMkY0NTQ3MzM" + "xRTJEODk3RTJEOUFGOUMzOEZEMjZGMjAyRjQ1MUY2RDAyN0VDNjc1REZFNEQyMjlEOTA0MDFDOTZG" + "MEY0MjdCMjA5Nzg2MEJBQkI4QjUzQ0U0MUFBNzA0QTI0OTQyN0IyMDk3ODYwQkFCQjhCNTNDRTQxQ" + "UE3MDRBMjQ5QTYwN0E2M0ZERTJFRjAzN0U1M0NEQzkyNEM1NTI0QUIxQzA2OUZDRDNFOThGMDNFNz" + "lBOEJFN0NBQzMzOERDNUVERDY5RDEwNThENEY2QzQ2NTNCNTE4NTNFMjI1OUI5Q0M3NjZGRjRFNDg" + "5RUI4MEZFQTRGMThFMTIyMTJEQTkQALQAAgBQAGEAYwBrAGEAZwBlAHMANEIwMDY1MDA3MjAwNjIw" + "MDY1MDA3MjAwNkYwMDczMDAyRDAwNEUwMDY1MDA3NzAwNjUwMDcyMDAyRDAwNEIwMDY1MDA3OTAwN" + "zMwMDAwMDA0QjAwNjUwMDcyMDA2MjAwNjUwMDcyMDA2RjAwNzMwMDAwMDA1NzAwNDQwMDY5MDA2Nz" + "AwNjUwMDczMDA3NDAwMDAwMDUzMDA2MTAwNkQwMDYyMDA2MTAwNDcwMDUwMDA0NzAwIAB2BAEAUAB" + "yAGkAbQBhAHIAeQA6AFMAYQBtAGIAYQBHAFAARwAyRDJEMkQyRDJENDI0NTQ3NDk0RTIwNTA0NzUw" + "MjA0RDQ1NTM1MzQxNDc0NTJEMkQyRDJEMkQwQTU2NjU3MjczNjk2RjZFM0EyMDQ3NkU3NTUwNDcyM" + "Dc2MzIwQTBBNjg1MTQ1NEQ0MTJGMzM3ODZCNDY2QTY4NDQzMzRCNkU0MTUxNjYyRjY1NDczNjc5NE" + "I2ODU5NjQ2OTZFNzEyRjRGMzg0QTcyNzg0QzQzMzY0NDc3NkU2NjZGNzA0QzRGNzQyRjM2NEY0QzM" + "yNDQ0MzQzNjc1NTUxNkM0MjUwNjMwQTMzNDE2NTMyNkU2QzU5NDUzMTc0MkYzNzdBNEM2NzY1NTA2" + "NDZBNDM2RjZBN0E0OTQzMzEzOTcxMzA0RjM5MkY1NzM0NEIzNTU0N0E3MzY5NjU2RjZDNkU0NTQ0N" + "jQzMjY3NTc0Njc1NTAzNzc4NEQ2NjQyNzI0RTY0NzM3QTM3MEE2RTYyNDg1NzdBNDc2NDM4NzU1Nj" + "JCNzQ0NDJGNkEzMDM4Nzk2NzcxNDI2ODdBNEI0QTU1NkE0OTU5NTY2MTUwNTQ3NjY2MzQ3NjMwNDM" + "3NjQ1NjI2NTQ0NDg0QzZDNTI3QTQ0Njc0Nzc5NDc3MzMxMzU0ODY0MzI1MDRDNDgzMjBBNkU2RDM1" + "NzU0OTc0NTk0OTY0NTczOTQyNkMzOTM4Mzk2MTU1MzczNjZENEU1MjU0NkIzOTMwMzE1MjM5NEM2O" + "TcwNTA2RDQ4NzU0MTc4NTE0NzQ0NTI3NTcxNEI2Rjc0NTg2QzU3NDY3NjM0NjE2NDY2NTU1NDZEMz" + "U2RTZCNTAwQTQ0Mzg3MzczNDU2QjMyNDM1NTZBNEQ0RjQ3NkI3MDJGMkYyQjM3MzQ0QjRBNzI2OTV" + "BMzg3NTcxMzE1NzMxNTY3ODY3NzE2RTc1NDY1ODREMzM3MjQyMzkzNjY2NDM1QTU4NDM0RDJGNDQ1" + "MjRDNjc1MzY4NjMzMDU4NTM0MjQ4MEE2ODQxNDE2QzVBMkI3MzYzNEQ1QTQ3Mzg0OTU3MkIzOTU2N" + "EI1QTU2NDY2QTZCNTk0RTZGNzI1MjRDNEE1QTZFNzY3MDU2NEQ2ODc2NTY2RTM1Mzk0QTYxNDE2Mj" + "U1NDY3MzUwNTk0Qjc0Nzg3ODJCNDU2RjZFMzczNzM4NDE1OTBBMzkzNDRCNzY1MDZFNEI1MzMzNzA" + "zMTQ5NjM2ODU3NTQ2QTc2NEM3MTc2NTk2Njc2NzM1NzRENjc2OTY0NEQzMDc5NUE1OTU5NEE2NzUx" + "NTM2QjdBNDczNzUzNEQ1OTY0NTA1QTc3NzkzMjQxNEQ2QTRFMzU0QTQzNTI2RDQ3NjkwQTY0NjUzN" + "jJGMzc2RjRFNTU1OTMxNEE1NTUwNTY0Njc1MzkyQjUyNUE2RDY4NjkzNTM1NEY3NzJGN0E0RjU1Nz" + "czMzcwNDc2NzBBM0Q3MTY4NkM0NjBBMkQyRDJEMkQyRDQ1NEU0NDIwNTA0NzUwMjA0RDQ1NTM1MzQ" + "xNDc0NTJEMkQyRDJEMkQwQQA="; + +static bool release_4_5_0pre_GPG_supplementalCredentials_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0x1030, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert_str_equal(tctx, r->sub.prefix, SUPPLEMENTAL_CREDENTIALS_PREFIX, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 5, "num_packages"); + torture_assert_str_equal(tctx, r->sub.packages[0].name, "Primary:Kerberos-Newer-Keys", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[1].name, "Primary:Kerberos", "name of package 1"); + torture_assert_str_equal(tctx, r->sub.packages[2].name, "Primary:WDigest", "name of package 2"); + torture_assert_str_equal(tctx, r->sub.packages[3].name, "Packages", "name of package 3"); + torture_assert_str_equal(tctx, r->sub.packages[4].name, "Primary:SambaGPG", "name of package 4"); + torture_assert_int_equal(tctx, r->unknown3, 0x00, "unknown3"); /* This is typically not initialized */ + + return true; +} + +static const char *win2012R2_supplementalCredentials = + "AAAAACgIAAAAAAAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAg" + "ACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAI" + "AAgACAAIAAgACAAUAAEADYA3AEBAFAAcgBpAG0AYQByAHkAOgBLAGUAcgBiAGUAcgBvAHMALQBOAG" + "UAdwBlAHIALQBLAGUAeQBzADA0MDAwMDAwMDMwMDAwMDAwMDAwMDAwMDNlMDAzZTAwNzgwMDAwMDA" + "wMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDEyMDAwMDAwMjAwMDAwMDBiNjAwMDAwMDAw" + "MDAwMDAwMDAwMDAwMDAwMDEwMDAwMDExMDAwMDAwMTAwMDAwMDBkNjAwMDAwMDAwMDAwMDAwMDAwM" + "DAwMDAwMDEwMDAwMDAzMDAwMDAwMDgwMDAwMDBlNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD" + "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMyMDAzMDAwMzAwMDM4MDA1MjAwMzIwMDJlMDA0ODA" + "wNGYwMDU3MDA1NDAwNGYwMDJlMDA0MTAwNDIwMDQxMDA1MjAwNTQwMDRjMDA0NTAwNTQwMDJlMDA0" + "ZTAwNDUwMDU0MDA2YjAwNzIwMDYyMDA3NDAwNjcwMDc0MDAwNzNhYTVmMjVmZjU2MzUyZTI2ZWYxZ" + "DMxMGJhZjAzMWY0MWZmMmFkNzZlNzA1YzgzNTQyZmJlZGJhNjY0ZjM0YTBmYTAyYzQxNWE3MmFiNj" + "JkYjdlNzliNDBmNjJhNjhjMjVlZjc0YzE5ZDM1OGZkIAD8AAEAUAByAGkAbQBhAHIAeQA6AEsAZQB" + "yAGIAZQByAG8AcwAwMzAwMDAwMDAxMDAwMDAwM2UwMDNlMDAzODAwMDAwMDAwMDAwMDAwMDAwMDAw" + "MDAwMzAwMDAwMDA4MDAwMDAwNzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM" + "DAwMDAwMDAwMzIwMDMwMDAzMDAwMzgwMDUyMDAzMjAwMmUwMDQ4MDA0ZjAwNTcwMDU0MDA0ZjAwMm" + "UwMDQxMDA0MjAwNDEwMDUyMDA1NDAwNGMwMDQ1MDA1NDAwMmUwMDRlMDA0NTAwNTQwMDZiMDA3MjA" + "wNjIwMDc0MDA2NzAwNzQwMGMyNWVmNzRjMTlkMzU4ZmQQAJAAAgBQAGEAYwBrAGEAZwBlAHMANGIw" + "MDY1MDA3MjAwNjIwMDY1MDA3MjAwNmYwMDczMDAyZDAwNGUwMDY1MDA3NzAwNjUwMDcyMDAyZDAwN" + "GIwMDY1MDA3OTAwNzMwMDAwMDA0YjAwNjUwMDcyMDA2MjAwNjUwMDcyMDA2ZjAwNzMwMDAwMDA1Nz" + "AwNDQwMDY5MDA2NzAwNjUwMDczMDA3NDAwHgDAAwEAUAByAGkAbQBhAHIAeQA6AFcARABpAGcAZQB" + "zAHQAMzEwMDAxMWQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA3ZGE1MjA2NjllNTdkYzhjMzI1NDQ0" + "Y2ZkYjU5ZjkxZGZiZDE1MzZhNDIxMjgyNjc4MjE4MWI5OTM2ZjczYWIzOWYyN2QxYjM4YmRlNzhiO" + "TVhOTJmNDM1YWQyNDAzNDU3ZGE1MjA2NjllNTdkYzhjMzI1NDQ0Y2ZkYjU5ZjkxZGZiZDE1MzZhND" + "IxMjgyNjc4MjE4MWI5OTM2ZjczYWIzNjFjNTgyZGY1NWZiOGZmMzMyMzc0ZDU4NDExNWI2Y2U3ZGE" + "1MjA2NjllNTdkYzhjMzI1NDQ0Y2ZkYjU5ZjkxZDU3NGM4MmQxMWZjNzcyOGNiYmY0NWI1Yjc0NmMx" + "NmY0NjhhZmFhYTI0OTEwODM2ZWMyMGYyMTBjNmQ1ODZmZTE3ZjQxN2RkOWIzMjE5OWJiOTkzZmMxN" + "mQ3NzA5MjFiMDU3NGM4MmQxMWZjNzcyOGNiYmY0NWI1Yjc0NmMxNmY0NjhhZmFhYTI0OTEwODM2ZW" + "MyMGYyMTBjNmQ1ODZmZTE5ODZlNWM1ZDM2NjllZDI0ZDgyM2RlNWYyZmZhNjk1ZTU3NGM4MmQxMWZ" + "jNzcyOGNiYmY0NWI1Yjc0NmMxNmY0OGNjZGJjMWZhZWM0OWFmOGY2ZGQ0N2M5MmViM2JmNjMwMzAy" + "Njc2NmI4ZWQzYjU0ZTQyNGU1ZDNlNDVkNjlkNmIwYzI3Y2ZhMzZmNjliN2NmNDVjMGNhYjU3NmY1M" + "zRiOWU3NTJhOWUyNTYzMmU4M2FkMmE5MDBmMWUyOGY5ZjE0MjFkYzUwODMyMjQ1Nzc3NTg5ODIyZT" + "EwNGY2NjNkNjY3YzJiZTc3Y2E4MzMwNzVkZmRlZTRlYTUwZWIxNzc3YjMxOGNmZTJlMzQzOTg0ZDU" + "xOTBlODAwMzA4ZmMxODBiMzE4Y2ZlMmUzNDM5ODRkNTE5MGU4MDAzMDhmYzE4MDRhYmIyZDQzMjE0" + "ZGQxNGFkNDA0YzdiYzE4ZGI0NzFjNDRjZjcyZmI2YmUwM2IwZWRiZjNhNzYyNTgwZGIzOTYzZjk5M" + "zVjNmM4N2Q2NWJhZmU0NGY4Zjc2NzViODE3NjgwN2RmZThiZGZlYjJiMTcyMzc4MWQzMThhZGRkZW" + "NmNzQ5MmIwZjE3ZmIwZmRmMjhkN2E2MWMzNTFlYTRkYWU2MDc0MzMxMTAwYjk5YWJiOTY1NTk5ZDY" + "1NjkxNDVjOWM5MTJjOWI2Yzk0ZjNiMDA3ZmM5YTA2OGFiMDhkNTA5gA=="; + + +static bool win2012R2_supplementalCredentials_check(struct torture_context *tctx, + struct supplementalCredentialsBlob *r) +{ + torture_assert_int_equal(tctx, r->unknown1, 0, "unknown1"); + torture_assert_int_equal(tctx, r->__ndr_size, 0x828, "__ndr_size"); + torture_assert_int_equal(tctx, r->unknown2, 0, "unknown2"); + torture_assert_str_equal(tctx, r->sub.prefix, SUPPLEMENTAL_CREDENTIALS_PREFIX, "prefix"); + torture_assert_int_equal(tctx, r->sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE, "signature"); + torture_assert_int_equal(tctx, r->sub.num_packages, 4, "num_packages"); + torture_assert_str_equal(tctx, r->sub.packages[0].name, "Primary:Kerberos-Newer-Keys", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[1].name, "Primary:Kerberos", "name of package 0"); + torture_assert_str_equal(tctx, r->sub.packages[2].name, "Packages", "name of package 1"); + torture_assert_str_equal(tctx, r->sub.packages[3].name, "Primary:WDigest", "name of package 2"); + torture_assert_int_equal(tctx, r->unknown3, 0x00, "unknown3"); /* This is typically not initialized, we force to 0 */ + + return true; +} + +struct torture_suite *ndr_drsblobs_suite(TALLOC_CTX *ctx) +{ + DATA_BLOB win2012R2_supplementalCredentials_blob; + struct torture_suite *suite = torture_suite_create(ctx, "drsblobs"); + struct torture_suite *empty1_suite = torture_suite_create(ctx, "empty1"); + struct torture_suite *empty2_suite = torture_suite_create(ctx, "empty2"); + struct torture_suite *alpha13_suite = torture_suite_create(ctx, "alpha13"); + struct torture_suite *release_4_1_0rc3_suite = torture_suite_create(ctx, "release-4-1-0rc3"); + struct torture_suite *release_4_5_0pre_GPG_suite = torture_suite_create(ctx, "release-4-5-0pre-GPG"); + struct torture_suite *win2012R2_suite = torture_suite_create(ctx, "win2012R2_suite"); + torture_suite_add_suite(suite, empty1_suite); + torture_suite_add_suite(suite, empty2_suite); + torture_suite_add_suite(suite, alpha13_suite); + torture_suite_add_suite(suite, release_4_1_0rc3_suite); + torture_suite_add_suite(suite, release_4_5_0pre_GPG_suite); + torture_suite_add_suite(suite, win2012R2_suite); + + torture_suite_add_ndr_pull_test(suite, ForestTrustInfo, forest_trust_info_data_out, forest_trust_info_check_out); + torture_suite_add_ndr_pull_test(suite, trustDomainPasswords, trust_domain_passwords_in, trust_domain_passwords_check_in); + + torture_suite_add_ndr_pull_validate_test_blob(suite, + trustAuthInOutBlob, + base64_decode_data_blob_talloc(suite, trustAuthIncoming), + NULL); + + torture_suite_add_ndr_pull_validate_test_blob(suite, + trustAuthInOutBlob, + base64_decode_data_blob_talloc(suite, trustAuthOutgoing), + NULL); + + torture_suite_add_ndr_pull_validate_test(empty1_suite, supplementalCredentialsBlob, + supplementalCredentials_empty1, + supplementalCredentials_empty1_check); + + torture_suite_add_ndr_pull_validate_test(empty2_suite, supplementalCredentialsBlob, + supplementalCredentials_empty2, + supplementalCredentials_empty2_check); + + torture_suite_add_ndr_pull_validate_test_blob(alpha13_suite, + supplementalCredentialsBlob, + base64_decode_data_blob_talloc(suite, alpha13_supplementalCredentials), + alpha13_supplementalCredentials_check); + + torture_suite_add_ndr_pull_validate_test_blob(release_4_1_0rc3_suite, + supplementalCredentialsBlob, + base64_decode_data_blob_talloc(suite, release_4_1_0rc3_supplementalCredentials), + release_4_1_0rc3_supplementalCredentials_check); + + torture_suite_add_ndr_pull_validate_test_blob(release_4_5_0pre_GPG_suite, + supplementalCredentialsBlob, + base64_decode_data_blob_talloc(suite, release_4_5_0pre_GPG_supplementalCredentials), + release_4_5_0pre_GPG_supplementalCredentials_check); + + /* This last byte is typically not initialized, we force to zero to allow pull/push */ + win2012R2_supplementalCredentials_blob = base64_decode_data_blob_talloc(suite, win2012R2_supplementalCredentials); + win2012R2_supplementalCredentials_blob.data[win2012R2_supplementalCredentials_blob.length-1] = 0; + torture_suite_add_ndr_pull_validate_test_blob(win2012R2_suite, + supplementalCredentialsBlob, + win2012R2_supplementalCredentials_blob, + win2012R2_supplementalCredentials_check); + + return suite; +} diff --git a/source4/torture/ndr/drsuapi.c b/source4/torture/ndr/drsuapi.c new file mode 100644 index 0000000..5d26c4b --- /dev/null +++ b/source4/torture/ndr/drsuapi.c @@ -0,0 +1,309 @@ +/* + Unix SMB/CIFS implementation. + test suite for drsuapi ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "torture/ndr/proto.h" + +static const uint8_t DsAddEntry_req1_dat[] = { + 0x00, 0x00, 0x00, 0x00, 0x52, 0xd7, 0x26, 0x47, 0x58, 0xd3, 0xd4, 0x45, + 0xaf, 0xa3, 0x85, 0x49, 0x9d, 0x26, 0xb6, 0x11, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x44, 0x00, + 0x53, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x2c, 0x00, 0x43, 0x00, + 0x4e, 0x00, 0x3d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x33, 0x00, + 0x2d, 0x00, 0x31, 0x00, 0x30, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x43, 0x00, + 0x4e, 0x00, 0x3d, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x65, 0x00, 0x2d, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2d, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x2d, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, 0x73, 0x00, 0x2c, 0x00, 0x43, 0x00, + 0x4e, 0x00, 0x3d, 0x00, 0x53, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x19, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, + 0x0e, 0x03, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x73, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, + 0x0e, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x2c, 0x07, 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x02, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, + 0x1c, 0x07, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x02, 0x00, + 0xb3, 0x05, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x02, 0x00, + 0x77, 0x01, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x00, + 0x03, 0x02, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x68, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x80, 0x68, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x54, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x94, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0xfd, 0x01, 0x0f, 0x00, + 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x16, 0xd8, 0xd8, 0x2d, 0x03, 0xe4, 0xc3, 0x74, 0x65, 0x8b, 0xdd, 0x61, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0xff, 0x01, 0x0f, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00, + 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x16, 0xd8, 0xd8, 0x2d, 0x03, 0xe4, 0xc3, 0x74, 0x65, 0x8b, 0xdd, 0x61, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x16, 0xd8, 0xd8, 0x2d, 0x03, 0xe4, 0xc3, 0x74, + 0x65, 0x8b, 0xdd, 0x61, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x17, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x02, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x4e, 0x00, + 0x54, 0x00, 0x44, 0x00, 0x53, 0x00, 0x2d, 0x00, 0x44, 0x00, 0x53, 0x00, + 0x41, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x2c, 0x00, + 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0xae, 0xa5, 0x70, 0xc5, + 0x6d, 0x2a, 0x27, 0x4e, 0xa1, 0xcc, 0xde, 0x11, 0xcb, 0x76, 0x56, 0x22, + 0x03, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0xb0, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x02, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x2c, 0x00, + 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0x44, 0x00, 0x02, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, + 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, + 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x02, 0x00, 0xb0, 0x00, 0x00, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x54, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x2c, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x6e, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, + 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x02, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x33, 0x00, 0x2d, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x43, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, + 0x2c, 0x00, 0x44, 0x00, 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x2c, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x3d, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t DsAddEntry_resp1_dat[] = { + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x88, 0xf1, 0x0d, 0x4a, 0xb8, 0xa0, 0xea, 0x47, 0xbb, 0xe5, 0xe6, 0x14, + 0x72, 0x3f, 0x16, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* +static const uint8_t DsBind_req1_dat[] = { + 0x00, 0x00, 0x02, 0x00, 0x9c, 0xb9, 0xfa, 0x6a, 0x26, 0x6e, 0x4a, 0x46, + 0x97, 0x5f, 0xf5, 0x8f, 0x10, 0x52, 0x18, 0xbc, 0x04, 0x00, 0x02, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x7f, 0xfb, 0xff, 0x1f, + 0xda, 0x95, 0x70, 0x26, 0xc1, 0x67, 0xae, 0x4e, 0xb6, 0xfe, 0xf2, 0x83, + 0x15, 0xe3, 0x87, 0xe8, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8a, 0xe3, 0x13, 0x71, 0x02, 0xf4, 0x36, 0x71, 0x02, 0x40, 0x28, 0x00, + 0x35, 0x42, 0x51, 0xe3, 0x06, 0x4b, 0xd1, 0x11, 0xab, 0x04, 0x00, 0xc0, + 0x4f, 0xc2, 0xdc, 0xd2, 0x04, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, + 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, + 0x02, 0x00, 0x00, 0x00 +}; +*/ + +static const uint8_t DsBind_resp1_dat[] = { + 0x00, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7f, 0xfb, 0xff, 0x1f, 0x81, 0xa6, 0xff, 0x5d, 0x80, 0x13, 0x94, 0x41, + 0xa3, 0x72, 0xe9, 0xb7, 0x79, 0xd7, 0x02, 0x68, 0xf8, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xc6, 0x55, 0x68, + 0xcf, 0x05, 0x42, 0x4a, 0xbb, 0xd3, 0x74, 0xcc, 0x6c, 0xaa, 0xdc, 0x73, + 0x00, 0x00, 0x00, 0x00 +}; + +/* +static const uint8_t DsBind_req2_dat[] = { + 0x00, 0x00, 0x02, 0x00, 0x9c, 0xb9, 0xfa, 0x6a, 0x26, 0x6e, 0x4a, 0x46, + 0x97, 0x5f, 0xf5, 0x8f, 0x10, 0x52, 0x18, 0xbc, 0x04, 0x00, 0x02, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x7f, 0xfb, 0xff, 0x1f, + 0xda, 0x95, 0x70, 0x26, 0xc1, 0x67, 0xae, 0x4e, 0xb6, 0xfe, 0xf2, 0x83, + 0x15, 0xe3, 0x87, 0xe8, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; +*/ + +static const uint8_t DsBind_resp2_dat[] = { + 0x00, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7f, 0xfb, 0xff, 0x1f, 0x81, 0xa6, 0xff, 0x5d, 0x80, 0x13, 0x94, 0x41, + 0xa3, 0x72, 0xe9, 0xb7, 0x79, 0xd7, 0x02, 0x68, 0xf8, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xd7, 0x26, 0x47, + 0x58, 0xd3, 0xd4, 0x45, 0xaf, 0xa3, 0x85, 0x49, 0x9d, 0x26, 0xb6, 0x11, + 0x00, 0x00, 0x00, 0x00 +}; + +struct torture_suite *ndr_drsuapi_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "drsuapi"); + + torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsAddEntry, DsAddEntry_req1_dat, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsAddEntry, DsAddEntry_resp1_dat, NDR_OUT, NULL ); + + /*torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsBind, DsBind_req1_dat, NDR_IN, NULL );*/ + torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsBind, DsBind_resp1_dat, NDR_OUT, NULL ); + + /* torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsBind, DsBind_req2_dat, NDR_IN, NULL ); */ + torture_suite_add_ndr_pull_fn_test(suite, drsuapi_DsBind, DsBind_resp2_dat, NDR_OUT, NULL ); + + return suite; +} + diff --git a/source4/torture/ndr/epmap.c b/source4/torture/ndr/epmap.c new file mode 100644 index 0000000..ddc1e74 --- /dev/null +++ b/source4/torture/ndr/epmap.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + test suite for epmap ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_epmapper.h" +#include "torture/ndr/proto.h" + +static const uint8_t map_in_data[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, + 0x0d, 0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, + 0x23, 0x45, 0x67, 0x89, 0xac, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x0d, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, + 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x09, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 +}; + +static bool map_in_check(struct torture_context *tctx, + struct epm_Map *r) +{ + /* FIXME: Object */ + torture_assert_int_equal(tctx, r->in.max_towers, 1, "max towers"); + torture_assert(tctx, r->in.map_tower != NULL, "map tower"); + torture_assert_int_equal(tctx, r->in.map_tower->tower_length, 75, "tower len"); + /* FIXME: entry handle */ + + return true; +} + +#if 0 +static const uint8_t map_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x18, 0xc3, 0x47, 0xdd, 0xe6, 0x5a, 0x8b, 0x42, + 0xb3, 0xb7, 0xc7, 0x79, 0x7b, 0xf0, 0x45, 0xe0, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00 +}; + +static bool map_out_check(struct torture_context *tctx, + struct epm_Map *r) +{ + torture_assert_int_equal(tctx, *r->out.num_towers, 1, "num towers"); + torture_assert_int_equal(tctx, r->out.result, 0x4b, "return code"); + /* FIXME: entry handle */ + + return true; +} +#endif + +struct torture_suite *ndr_epmap_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "epmap"); + + torture_suite_add_ndr_pull_fn_test(suite, epm_Map, map_in_data, NDR_IN, map_in_check ); + /* torture_suite_add_ndr_pull_fn_test(suite, epm_Map, map_out_data, NDR_OUT, map_out_check ); */ + + return suite; +} + diff --git a/source4/torture/ndr/krb5pac.c b/source4/torture/ndr/krb5pac.c new file mode 100644 index 0000000..fe0309f --- /dev/null +++ b/source4/torture/ndr/krb5pac.c @@ -0,0 +1,705 @@ +/* + Unix SMB/CIFS implementation. + test suite for PAC ndr operations + + Copyright (C) Guenther Deschner 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "torture/ndr/proto.h" +#include "lib/krb5_wrap/krb5_samba.h" + +static const uint8_t PAC_DATA_data[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xb2, 0x98, 0xae, 0xb6, 0x70, 0xd8, 0xcd, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xb2, 0xd0, 0x46, 0x15, 0x4c, 0xce, 0xcd, 0x01, 0xb2, 0xd0, 0x46, 0x15, + 0x4c, 0xce, 0xcd, 0x01, 0xb2, 0x50, 0xa0, 0x0a, 0x4d, 0xef, 0xcd, 0x01, + 0x1a, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x9e, 0x03, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, 0x20, 0x00, 0x02, 0x00, + 0x0e, 0x00, 0x10, 0x00, 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x44, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, + 0x52, 0x00, 0x32, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, + 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x58, 0xcd, 0x06, 0x06, 0xed, 0x40, 0x2a, 0x76, 0x83, 0xc8, 0xe3, 0x99, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x20, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x58, 0xcd, 0x06, 0x06, 0xed, 0x40, 0x2a, 0x76, + 0x83, 0xc8, 0xe3, 0x99, 0x3c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x56, 0x9d, 0x59, 0x71, 0xd8, 0xcd, 0x01, 0x1a, 0x00, 0x61, 0x00, + 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x10, 0x00, 0x2c, 0x00, 0x58, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x64, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x40, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x38, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x38, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x2e, 0x00, + 0x42, 0x00, 0x45, 0x00, 0x52, 0x00, 0x2e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x44, 0x00, 0x48, 0x00, 0x41, 0x00, 0x54, 0x00, 0x2e, 0x00, 0x43, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, + 0x1a, 0x46, 0xc3, 0x88, 0x72, 0x36, 0xa4, 0x0f, 0x60, 0x0e, 0xed, 0x03, + 0xc8, 0xa6, 0x1a, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, + 0xd6, 0x4f, 0xa7, 0xac, 0x53, 0x73, 0x9b, 0x5c, 0xdf, 0xb1, 0xdf, 0xa6, + 0xdd, 0x84, 0x8e, 0xfb, 0x00, 0x00, 0x00, 0x00 +}; + +static bool PAC_DATA_check(struct torture_context *tctx, + struct PAC_DATA *r) +{ + int i; + + torture_assert_int_equal(tctx, r->num_buffers, 5, "num_buffers"); + + for (i=0; i < r->num_buffers; i++) { + switch (r->buffers[i].type) { + case PAC_TYPE_UPN_DNS_INFO: + torture_assert_int_equal(tctx, + r->buffers[i].info->upn_dns_info.upn_name_size, + 2*strlen_m("Administrator@w2k8dom.ber.redhat.com"), + "upn_name_size"); + torture_assert_str_equal(tctx, + r->buffers[i].info->upn_dns_info.upn_name, + "Administrator@w2k8dom.ber.redhat.com", + "upn_name"); + torture_assert_int_equal(tctx, + r->buffers[i].info->upn_dns_info.dns_domain_name_size, + 2*strlen_m("W2K8DOM.BER.REDHAT.COM"), + "dns_domain_name_size"); + torture_assert_str_equal(tctx, + r->buffers[i].info->upn_dns_info.dns_domain_name, + "W2K8DOM.BER.REDHAT.COM", + "dns_domain_name"); + break; + default: + continue; + } + } + + return true; +} + +static const uint8_t PAC_DATA_data2[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x98, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x4f, 0xb3, 0xf4, 0xa3, 0x8d, 0x00, 0xce, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xd2, 0x34, 0x6e, 0x6b, 0xfe, 0xcd, 0x01, 0xff, 0x92, 0x9e, 0x98, + 0x34, 0xff, 0xcd, 0x01, 0xff, 0x52, 0x8e, 0x63, 0x6c, 0x1f, 0xce, 0x01, + 0x1a, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x20, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x12, 0x00, 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, + 0x34, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x02, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x44, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x3a, 0x89, 0x96, 0x1a, 0x4d, 0x4c, 0x08, 0xc4, 0xe5, 0x87, 0x18, 0x44, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x3a, 0x89, 0x96, 0x1a, + 0x4d, 0x4c, 0x08, 0xc4, 0xe5, 0x87, 0x18, 0x44, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x70, 0xe2, 0xbf, 0x8d, 0x00, 0xce, 0x01, 0x1a, 0x00, 0x61, 0x00, + 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x60, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x64, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x40, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x32, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x2e, 0x00, 0x42, 0x00, + 0x45, 0x00, 0x52, 0x00, 0x2e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x48, 0x00, 0x41, 0x00, 0x54, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x4f, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xa9, 0x20, 0x93, 0x4b, + 0x8e, 0xc6, 0x88, 0x88, 0x7a, 0xd6, 0x12, 0xc6, 0xf3, 0x6f, 0x98, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xd5, 0xfe, 0x5a, 0x1e, + 0x73, 0xa4, 0x22, 0x64, 0x48, 0x72, 0x2d, 0xd8, 0x0f, 0xef, 0xe5, 0x81, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool PAC_DATA_check2(struct torture_context *tctx, + struct PAC_DATA *r) +{ + int i; + + torture_assert_int_equal(tctx, r->num_buffers, 5, "num_buffers"); + + for (i=0; i < r->num_buffers; i++) { + switch (r->buffers[i].type) { + case PAC_TYPE_UPN_DNS_INFO: + torture_assert_int_equal(tctx, + r->buffers[i].info->upn_dns_info.upn_name_size, + 2*strlen_m("Administrator@w2k12dom.ber.redhat.com"), + "upn_name_size"); + torture_assert_str_equal(tctx, + r->buffers[i].info->upn_dns_info.upn_name, + "Administrator@w2k12dom.ber.redhat.com", + "upn_name"); + torture_assert_int_equal(tctx, + r->buffers[i].info->upn_dns_info.dns_domain_name_size, + 2*strlen_m("W2K12DOM.BER.REDHAT.COM"), + "dns_domain_name_size"); + torture_assert_str_equal(tctx, + r->buffers[i].info->upn_dns_info.dns_domain_name, + "W2K12DOM.BER.REDHAT.COM", + "dns_domain_name"); + break; + default: + continue; + } + } + + return true; +} + +/* Thanks to Tris Mabbs for this sample. */ + +static const uint8_t PAC_DATA_data3[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xb0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xac, 0x03, 0xe5, 0x76, + 0xaa, 0x13, 0xce, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0x9d, 0x37, 0xcc, 0x87, 0x4d, 0x13, 0xce, 0x01, 0x9d, 0xf7, 0x35, 0xb2, + 0x16, 0x14, 0xce, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x06, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x24, 0x00, 0x24, 0x00, 0x10, 0x00, 0x02, 0x00, 0x22, 0x00, 0x22, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x18, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x5b, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x20, 0x00, 0x02, 0x00, 0x14, 0x00, 0x16, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x47, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x5c, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x73, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x47, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x3a, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6d, 0x04, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x50, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x6b, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x08, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x53, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9d, 0x04, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7a, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5d, 0x04, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x45, 0x00, 0x44, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x46, 0x00, 0x49, 0x00, + 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x47, 0x00, 0x52, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x7d, 0xbc, 0x30, 0x51, 0xd8, 0x07, 0x05, 0x60, 0x40, 0x59, 0x47, 0xb5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0xde, 0x76, 0xaa, 0x13, 0xce, 0x01, 0x06, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x7a, 0x00, + 0x28, 0x00, 0x10, 0x00, 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x40, 0x00, 0x46, 0x00, 0x69, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x67, 0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x2e, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x46, 0x00, 0x49, 0x00, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x47, 0x00, 0x52, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x2e, 0x00, 0x43, 0x00, + 0x4f, 0x00, 0x2e, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x76, 0xff, 0xff, 0xff, 0xff, 0x37, 0xcd, 0x48, + 0x1b, 0x6f, 0x9f, 0xca, 0x37, 0xe1, 0x02, 0x1c, 0xaa, 0x46, 0x0a, 0xf4, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xff, 0xff, 0xff, 0x3b, 0x96, 0xcc, 0xbb, 0xbb, 0x9d, 0xe4, 0x57, 0x13, 0xc9, 0x6d, 0x1c, + 0x65, 0xa0, 0xb1, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t PAC_DATA_pkinit_AS[] = { + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xd8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x48, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xa2, 0xd4, 0x65, 0xa4, + 0x59, 0x48, 0xd1, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0x70, 0x0b, 0xe4, 0x66, 0x97, 0x47, 0xd1, 0x01, 0x70, 0xcb, 0x4d, 0x91, + 0x60, 0x48, 0xd1, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x0e, 0x00, 0x0e, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x50, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x18, 0x00, 0x20, 0x00, 0x02, 0x00, 0x12, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x00, 0x4b, 0x00, + 0x20, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, + 0x32, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x33, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x34, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x55, 0x93, 0x92, 0x10, + 0xf4, 0xb0, 0xa6, 0xca, 0x96, 0x47, 0x97, 0x56, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xdc, 0xb1, 0xe1, 0x35, 0x5f, 0x4d, 0xa7, 0xef, 0x84, 0x86, 0x04, 0x42, 0x9f, 0x4f, 0x5e, 0x5b, + 0x22, 0x14, 0x66, 0x04, 0xc4, 0xf4, 0x18, 0x8d, 0x53, 0x30, 0xbe, 0x8c, 0xf4, 0xf7, 0x50, 0x0a, + 0x87, 0xef, 0x73, 0x3d, 0xd7, 0x7a, 0x0b, 0xf3, 0xd7, 0x30, 0x57, 0xb7, 0x1a, 0x1b, 0x8a, 0x35, + 0xc2, 0xde, 0x5b, 0xed, 0x4a, 0x25, 0xad, 0xc5, 0x15, 0x1b, 0xfc, 0xaf, 0x00, 0xb5, 0x7a, 0xea, + 0xda, 0xad, 0x77, 0x75, 0xf0, 0xf6, 0x17, 0x46, 0xf3, 0x5f, 0x7e, 0x89, 0x0c, 0xb3, 0x70, 0x31, + 0x09, 0x23, 0x16, 0x00, 0x9a, 0xf4, 0x03, 0x5f, 0xd4, 0xab, 0x3b, 0x6a, 0xc2, 0x7d, 0xb3, 0x8a, + 0x61, 0x8d, 0x15, 0xfb, 0x43, 0x38, 0x3d, 0x3b, 0x77, 0xda, 0xf4, 0x66, 0x0c, 0x0d, 0x36, 0xf5, + 0xc7, 0x01, 0xf9, 0xfb, 0xa4, 0xf8, 0x1f, 0xb8, 0x55, 0x65, 0x7a, 0xc2, 0xf3, 0x23, 0x8f, 0x9b, + 0x1e, 0xf1, 0xb8, 0x56, 0x70, 0x01, 0x75, 0xb4, 0x7d, 0xcb, 0x04, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x09, 0x4e, 0x6f, 0x33, 0x49, 0xd1, 0x01, 0x0e, 0x00, 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x40, 0x00, 0x77, 0x00, 0x34, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2d, 0x00, 0x6c, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x34, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x42, 0x00, 0x41, 0x00, + 0x53, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc2, 0x78, 0x2b, 0x92, + 0x2c, 0x06, 0x36, 0x32, 0xb0, 0x72, 0x75, 0x9a, 0x76, 0xff, 0xff, 0xff, 0x9c, 0x73, 0x1e, 0xb8, + 0x3a, 0xd8, 0xca, 0x01, 0x53, 0x60, 0xd4, 0x1d, 0x1a, 0x69, 0xde, 0x38, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t PAC_DATA_pkinit_TGS[] = { + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xd8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x48, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xa2, 0xd4, 0x65, 0xa4, + 0x59, 0x48, 0xd1, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0x70, 0x0b, 0xe4, 0x66, 0x97, 0x47, 0xd1, 0x01, 0x70, 0xcb, 0x4d, 0x91, + 0x60, 0x48, 0xd1, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x0e, 0x00, 0x0e, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x50, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x18, 0x00, 0x20, 0x00, 0x02, 0x00, 0x12, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x00, 0x4b, 0x00, + 0x20, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, + 0x32, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x33, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x34, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x55, 0x93, 0x92, 0x10, + 0xf4, 0xb0, 0xa6, 0xca, 0x96, 0x47, 0x97, 0x56, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xdc, 0xb1, 0xe1, 0x35, 0x5f, 0x4d, 0xa7, 0xef, 0x84, 0x86, 0x04, 0x42, 0x9f, 0x4f, 0x5e, 0x5b, + 0x22, 0x14, 0x66, 0x04, 0xc4, 0xf4, 0x18, 0x8d, 0x53, 0x30, 0xbe, 0x8c, 0xf4, 0xf7, 0x50, 0x0a, + 0x87, 0xef, 0x73, 0x3d, 0xd7, 0x7a, 0x0b, 0xf3, 0xd7, 0x30, 0x57, 0xb7, 0x1a, 0x1b, 0x8a, 0x35, + 0xc2, 0xde, 0x5b, 0xed, 0x4a, 0x25, 0xad, 0xc5, 0x15, 0x1b, 0xfc, 0xaf, 0x00, 0xb5, 0x7a, 0xea, + 0xda, 0xad, 0x77, 0x75, 0xf0, 0xf6, 0x17, 0x46, 0xf3, 0x5f, 0x7e, 0x89, 0x0c, 0xb3, 0x70, 0x31, + 0x09, 0x23, 0x16, 0x00, 0x9a, 0xf4, 0x03, 0x5f, 0xd4, 0xab, 0x3b, 0x6a, 0xc2, 0x7d, 0xb3, 0x8a, + 0x61, 0x8d, 0x15, 0xfb, 0x43, 0x38, 0x3d, 0x3b, 0x77, 0xda, 0xf4, 0x66, 0x0c, 0x0d, 0x36, 0xf5, + 0xc7, 0x01, 0xf9, 0xfb, 0xa4, 0xf8, 0x1f, 0xb8, 0x55, 0x65, 0x7a, 0xc2, 0xf3, 0x23, 0x8f, 0x9b, + 0x1e, 0xf1, 0xb8, 0x56, 0x70, 0x01, 0x75, 0xb4, 0x7d, 0xcb, 0x04, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x09, 0x4e, 0x6f, 0x33, 0x49, 0xd1, 0x01, 0x0e, 0x00, 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x31, 0x00, 0x40, 0x00, 0x77, 0x00, 0x34, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2d, 0x00, 0x6c, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x34, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x42, 0x00, 0x41, 0x00, + 0x53, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x82, 0xcd, 0xb1, 0x67, + 0xaa, 0x7c, 0xca, 0xa5, 0x0c, 0xf0, 0xbe, 0x75, 0x76, 0xff, 0xff, 0xff, 0x8b, 0x4e, 0xb0, 0x67, + 0x3c, 0x17, 0xe6, 0x05, 0x90, 0x66, 0x20, 0x45, 0x34, 0x2f, 0x32, 0x9b, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t PAC_DATA_pkinit_PAC_CREDENTIAL_DATA_NDR[] = { + 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x4c, 0x00, 0x4d, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x3e, 0x95, 0x63, + 0x88, 0x8b, 0x12, 0x02, 0x9f, 0x6e, 0x2b, 0x8d, 0xf6, 0x9a, 0x6e, 0xb3, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x3e, 0x95, 0x63, 0x88, 0x8b, 0x12, 0x02, + 0x9f, 0x6e, 0x2b, 0x8d, 0xf6, 0x9a, 0x6e, 0xb3, +}; + +static bool PAC_DATA_pkinit(struct torture_context *tctx, + struct PAC_DATA *r) +{ + DATA_BLOB reply_key_blob = data_blob_null; + krb5_context ctx; + krb5_keyblock reply_key; + krb5_enc_data input; + krb5_data plain_data; + DATA_BLOB plain_data_blob = data_blob_null; + + torture_assert_int_equal(tctx, r->version, 0, "version"); + + torture_assert_int_equal(tctx, r->num_buffers, 6, "num_buffers"); + + torture_assert_int_equal(tctx, r->buffers[0].type, PAC_TYPE_LOGON_INFO, "PAC_TYPE_LOGON_INFO"); + torture_assert_int_equal(tctx, r->buffers[0]._ndr_size, 448, "PAC_TYPE_LOGON_INFO _ndr_size"); + torture_assert(tctx, r->buffers[0].info != NULL, "PAC_TYPE_LOGON_INFO info"); + + torture_assert_int_equal(tctx, r->buffers[1].type, PAC_TYPE_CREDENTIAL_INFO, "PAC_TYPE_CREDENTIAL_INFO"); + torture_assert_int_equal(tctx, r->buffers[1]._ndr_size, 148, "PAC_TYPE_CREDENTIALS_INFO _ndr_size"); + torture_assert(tctx, r->buffers[1].info != NULL, "PAC_TYPE_CREDENTIALS_INFO info"); + torture_assert_int_equal(tctx, + r->buffers[1].info->credential_info.version, + 0, + "PAC_TYPE_CREDENTIALS_INFO version"); + torture_assert_int_equal(tctx, + r->buffers[1].info->credential_info.encryption_type, + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + "PAC_TYPE_CREDENTIALS_INFO encryption_type"); + torture_assert_data_blob_equal(tctx, + r->buffers[1].info->credential_info.encrypted_data, + data_blob_const(PAC_DATA_pkinit_AS+0x230, 140), + "PAC_TYPE_CREDENTIALS_INFO encrypted_data"); + + /* + * This is the PKINIT based reply key. + */ + reply_key_blob = strhex_to_data_blob(tctx, + "c9deb5412b3fba34250b0e4b1f3b6cba3d70bdcdac0f097a9b6a7c763a5524ed"); + torture_assert_int_equal(tctx, reply_key_blob.length, 32, "reply_key_blob.length"); + torture_assert_int_equal(tctx, krb5_init_context(&ctx), 0, "krb5_init_context"); + torture_assert_int_equal(tctx, smb_krb5_keyblock_init_contents(ctx, + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + reply_key_blob.data, reply_key_blob.length, + &reply_key), 0, + "smb_krb5_keyblock_init_contents"); + + ZERO_STRUCT(input); + + input.ciphertext.data = (char *)r->buffers[1].info->credential_info.encrypted_data.data; + input.ciphertext.length = r->buffers[1].info->credential_info.encrypted_data.length; + input.enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96; + + plain_data.data = malloc(r->buffers[1].info->credential_info.encrypted_data.length); + plain_data.length = r->buffers[1].info->credential_info.encrypted_data.length; + torture_assert(tctx, plain_data.data, "malloc failed"); + + torture_assert_krb5_error_equal(tctx, krb5_c_decrypt(ctx, +#ifdef SAMBA4_USES_HEIMDAL + reply_key, +#else + &reply_key, +#endif + KRB5_KU_OTHER_ENCRYPTED, + NULL, + &input, + &plain_data), 0, + "krb5_decrypt"); + + torture_assert_int_equal(tctx, plain_data.length, 112, "plain_data.length"); + plain_data_blob = data_blob_talloc(tctx, plain_data.data, plain_data.length); + torture_assert_int_equal(tctx, plain_data_blob.length, 112, "plain_data_blob.length"); + smb_krb5_free_data_contents(ctx, &plain_data); + krb5_free_keyblock_contents(ctx, &reply_key); + krb5_free_context(ctx); + torture_assert_data_blob_equal(tctx, + plain_data_blob, + data_blob_const(PAC_DATA_pkinit_PAC_CREDENTIAL_DATA_NDR, + sizeof(PAC_DATA_pkinit_PAC_CREDENTIAL_DATA_NDR)), + "PAC_CREDENTIALS_DATA_NDR plain_data"); + + torture_assert_int_equal(tctx, r->buffers[2].type, PAC_TYPE_LOGON_NAME, "PAC_TYPE_LOGON_NAME"); + torture_assert_int_equal(tctx, r->buffers[2]._ndr_size, 24, "PAC_TYPE_LOGON_NAME _ndr_size"); + torture_assert(tctx, r->buffers[2].info != NULL, "PAC_TYPE_LOGON_NAME info"); + torture_assert_int_equal(tctx, + r->buffers[2].info->logon_name.size, + 2*strlen_m("pkinit1"), + "size"); + torture_assert_str_equal(tctx, + r->buffers[2].info->logon_name.account_name, + "pkinit1", + "account_name"); + torture_assert_u64_equal(tctx, + r->buffers[2].info->logon_name.logon_time, + 130966349430000000ULL, + "logon_time"); + + torture_assert_int_equal(tctx, r->buffers[3].type, PAC_TYPE_UPN_DNS_INFO, "PAC_TYPE_UPN_DNS_INFO"); + torture_assert_int_equal(tctx, r->buffers[3]._ndr_size, 96, "PAC_TYPE_UPN_DNS_INFO _ndr_size"); + torture_assert(tctx, r->buffers[3].info != NULL, "PAC_TYPE_UPN_DNS_INFO info"); + torture_assert_int_equal(tctx, + r->buffers[3].info->upn_dns_info.upn_name_size, + 2*strlen_m("pkinit1@w4edom-l4.base"), + "upn_name_size"); + torture_assert_str_equal(tctx, + r->buffers[3].info->upn_dns_info.upn_name, + "pkinit1@w4edom-l4.base", + "upn_name"); + torture_assert_int_equal(tctx, + r->buffers[3].info->upn_dns_info.dns_domain_name_size, + 2*strlen_m("W4EDOM-L4.BASE"), + "dns_domain_name_size"); + torture_assert_str_equal(tctx, + r->buffers[3].info->upn_dns_info.dns_domain_name, + "W4EDOM-L4.BASE", + "dns_domain_name"); + + torture_assert_int_equal(tctx, r->buffers[4].type, PAC_TYPE_SRV_CHECKSUM, "PAC_TYPE_SRV_CHECKSUM"); + torture_assert_int_equal(tctx, r->buffers[4]._ndr_size, 16, "PAC_TYPE_SRV_CHECKSUM _ndr_size"); + torture_assert(tctx, r->buffers[4].info != NULL, "PAC_TYPE_SRV_CHECKSUM info"); + torture_assert_int_equal(tctx, + r->buffers[4].info->srv_cksum.type, + CKSUMTYPE_HMAC_SHA1_96_AES_256, + "srv_cksum"); + torture_assert_int_equal(tctx, + r->buffers[4].info->srv_cksum.signature.length, + 12, + "PAC_TYPE_SRV_CHECKSUM signature.length"); + + torture_assert_int_equal(tctx, r->buffers[5].type, PAC_TYPE_KDC_CHECKSUM, "PAC_TYPE_KDC_CHECKSUM"); + torture_assert_int_equal(tctx, r->buffers[5]._ndr_size, 20, "PAC_TYPE_KDC_CHECKSUM _ndr_size"); + torture_assert(tctx, r->buffers[5].info != NULL, "PAC_TYPE_KDC_CHECKSUM info"); + torture_assert_int_equal(tctx, + r->buffers[5].info->kdc_cksum.type, + CKSUMTYPE_HMAC_MD5, + "kdc_cksum"); + torture_assert_int_equal(tctx, + r->buffers[5].info->kdc_cksum.signature.length, + 16, + "PAC_TYPE_KDC_CHECKSUM signature.length"); + + return true; +} + +static bool PAC_CREDENTIAL_DATA_NDR_check(struct torture_context *tctx, + struct PAC_CREDENTIAL_DATA_NDR *r) +{ + torture_assert(tctx, r->ctr.data != NULL, "data"); + + torture_assert_int_equal(tctx, r->ctr.data->credential_count, 1, "credential_count"); + torture_assert_int_equal(tctx, + r->ctr.data->credentials[0].package_name.size, + 2*strlen_m("NTLM"), + "package_name.size"); + torture_assert_int_equal(tctx, + r->ctr.data->credentials[0].package_name.length, + 2*strlen_m("NTLM"), + "package_name.length"); + torture_assert_str_equal(tctx, + r->ctr.data->credentials[0].package_name.string, + "NTLM", + "package_name.string"); + torture_assert_int_equal(tctx, + r->ctr.data->credentials[0].credential_size, + sizeof(PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG), + "credential_size"); + torture_assert_data_blob_equal(tctx, + data_blob_const(r->ctr.data->credentials[0].credential, + r->ctr.data->credentials[0].credential_size), + data_blob_const(PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG, + sizeof(PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG)), + "PAC_CREDENTIAL_NTLM_SECPKG credential"); + + return true; +} + +static bool PAC_CREDENTIAL_NTLM_SECPKG_check(struct torture_context *tctx, + struct PAC_CREDENTIAL_NTLM_SECPKG *r) +{ + torture_assert_int_equal(tctx, r->version, 0, "version"); + + torture_assert_int_equal(tctx, + r->flags, + PAC_CREDENTIAL_NTLM_HAS_NT_HASH, + "flags"); + + torture_assert_data_blob_equal(tctx, + data_blob_const(r->lm_password.hash, + sizeof(r->lm_password.hash)), + data_blob_const(PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG+0x08, + 16), + "lm_password"); + torture_assert_data_blob_equal(tctx, + data_blob_const(r->nt_password.hash, + sizeof(r->nt_password.hash)), + data_blob_const(PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG+0x18, + 16), + "nt_password"); + + return true; +} + +struct torture_suite *ndr_krb5pac_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "krb5pac"); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA_RAW, + PAC_DATA_data, + NULL); + /* + * We can't use torture_suite_add_ndr_pull_validate_test() + * here with PAC_DATA, as we don't match the unique + * pointer values inside PAC_LOGON_INFO, for these + * case where we have S-1-5-18-1, as extra sid. + */ + torture_suite_add_ndr_pull_test(suite, + PAC_DATA, + PAC_DATA_data, + PAC_DATA_check); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA_RAW, + PAC_DATA_data2, + NULL); + /* + * We can't use torture_suite_add_ndr_pull_validate_test() + * here with PAC_DATA, as we don't match the unique + * pointer values inside PAC_LOGON_INFO, for these + * case where we have S-1-5-18-1, as extra sid. + */ + torture_suite_add_ndr_pull_test(suite, + PAC_DATA, + PAC_DATA_data2, + PAC_DATA_check2); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA_RAW, + PAC_DATA_data3, + NULL); + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA, + PAC_DATA_data3, + NULL); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA_RAW, + PAC_DATA_pkinit_AS, + NULL); + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA, + PAC_DATA_pkinit_AS, + PAC_DATA_pkinit); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA_RAW, + PAC_DATA_pkinit_TGS, + NULL); + torture_suite_add_ndr_pull_validate_test(suite, + PAC_DATA, + PAC_DATA_pkinit_AS, + PAC_DATA_pkinit); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_CREDENTIAL_DATA_NDR, + PAC_DATA_pkinit_PAC_CREDENTIAL_DATA_NDR, + PAC_CREDENTIAL_DATA_NDR_check); + + torture_suite_add_ndr_pull_validate_test(suite, + PAC_CREDENTIAL_NTLM_SECPKG, + PAC_DATA_pkinit_PAC_CREDENTIAL_NTLM_SECPKG, + PAC_CREDENTIAL_NTLM_SECPKG_check); + + return suite; +} diff --git a/source4/torture/ndr/lsa.c b/source4/torture/ndr/lsa.c new file mode 100644 index 0000000..2f8f47c --- /dev/null +++ b/source4/torture/ndr/lsa.c @@ -0,0 +1,2229 @@ +/* + Unix SMB/CIFS implementation. + test suite for atsvc ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "torture/ndr/proto.h" + +static const uint8_t lsarlookupnames_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x64, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x47, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x4f, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x50, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x53, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x59, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x62, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames_in_check(struct torture_context *tctx, + struct lsa_LookupNames *r) +{ + int i; + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.num_names, 100, "num names"); + for (i = 0; i < 100; i++) { + torture_assert_str_equal(tctx, r->in.names[i].string, "Users", "names"); + } + torture_assert(tctx, r->in.sids != NULL, "sids"); + torture_assert_int_equal(tctx, r->in.sids->count, 0, "sids count"); + torture_assert(tctx, r->in.sids->sids == NULL, "sids domains"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert(tctx, r->in.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->in.count, 0, "count"); + return true; +} + +static const uint8_t lsarlookupnames_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x72, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x69, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x55, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x35, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x57, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x45, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x43, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x73, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x76, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x65, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xb4, 0xfc, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd8, 0x9f, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x42, 0x48, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0xbf, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf4, 0x98, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc4, 0x18, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x94, 0xb3, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x7f, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x9b, 0x92, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf7, 0x59, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x75, 0xa3, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x37, 0xeb, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x98, 0xbe, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5a, 0x7e, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x7c, 0xae, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xcb, 0x87, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1c, 0x77, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x45, 0xa9, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6f, 0xbf, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x86, 0x6d, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xbc, 0x61, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x18, 0xa7, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5f, 0xa8, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x44, 0x3c, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x4e, 0x0d, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xcf, 0xb0, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd1, 0x87, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x48, 0xc1, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa8, 0xb3, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x52, 0xf4, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x3e, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xb6, 0x7f, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xee, 0x43, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc1, 0x85, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8a, 0x80, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xe1, 0x1e, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5b, 0xdf, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x98, 0xe0, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x85, 0x8b, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc9, 0xb4, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x60, 0xba, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x9b, 0x95, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0f, 0x10, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc9, 0xed, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x72, 0xa6, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xbe, 0x11, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x37, 0xcd, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc3, 0x40, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x51, 0x12, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd3, 0x25, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x4f, 0x72, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x33, 0xd7, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xfb, 0x70, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xdc, 0xd2, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1f, 0xeb, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd0, 0x98, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x87, 0x14, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xc6, 0xb5, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x94, 0x74, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5a, 0x50, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x5d, 0x43, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x34, 0x6a, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6d, 0x30, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x01, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf7, 0x37, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0x90, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x91, 0xf6, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8e, 0x0d, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x54, 0x87, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xb2, 0x14, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf8, 0x8d, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x25, 0x23, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x2c, 0x2f, 0x21, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames_out_check(struct torture_context *tctx, + struct lsa_LookupNames *r) +{ + struct lsa_RefDomainList *domains = *(r->out.domains); + torture_assert(tctx, r->out.domains != NULL, "domains ptr"); + torture_assert_int_equal(tctx, domains->count, 1, "domains count"); + torture_assert_int_equal(tctx, domains->max_size, 32, "domains size"); + torture_assert(tctx, domains->domains != NULL, "domains domains"); + torture_assert_str_equal(tctx, domains->domains[0].name.string, "BUILTIN", "domain name"); + /* FIXME: SID */ + torture_assert(tctx, r->out.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->out.count, 100, "count"); + + torture_assert_int_equal(tctx, r->out.sids->count, 100, "sids count"); + torture_assert_int_equal(tctx, r->out.sids->sids[0].sid_type, 4, "sid type"); + torture_assert_int_equal(tctx, r->out.sids->sids[0].rid, 0x221, "sid rid"); + torture_assert_int_equal(tctx, r->out.sids->sids[0].sid_index, 0, "sid index"); + return true; +} + +static const uint8_t lsarlookupsids_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x64, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupsids_in_check(struct torture_context *tctx, + struct lsa_LookupSids *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.sids->num_sids, 100, "num sids"); + torture_assert(tctx, r->in.sids->sids != NULL, "sids sids"); + torture_assert_int_equal(tctx, r->in.names->count, 0, "names count"); + torture_assert(tctx, r->in.names->names == NULL, "names names"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert(tctx, r->in.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->in.count, 0, "count"); + + return true; +} + +static const uint8_t lsarlookupsids_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x2c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x40, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x44, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x48, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x4c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x54, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x58, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x5c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x60, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x68, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x7c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x84, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x88, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x8c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x90, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x94, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x9c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xa4, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xa8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xac, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xb0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xde, 0x36, + 0x0a, 0x00, 0x0a, 0x00, 0xb4, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xb8, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xbc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xc0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xc4, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xc8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xd0, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xd4, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xd8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xdc, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xe0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xe4, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xe8, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xec, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xf4, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x04, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0c, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x10, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x14, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x18, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x1c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x24, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x28, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x2c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x30, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x34, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x38, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x3c, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x44, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x48, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x4c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x50, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x54, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x58, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x5c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x60, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x64, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x68, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x6c, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x70, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x74, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x7c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x80, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x84, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x88, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x8c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x90, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0x94, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x98, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x9c, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, + 0xa0, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupsids_out_check(struct torture_context *tctx, + struct lsa_LookupSids *r) +{ + struct lsa_RefDomainList *domains = *(r->out.domains); + torture_assert(tctx, domains != NULL, "domains"); + torture_assert_int_equal(tctx, domains->count, 1, "domains count"); + torture_assert_int_equal(tctx, domains->max_size, 32, "domains size"); + torture_assert(tctx, domains->domains != NULL, "domains domains"); + torture_assert_str_equal(tctx, domains->domains[0].name.string, "BUILTIN", "name"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsaropenpolicy2_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsaropenpolicy2_in_check(struct torture_context *tctx, + struct lsa_OpenPolicy2 *r) +{ + torture_assert_str_equal(tctx, r->in.system_name, "\\", "system name"); + torture_assert(tctx, r->in.attr != NULL, "attr ptr"); + torture_assert_int_equal(tctx, r->in.attr->len, 0, "attr len"); + torture_assert(tctx, r->in.attr->root_dir == NULL, "attr root"); + torture_assert(tctx, r->in.attr->object_name == NULL, "attr object name"); + torture_assert_int_equal(tctx, r->in.attr->attributes, 0, "attr attributes"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x02000000, "access mask"); + return true; +} + +static const uint8_t lsaropenpolicy2_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsaropenpolicy2_out_check(struct torture_context *tctx, + struct lsa_OpenPolicy2 *r) +{ + /* FIXME: handle */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsaropenpolicy_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsaropenpolicy_in_check(struct torture_context *tctx, + struct lsa_OpenPolicy *r) +{ + torture_assert(tctx, r->in.system_name != NULL, "system name"); + torture_assert(tctx, r->in.attr != NULL, "attr ptr"); + torture_assert_int_equal(tctx, r->in.attr->len, 0, "attr len"); + torture_assert(tctx, r->in.attr->root_dir == NULL, "attr root"); + torture_assert(tctx, r->in.attr->object_name == NULL, "attr object name"); + torture_assert_int_equal(tctx, r->in.attr->attributes, 0, "attr attributes"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x02000000, "access mask"); + + return true; +} + +static const uint8_t lsaropenpolicy_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xbd, 0xf1, 0xb1, 0xe8, 0xd7, 0xf8, 0x43, + 0xae, 0xb4, 0x5f, 0x9b, 0xbe, 0x06, 0xf2, 0xce, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsaropenpolicy_out_check(struct torture_context *tctx, + struct lsa_OpenPolicy *r) +{ + /* FIXME: Handle */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarcreateaccount_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xb4, 0x71, 0xbc, 0x00, + 0xe1, 0x10, 0x00, 0x00, 0x26, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsarcreateaccount_in_check(struct torture_context *tctx, + struct lsa_CreateAccount *r) +{ + /* FIXME: Handle */ + /* FIXME: Sid */ + torture_assert_int_equal(tctx, r->in.access_mask, 0x2000000, "access mask"); + return true; +} + +static const uint8_t lsarcreateaccount_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x28, 0x64, 0xd8, 0x9a, 0xad, 0x2f, 0x48, + 0xa5, 0x37, 0x26, 0xb4, 0x17, 0x71, 0x3a, 0xe8, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarcreateaccount_out_check(struct torture_context *tctx, + struct lsa_CreateAccount *r) +{ + /* FIXME */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsardelete_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x28, 0x64, 0xd8, 0x9a, 0xad, 0x2f, 0x48, + 0xa5, 0x37, 0x26, 0xb4, 0x17, 0x71, 0x3a, 0xe8 +}; + +static bool lsardelete_in_check(struct torture_context *tctx, + struct lsa_Delete *r) +{ + /* FIXME: Handle */ + return true; +} + +static const uint8_t lsardelete_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsardelete_out_check(struct torture_context *tctx, + struct lsa_Delete *r) +{ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarcreatesecret_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x2e, 0x00, 0x2e, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x35, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x38, 0x00, 0x35, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsarcreatesecret_in_check(struct torture_context *tctx, + struct lsa_CreateSecret *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.name.string, "torturesecret-852885356", "name"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x2000000, "access mask"); + return true; +} + +static const uint8_t lsarcreatesecret_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x2d, 0x02, 0x15, 0x3d, 0xfb, 0x27, 0x4c, + 0xaa, 0x22, 0x13, 0x79, 0x20, 0x14, 0x7f, 0xad, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarcreatesecret_out_check(struct torture_context *tctx, + struct lsa_CreateSecret *r) +{ + /* FIXME: Handle */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsaropensecret_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x2e, 0x00, 0x2e, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x35, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x38, 0x00, 0x35, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsaropensecret_in_check(struct torture_context *tctx, + struct lsa_OpenSecret *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.name.string, "torturesecret-852885356", "name"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x2000000, "access mask"); + return true; +} + +static const uint8_t lsaropensecret_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x9f, 0x6d, 0x07, 0x35, 0x08, 0x43, 0xd9, 0x4b, + 0xbb, 0xcf, 0xeb, 0x4a, 0x91, 0xd2, 0x24, 0xe7, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsaropensecret_out_check(struct torture_context *tctx, + struct lsa_OpenSecret *r) +{ + /* FIXME: Handle */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarsetsecret_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x2d, 0x02, 0x15, 0x3d, 0xfb, 0x27, 0x4c, + 0xaa, 0x22, 0x13, 0x79, 0x20, 0x14, 0x7f, 0xad, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0xda, 0xb8, 0x19, 0xb6, 0xaf, 0x8c, 0x0f, 0xf5, 0x28, 0x81, 0xca, 0xce, + 0xcc, 0x8b, 0x70, 0xc4, 0x8a, 0xe5, 0xad, 0x51, 0x1a, 0x0e, 0xb5, 0xaa, + 0x3b, 0xdc, 0xbf, 0x38, 0x30, 0xb4, 0x18, 0x6d, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarsetsecret_in_check(struct torture_context *tctx, + struct lsa_SetSecret *r) +{ + /* FIXME: Handle */ + torture_assert(tctx, r->in.new_val != NULL, "new val ptr"); + torture_assert(tctx, r->in.old_val == NULL, "old val ptr"); + torture_assert_int_equal(tctx, r->in.new_val->length, 32, "new val len"); + torture_assert_int_equal(tctx, r->in.new_val->size, 32, "new val size"); + return true; +} + + +static const uint8_t lsarsetsecret_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarsetsecret_out_check(struct torture_context *tctx, + struct lsa_SetSecret *r) +{ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarquerysecret_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x2d, 0x02, 0x15, 0x3d, 0xfb, 0x27, 0x4c, + 0xaa, 0x22, 0x13, 0x79, 0x20, 0x14, 0x7f, 0xad, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarquerysecret_in_check(struct torture_context *tctx, + struct lsa_QuerySecret *r) +{ + /* FIXME: Handle */ + torture_assert(tctx, r->in.new_val != NULL, "new val ptr"); + torture_assert(tctx, r->in.new_val->buf == NULL, "new val ptr ptr"); + torture_assert(tctx, r->in.new_mtime != NULL, "new mtime ptr"); + /* FIXME: *new_mtime */ + torture_assert(tctx, r->in.old_val == NULL, "old val ptr"); + torture_assert(tctx, r->in.old_mtime == NULL, "old mtime ptr"); + return true; +} + + +static const uint8_t lsarquerysecret_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xda, 0xb8, 0x19, 0xb6, + 0xaf, 0x8c, 0x0f, 0xf5, 0x28, 0x81, 0xca, 0xce, 0xcc, 0x8b, 0x70, 0xc4, + 0x8a, 0xe5, 0xad, 0x51, 0x1a, 0x0e, 0xb5, 0xaa, 0x3b, 0xdc, 0xbf, 0x38, + 0x30, 0xb4, 0x18, 0x6d, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x3e, 0x63, 0x7e, 0xee, 0xf1, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarquerysecret_out_check(struct torture_context *tctx, + struct lsa_QuerySecret *r) +{ + /* FIXME: Handle */ + torture_assert(tctx, r->out.new_val != NULL, "new val ptr"); + torture_assert(tctx, r->out.new_mtime != NULL, "new mtime ptr"); + /* FIXME: *new_mtime */ + torture_assert(tctx, r->out.old_val == NULL, "old val ptr"); + torture_assert(tctx, r->out.old_mtime == NULL, "old mtime ptr"); + return true; +} + +static const uint8_t lsarcreatetrusteddomain_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x1a, 0x00, 0x1a, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x76, 0x7c, 0x01, 0x00, 0x93, 0xcb, 0x05, 0x00, + 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool lsarcreatetrusteddomain_in_check(struct torture_context *tctx, + struct lsa_CreateTrustedDomain *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.info->name.string, "torturedomain", "name"); + torture_assert(tctx, r->in.info->sid != NULL, "sid"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x2000000, "access mask"); + return true; +} + +static const uint8_t lsarcreatetrusteddomain_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb5, 0x23, 0x36, 0x5f, 0x33, 0x92, 0x41, 0x4c, + 0x9a, 0x73, 0x7d, 0x6a, 0x23, 0x14, 0x62, 0x56, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarcreatetrusteddomain_out_check(struct torture_context *tctx, + struct lsa_CreateTrustedDomain *r) +{ + /* FIXME: Handle */ + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarenumerateaccounts_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00 +}; + +static bool lsarenumerateaccounts_in_check(struct torture_context *tctx, + struct lsa_EnumAccounts *r) +{ + /* FIXME: handle */ + torture_assert(tctx, r->in.resume_handle != NULL, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->in.resume_handle, 0, "resume handle"); + torture_assert_int_equal(tctx, r->in.num_entries, 100, "num entries"); + return true; +} + +static const uint8_t lsarenumerateaccounts_out_data[] = { + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x10, 0x00, 0x02, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x24, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, + 0xcc, 0x04, 0x9c, 0x4c, 0xe9, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarenumerateaccounts_out_check(struct torture_context *tctx, + struct lsa_EnumAccounts *r) +{ + torture_assert(tctx, r->out.resume_handle != NULL, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->out.resume_handle, 7, "resume handle"); + torture_assert_int_equal(tctx, r->out.sids->num_sids, 7, "num sids"); + torture_assert(tctx, r->out.sids->sids != NULL, "sids sids"); + torture_assert(tctx, r->out.sids->sids[0].sid != NULL, "sids sids"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarlookupsids2_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, + 0x07, 0x32, 0xfb, 0xb2, 0xcc, 0x04, 0x9c, 0x4c, 0xe9, 0x03, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupsids2_in_check(struct torture_context *tctx, + struct lsa_LookupSids2 *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.sids->num_sids, 7, "num sids"); + torture_assert(tctx, r->in.sids->sids != NULL, "sids sids"); + torture_assert(tctx, r->in.sids->sids[0].sid != NULL, "sids sids"); + torture_assert(tctx, r->in.names != NULL, "names ptr"); + torture_assert_int_equal(tctx, r->in.names->count, 0, "names count"); + torture_assert(tctx, r->in.names->names == NULL, "names"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert(tctx, r->in.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->in.count, 7, "count"); + torture_assert_int_equal(tctx, r->in.lookup_options, 0, "unknown 1"); + torture_assert_int_equal(tctx, r->in.client_revision, 0, "unknown 2"); + + return true; +} + +static const uint8_t lsarlookupsids2_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x1a, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x14, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x10, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x12, 0x00, 0x14, 0x00, + 0x20, 0x00, 0x02, 0x00, 0x24, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x55, 0x00, 0x54, 0x00, 0x48, 0x00, 0x4f, 0x00, + 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x33, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, + 0xcc, 0x04, 0x9c, 0x4c, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x22, 0x00, 0x22, 0x00, + 0x2c, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x34, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x20, 0x00, + 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x3c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x12, 0x00, + 0x26, 0x00, 0x28, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x44, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x63, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x53, 0x00, 0x55, 0x00, 0x50, 0x00, 0x50, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x33, 0x00, 0x38, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x34, 0x00, 0x35, 0x00, 0x61, 0x00, 0x30, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x45, 0x00, 0x54, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x4b, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, + 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x43, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, + 0x52, 0x00, 0x56, 0x00, 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x45, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupsids2_out_check(struct torture_context *tctx, + struct lsa_LookupSids2 *r) +{ + struct lsa_RefDomainList *domains = *(r->out.domains); + /* FIXME: Handle */ + torture_assert(tctx, r->out.names != NULL, "names ptr"); + torture_assert(tctx, r->out.domains != NULL, "domains ptr"); + torture_assert_int_equal(tctx, domains->count, 4, "domains count"); + torture_assert_int_equal(tctx, domains->max_size, 32, "domains size"); + torture_assert_str_equal(tctx, domains->domains[0].name.string, "NT AUTHORITY", "trust info name"); + torture_assert_int_equal(tctx, r->out.names->count, 7, "names count"); + torture_assert_str_equal(tctx, r->out.names->names[0].name.string, "Account Operators", "name str 1"); + torture_assert_str_equal(tctx, r->out.names->names[1].name.string, "Administrators", "name str 2"); + torture_assert_str_equal(tctx, r->out.names->names[2].name.string, "SUPPORT_388945a0", "name str 3"); + torture_assert(tctx, r->out.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->out.count, 7, "count"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t lsarlookupnames2_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x26, 0x00, 0x26, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x63, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x53, 0x00, 0x55, 0x00, 0x50, 0x00, 0x50, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x33, 0x00, 0x38, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x34, 0x00, 0x35, 0x00, 0x61, 0x00, 0x30, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x45, 0x00, 0x54, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x4b, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, + 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x43, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, + 0x52, 0x00, 0x56, 0x00, 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x45, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames2_in_check(struct torture_context *tctx, + struct lsa_LookupNames2 *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.num_names, 7, "num names"); + torture_assert_str_equal(tctx, r->in.names[0].string, "Account Operators", + "names[0]"); + torture_assert_str_equal(tctx, r->in.names[1].string, "Administrators", + "names[1]"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert_int_equal(tctx, r->in.lookup_options, 0, "lookup_options"); + torture_assert_int_equal(tctx, r->in.client_revision, 0, "client_revision"); + torture_assert_int_equal(tctx, *r->in.count, 0, "count"); + torture_assert_int_equal(tctx, r->in.sids->count, 0, "sids count"); + torture_assert(tctx, r->in.sids->sids == NULL, "sids sids"); + return true; +} + +static const uint8_t lsarlookupnames2_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x1a, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x14, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x10, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x12, 0x00, 0x14, 0x00, + 0x20, 0x00, 0x02, 0x00, 0x24, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x55, 0x00, 0x54, 0x00, 0x48, 0x00, 0x4f, 0x00, + 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x33, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, + 0xcc, 0x04, 0x9c, 0x4c, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xe9, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames2_out_check(struct torture_context *tctx, + struct lsa_LookupNames2 *r) +{ + torture_assert_int_equal(tctx, *r->out.count, 7, "count"); + torture_assert_int_equal(tctx, r->out.sids->count, 7, "sids count"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarlookupnames3_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x26, 0x00, 0x26, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x63, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x53, 0x00, 0x55, 0x00, 0x50, 0x00, 0x50, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x33, 0x00, 0x38, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x34, 0x00, 0x35, 0x00, 0x61, 0x00, 0x30, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x45, 0x00, 0x54, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x4b, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, + 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x43, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x20, 0x00, 0x53, 0x00, 0x45, 0x00, + 0x52, 0x00, 0x56, 0x00, 0x49, 0x00, 0x43, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x45, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames3_in_check(struct torture_context *tctx, struct lsa_LookupNames3 *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.num_names, 7, "num names"); + torture_assert_str_equal(tctx, r->in.names[0].string, "Account Operators", + "names[0]"); + torture_assert_str_equal(tctx, r->in.names[1].string, "Administrators", + "names[1]"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert_int_equal(tctx, r->in.lookup_options, 0, "lookup_options"); + torture_assert_int_equal(tctx, r->in.client_revision, 0, "client_revision"); + torture_assert_int_equal(tctx, *r->in.count, 0, "count"); + torture_assert_int_equal(tctx, r->in.sids->count, 0, "sids count"); + torture_assert(tctx, r->in.sids->sids == NULL, "sids sids"); + return true; +} + +static const uint8_t lsarlookupnames3_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x1a, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x14, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x10, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x12, 0x00, 0x14, 0x00, + 0x20, 0x00, 0x02, 0x00, 0x24, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x55, 0x00, 0x54, 0x00, 0x48, 0x00, 0x4f, 0x00, + 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x33, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, + 0xcc, 0x04, 0x9c, 0x4c, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x38, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x00, + 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x44, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, + 0x24, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, + 0xcc, 0x04, 0x9c, 0x4c, 0xe9, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupnames3_out_check(struct torture_context *tctx, + struct lsa_LookupNames3 *r) +{ + torture_assert_int_equal(tctx, *r->out.count, 7, "count"); + torture_assert_int_equal(tctx, r->out.sids->count, 7, "sids count"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + + + +static const uint8_t lsarlookupsids3_in_data[] = { + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x14, 0xcd, 0xfb, 0x2b, 0x07, 0x32, 0xfb, 0xb2, 0xcc, 0x04, 0x9c, 0x4c, + 0xe9, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x13, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarlookupsids3_in_check(struct torture_context *tctx, + struct lsa_LookupSids3 *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.sids->num_sids, 7, "num sids"); + torture_assert(tctx, r->in.sids->sids != NULL, "sids sids"); + torture_assert(tctx, r->in.sids->sids[0].sid != NULL, "sids sids"); + torture_assert(tctx, r->in.names != NULL, "names ptr"); + torture_assert_int_equal(tctx, r->in.names->count, 0, "names count"); + torture_assert(tctx, r->in.names->names == NULL, "names"); + torture_assert_int_equal(tctx, r->in.level, 1, "level"); + torture_assert(tctx, r->in.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->in.count, 7, "count"); + torture_assert_int_equal(tctx, r->in.lookup_options, 0, "unknown 1"); + torture_assert_int_equal(tctx, r->in.client_revision, 0, "unknown 2"); + + return true; +} + +#if 0 +static const uint8_t lsarlookupsids3_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0xc0 +}; + +static bool lsarlookupsids3_out_check(struct torture_context *tctx, + struct lsa_LookupSids3 *r) +{ + struct lsa_RefDomainList *domains = *(r->out.domains); + /* FIXME: Handle */ + torture_assert(tctx, r->out.names != NULL, "names ptr"); + torture_assert(tctx, r->out.domains != NULL, "domains ptr"); + torture_assert_int_equal(tctx, domains->count, 4, "domains count"); + torture_assert_int_equal(tctx, domains->max_size, 32, "domains size"); + torture_assert_str_equal(tctx, domains->domains[0].name.string, "NT AUTHORITY", "trust info name"); + torture_assert_int_equal(tctx, r->out.names->count, 7, "names count"); + torture_assert_str_equal(tctx, r->out.names->names[0].name.string, "Account Operators", "name str 1"); + torture_assert_str_equal(tctx, r->out.names->names[1].name.string, "Administrators", "name str 2"); + torture_assert_str_equal(tctx, r->out.names->names[2].name.string, "SUPPORT_388945a0", "name str 3"); + torture_assert(tctx, r->out.count != NULL, "count ptr"); + torture_assert_int_equal(tctx, *r->out.count, 7, "count"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} +#endif + +static const uint8_t lsarenumerateprivileges_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xab, 0xb8, 0x84, 0x36, 0xc6, 0xed, 0x4f, + 0x83, 0x16, 0x04, 0xe8, 0x63, 0x15, 0xeb, 0x84, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00 +}; + +static bool lsarenumerateprivileges_in_check(struct torture_context *tctx, + struct lsa_EnumPrivs *r) +{ + /* FIXME handle */ + torture_assert(tctx, r->in.resume_handle != NULL, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->in.resume_handle, 0, "resume handle"); + torture_assert_int_equal(tctx, r->in.max_count, 100, "max count"); + return true; +} + +static const uint8_t lsarenumerateprivileges_out_data[] = { + 0x1d, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x3c, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x2c, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x32, 0x00, 0x10, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x1e, 0x00, 0x18, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x32, 0x00, + 0x20, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x32, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x2c, 0x00, + 0x2c, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x40, 0x00, 0x30, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x00, + 0x38, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x36, 0x00, 0x3c, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x24, 0x00, 0x40, 0x00, 0x02, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x26, 0x00, + 0x44, 0x00, 0x02, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x28, 0x00, 0x48, 0x00, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x22, 0x00, 0x4c, 0x00, 0x02, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x22, 0x00, + 0x50, 0x00, 0x02, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x3a, 0x00, 0x54, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x58, 0x00, 0x02, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x00, + 0x5c, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x24, 0x00, 0x60, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x2a, 0x00, 0x64, 0x00, 0x02, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x38, 0x00, + 0x68, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x70, 0x00, 0x02, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x30, 0x00, + 0x74, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x43, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x6b, 0x00, 0x65, 0x00, + 0x6e, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x41, 0x00, 0x73, 0x00, 0x73, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x79, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x6b, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x4c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x4d, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x79, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6e, 0x00, + 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x51, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x61, 0x00, 0x50, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x63, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x41, 0x00, + 0x63, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x54, 0x00, 0x63, 0x00, 0x62, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x53, 0x00, 0x65, 0x00, 0x63, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x54, 0x00, 0x61, 0x00, + 0x6b, 0x00, 0x65, 0x00, 0x4f, 0x00, 0x77, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x68, 0x00, 0x69, 0x00, 0x70, 0x00, 0x50, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x4c, 0x00, 0x6f, 0x00, + 0x61, 0x00, 0x64, 0x00, 0x44, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x6d, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x53, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x73, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x42, 0x00, 0x61, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x43, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x50, 0x00, 0x61, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x43, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x6e, 0x00, + 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x42, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, + 0x75, 0x00, 0x70, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x52, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x53, 0x00, 0x68, 0x00, 0x75, 0x00, 0x74, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x44, 0x00, 0x65, 0x00, + 0x62, 0x00, 0x75, 0x00, 0x67, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x41, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, + 0x45, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x69, 0x00, 0x72, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x50, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x43, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x65, 0x00, 0x4e, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x79, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x53, 0x00, 0x68, 0x00, + 0x75, 0x00, 0x74, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x55, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x6b, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x53, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x41, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x44, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x56, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x49, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x50, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x53, 0x00, 0x65, 0x00, 0x43, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x47, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x62, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsarenumerateprivileges_out_check(struct torture_context *tctx, + struct lsa_EnumPrivs *r) +{ + torture_assert(tctx, r->out.resume_handle != NULL, "resume handle ptr"); + torture_assert_int_equal(tctx, *r->out.resume_handle, 29, "resume handle"); + torture_assert_str_equal(tctx, r->out.privs->privs[0].name.string, "SeCreateTokenPrivilege", "name"); + torture_assert_str_equal(tctx, r->out.privs->privs[1].name.string, "SeAssignPrimaryTokenPrivilege", "name"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t lsarsetforesttrustsinformation_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3e, 0xd4, 0x20, 0x20, 0xe8, 0xa1, 0x43, + 0x96, 0x67, 0x8c, 0xd1, 0xb9, 0x48, 0xa0, 0x3d, 0x0e, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x66, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x60, 0xcc, 0x85, 0x53, 0x92, 0x64, 0x11, 0x5e, 0x37, 0xa1, 0x11, 0x65, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x46, 0x00, 0x31, 0x00, 0x01 +}; + +static bool lsarsetforesttrustsinformation_in_check(struct torture_context *tctx, + struct lsa_lsaRSetForestTrustInformation *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.trusted_domain_name->string, "f1.test", "trusted domain name"); + torture_assert_int_equal(tctx, r->in.highest_record_type, 2, "highest record type"); + torture_assert(tctx, r->in.forest_trust_info != NULL, "forest trust info"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->count, 2, "number of forest trust records"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[0]->flags, 0, "first entry flags"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[0]->type, 0, "first entry type"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[0]->time, 0, "first entry time"); + torture_assert_str_equal(tctx, r->in.forest_trust_info->entries[0]->forest_trust_data.top_level_name.string, "f1.test", "first entry data"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[1]->flags, 0, "second entry flags"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[1]->type, 2, "second entry type"); + torture_assert_int_equal(tctx, r->in.forest_trust_info->entries[1]->time, 0, "second entry time"); + torture_assert_str_equal(tctx, r->in.forest_trust_info->entries[1]->forest_trust_data.domain_info.dns_domain_name.string, "f1.test", "second entry data"); + torture_assert_str_equal(tctx, r->in.forest_trust_info->entries[1]->forest_trust_data.domain_info.netbios_domain_name.string, "F1", "second entry data"); + torture_assert_int_equal(tctx, r->in.check_only, 1, "check only"); + + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t lsasettrusteddomaininfobyname_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xe2, 0xbe, 0xb5, 0xfe, 0x4a, 0xe2, 0x25, 0x43, + 0xaf, 0x37, 0x14, 0x77, 0xa5, 0xd6, 0xd9, 0x31, 0x0e, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x66, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x10, 0x00, 0x04, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x02, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x66, 0x00, 0x31, 0x00, + 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x60, 0xcc, 0x85, 0x53, + 0x92, 0x64, 0x11, 0x5e, 0x37, 0xa1, 0x11, 0x65, 0x44, 0x02, 0x00, 0x00, + 0x25, 0xf5, 0x9e, 0xfc, 0x2c, 0x36, 0x8e, 0x0d, 0xd8, 0x6b, 0x98, 0x14, + 0xb6, 0x78, 0xcc, 0xca, 0xb2, 0xbd, 0xa0, 0x8d, 0x59, 0xd9, 0x51, 0x90, + 0x14, 0x0e, 0x0c, 0x3f, 0xac, 0xed, 0x67, 0x98, 0xd9, 0x44, 0xe7, 0xec, + 0x72, 0xd7, 0x83, 0xba, 0x12, 0x3e, 0xcb, 0x8a, 0xaa, 0x87, 0xdb, 0xf2, + 0xf8, 0x35, 0x00, 0x9c, 0xc7, 0x76, 0x85, 0x8d, 0x04, 0x08, 0x4c, 0xa3, + 0x05, 0x4b, 0x02, 0x85, 0xcd, 0x1c, 0x83, 0xd4, 0x1e, 0xcc, 0xd8, 0xa3, + 0x32, 0x9e, 0xa5, 0x6f, 0xd8, 0x3d, 0xe2, 0xcd, 0xa1, 0x44, 0xf5, 0x03, + 0x47, 0x79, 0x22, 0xf3, 0xb4, 0x14, 0x3d, 0x6c, 0xe3, 0x98, 0x91, 0x96, + 0x89, 0x78, 0x26, 0xa1, 0x77, 0x78, 0x58, 0xa1, 0xba, 0x84, 0xb7, 0xb3, + 0x7a, 0xad, 0xcf, 0x77, 0x5c, 0x92, 0x97, 0x3a, 0x19, 0x0f, 0xfa, 0x7d, + 0x48, 0xa4, 0x11, 0x33, 0xdd, 0x51, 0xd6, 0x0c, 0x48, 0xd6, 0xd2, 0x59, + 0x83, 0x4d, 0xf6, 0x8b, 0x6b, 0x4d, 0x6a, 0x0e, 0xcc, 0x15, 0xd6, 0x1a, + 0x2f, 0x44, 0x61, 0x45, 0x8f, 0xa8, 0x1b, 0x3f, 0x2d, 0xbd, 0x3a, 0xdb, + 0xe0, 0x74, 0x44, 0x27, 0x02, 0x85, 0x02, 0xb4, 0xf9, 0x7f, 0x81, 0xcb, + 0x28, 0x27, 0x83, 0xfb, 0xa7, 0x92, 0x43, 0x70, 0x73, 0x2b, 0x89, 0xda, + 0x03, 0x83, 0x48, 0x58, 0x04, 0xba, 0x1e, 0xe2, 0x84, 0xf3, 0xa2, 0xfa, + 0x22, 0xe8, 0x5f, 0x41, 0xf3, 0xe6, 0x47, 0x92, 0x06, 0x61, 0x77, 0x31, + 0x00, 0x1b, 0x9f, 0x9b, 0x8f, 0xfd, 0x1d, 0x9e, 0xcb, 0x09, 0xd7, 0xdc, + 0x19, 0x94, 0xf4, 0x18, 0xfa, 0x96, 0x3e, 0xb3, 0xf0, 0x6b, 0x1c, 0x21, + 0xe4, 0x52, 0xb3, 0x48, 0x19, 0x5d, 0x10, 0x8e, 0xf1, 0xb5, 0x8b, 0x72, + 0x69, 0xdb, 0x60, 0x7b, 0x7c, 0xef, 0x5c, 0x16, 0x1b, 0x11, 0xf2, 0x97, + 0x2e, 0xf4, 0xd1, 0xc5, 0x13, 0x52, 0xb4, 0xc7, 0xca, 0xf8, 0xc0, 0x46, + 0x61, 0xdc, 0x9b, 0x8a, 0x5b, 0xcd, 0xf1, 0x1d, 0x7a, 0xc4, 0x0f, 0x02, + 0x0c, 0x22, 0x4f, 0x42, 0x12, 0xbb, 0xa6, 0xe2, 0xbb, 0x92, 0xda, 0xdb, + 0x12, 0xea, 0xe8, 0x61, 0xf2, 0xdd, 0x45, 0x3c, 0x35, 0x2c, 0x89, 0x92, + 0x22, 0x35, 0xb0, 0x24, 0x5b, 0xa7, 0x54, 0x58, 0xe1, 0x8c, 0xf5, 0x36, + 0x4d, 0x04, 0xdf, 0x25, 0x36, 0x48, 0x7b, 0x84, 0xc9, 0xb9, 0xc2, 0x2a, + 0xc5, 0x62, 0x06, 0xcb, 0xa1, 0xf5, 0x26, 0x05, 0xb4, 0x20, 0xcc, 0x8b, + 0xed, 0x2e, 0xa2, 0x4b, 0xa4, 0x85, 0x3e, 0x7f, 0x26, 0x25, 0x39, 0x69, + 0x2f, 0x89, 0x47, 0x7e, 0xde, 0xc7, 0xa4, 0x12, 0x01, 0xc5, 0x98, 0x01, + 0xf5, 0xae, 0x2e, 0x3e, 0xbd, 0xb7, 0x62, 0xaa, 0x57, 0x5d, 0xa0, 0x6f, + 0xac, 0xc5, 0x4e, 0x09, 0xcc, 0x87, 0x8e, 0x76, 0x93, 0xf2, 0xc6, 0x08, + 0x45, 0x88, 0x9f, 0x18, 0x9b, 0xeb, 0xa6, 0x1b, 0xf7, 0x64, 0x47, 0x73, + 0x0c, 0xb2, 0xc7, 0xc5, 0xe5, 0x62, 0x56, 0x7f, 0x0a, 0xe4, 0x79, 0xaf, + 0x7e, 0x71, 0xe6, 0x09, 0x22, 0x3d, 0x22, 0x10, 0x5c, 0x94, 0x71, 0x35, + 0xfd, 0x28, 0x20, 0x79, 0x89, 0x47, 0x5c, 0x37, 0x41, 0xd1, 0xfe, 0xee, + 0x2e, 0xd8, 0x41, 0x8e, 0x1c, 0x4d, 0x77, 0x09, 0x43, 0x6a, 0xee, 0x3c, + 0x80, 0x9b, 0xb7, 0xe7, 0x4c, 0xe8, 0x38, 0xd1, 0x6b, 0xc0, 0x03, 0x4b, + 0xbf, 0x8d, 0x19, 0x06, 0xad, 0x28, 0x22, 0xe7, 0x1a, 0x4e, 0x14, 0xa9, + 0x90, 0xba, 0xc4, 0x13, 0x8c, 0xde, 0x30, 0xfc, 0xe2, 0xb8, 0x97, 0x90, + 0x63, 0x3f, 0x30, 0xfc, 0xf5, 0x0d, 0xd2, 0xc2, 0xbe, 0xd2, 0xe3, 0x7f, + 0x52, 0x4e, 0xc5, 0x91, 0x38, 0xfc, 0xa7, 0x0d, 0xec, 0xa5, 0x4f, 0xd5, + 0x65, 0xb3, 0x51, 0x44, 0x21, 0x2a, 0x2e, 0x87, 0xe6, 0x91, 0x09, 0x8c, + 0xa5, 0x89, 0x13, 0x69, 0x01, 0x28, 0xa8, 0x64, 0x4f, 0x87, 0x0d, 0x12, + 0xe5, 0xeb, 0xce, 0xb9, 0xfa, 0xca, 0x10, 0x69, 0xa6, 0x95, 0x3b, 0x6d, + 0x6e, 0xca, 0x9e, 0x75, 0x25, 0x1c, 0xfa, 0xd6, 0x68, 0x84, 0xe0, 0x1f, + 0x35, 0x7e, 0x6e, 0xe8, 0xb7, 0x0a, 0x32, 0x9f, 0xc3, 0x31, 0x35, 0x84, + 0xa6, 0xc7, 0x5a, 0xa2, 0x0c, 0x8c, 0x07, 0x6a, 0x66, 0xd8, 0x58, 0xb1, + 0x4c, 0xb9, 0xbc, 0x46 +}; + +static bool lsasettrusteddomaininfobyname_in_check(struct torture_context *tctx, + struct lsa_SetTrustedDomainInfoByName *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.trusted_domain->string, "f1.test", "trusted domain"); + torture_assert_int_equal(tctx, r->in.level, LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL, "level"); + torture_assert(tctx, r->in.info != NULL, "trust info"); + torture_assert_str_equal(tctx, r->in.info->full_info_internal.info_ex.domain_name.string, "f1.test", "domain name"); + torture_assert_str_equal(tctx, r->in.info->full_info_internal.info_ex.netbios_name.string, "F1", "netbios name"); + torture_assert(tctx, r->in.info->full_info_internal.info_ex.sid != NULL, "domain sid ptr"); + torture_assert_sid_equal(tctx, r->in.info->full_info_internal.info_ex.sid, dom_sid_parse_talloc(tctx, "S-1-5-21-1401277536-1578198162-1695654199"), "domain sid"); + torture_assert_int_equal(tctx, r->in.info->full_info_internal.info_ex.trust_direction, 3, "trust direction"); + torture_assert_int_equal(tctx, r->in.info->full_info_internal.info_ex.trust_type, LSA_TRUST_TYPE_UPLEVEL, "trust type"); + torture_assert_int_equal(tctx, r->in.info->full_info_internal.info_ex.trust_attributes, 8, "trust attributes"); + torture_assert_int_equal(tctx, r->in.info->full_info_internal.posix_offset.posix_offset, 0, "posix offset"); + torture_assert_int_equal(tctx, r->in.info->full_info_internal.auth_info.auth_blob.size, 580, "auth blob size"); + + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t lsa_lsaRQueryForestTrustInformation_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0xfd, 0xa1, 0xb8, 0x04, 0x3a, 0xa2, 0x46, + 0xb1, 0x45, 0x5c, 0xaa, 0xf7, 0x54, 0x13, 0x9a, 0x16, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x61, 0x00, 0x64, 0x00, 0x32, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x02, 0x00 +}; + +static bool lsa_lsaRQueryForestTrustInformation_in_check(struct torture_context *tctx, + struct lsa_lsaRQueryForestTrustInformation *r) +{ + return true; +} + +static const uint8_t lsa_lsaRQueryForestTrustInformation_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xd1, 0x06, 0xc6, + 0xb9, 0xba, 0xcf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x18, 0x00, + 0x0c, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x61, 0x00, 0x64, 0x00, 0x32, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xbe, 0xd1, 0x06, 0xc6, 0xb9, 0xba, 0xcf, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x16, 0x00, 0x18, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x51, 0xd1, 0xbb, 0x42, 0xa8, 0x23, 0x83, 0xb1, 0x31, 0x59, 0xf1, 0x8d, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x64, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, + 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x44, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool lsa_lsaRQueryForestTrustInformation_out_check(struct torture_context *tctx, + struct lsa_lsaRQueryForestTrustInformation *r) +{ + return true; +} + +struct torture_suite *ndr_lsa_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "lsa"); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenPolicy, lsaropenpolicy_in_data, NDR_IN, lsaropenpolicy_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenPolicy, lsaropenpolicy_out_data, NDR_OUT, lsaropenpolicy_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenPolicy2, lsaropenpolicy2_in_data, NDR_IN, lsaropenpolicy2_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenPolicy2, lsaropenpolicy2_out_data, NDR_OUT, lsaropenpolicy2_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames, lsarlookupnames_in_data, NDR_IN, lsarlookupnames_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames, lsarlookupnames_out_data, NDR_OUT, lsarlookupnames_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids, lsarlookupsids_in_data, NDR_IN, lsarlookupsids_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids, lsarlookupsids_out_data, NDR_OUT, lsarlookupsids_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateAccount, lsarcreateaccount_in_data, NDR_IN, lsarcreateaccount_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateAccount, lsarcreateaccount_out_data, NDR_OUT, lsarcreateaccount_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_Delete, lsardelete_in_data, NDR_IN, lsardelete_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_Delete, lsardelete_out_data, NDR_OUT, lsardelete_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateSecret, lsarcreatesecret_in_data, NDR_IN, lsarcreatesecret_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateSecret, lsarcreatesecret_out_data, NDR_OUT, lsarcreatesecret_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenSecret, lsaropensecret_in_data, NDR_IN, lsaropensecret_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_OpenSecret, lsaropensecret_out_data, NDR_OUT, lsaropensecret_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_SetSecret, lsarsetsecret_in_data, NDR_IN, lsarsetsecret_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_SetSecret, lsarsetsecret_out_data, NDR_OUT, lsarsetsecret_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_QuerySecret, lsarquerysecret_in_data, NDR_IN, lsarquerysecret_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_QuerySecret, lsarquerysecret_out_data, NDR_OUT, lsarquerysecret_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateTrustedDomain, lsarcreatetrusteddomain_in_data, NDR_IN, lsarcreatetrusteddomain_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_CreateTrustedDomain, lsarcreatetrusteddomain_out_data, NDR_OUT, lsarcreatetrusteddomain_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_EnumAccounts, lsarenumerateaccounts_in_data, NDR_IN, lsarenumerateaccounts_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_EnumAccounts, lsarenumerateaccounts_out_data, NDR_OUT, lsarenumerateaccounts_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids2, lsarlookupsids2_in_data, NDR_IN, lsarlookupsids2_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids2, lsarlookupsids2_out_data, NDR_OUT, lsarlookupsids2_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames2, lsarlookupnames2_in_data, NDR_IN, lsarlookupnames2_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames2, lsarlookupnames2_out_data, NDR_OUT, lsarlookupnames2_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames3, lsarlookupnames3_in_data, NDR_IN, lsarlookupnames3_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupNames3, lsarlookupnames3_out_data, NDR_OUT, lsarlookupnames3_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids3, lsarlookupsids3_in_data, NDR_IN, lsarlookupsids3_in_check); + /* torture_suite_add_ndr_pull_fn_test(suite, lsa_LookupSids3, lsarlookupsids3_out_data, NDR_OUT, lsarlookupsids3_out_check); */ + + torture_suite_add_ndr_pull_fn_test(suite, lsa_EnumPrivs, lsarenumerateprivileges_in_data, NDR_IN, lsarenumerateprivileges_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_EnumPrivs, lsarenumerateprivileges_out_data, NDR_OUT, lsarenumerateprivileges_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_lsaRSetForestTrustInformation, lsarsetforesttrustsinformation_in_data, NDR_IN, lsarsetforesttrustsinformation_in_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_SetTrustedDomainInfoByName, lsasettrusteddomaininfobyname_in_data, NDR_IN, lsasettrusteddomaininfobyname_in_check); + + torture_suite_add_ndr_pull_fn_test(suite, lsa_lsaRQueryForestTrustInformation, lsa_lsaRQueryForestTrustInformation_in_data, NDR_IN, lsa_lsaRQueryForestTrustInformation_in_check); + torture_suite_add_ndr_pull_fn_test(suite, lsa_lsaRQueryForestTrustInformation, lsa_lsaRQueryForestTrustInformation_out_data, NDR_OUT, lsa_lsaRQueryForestTrustInformation_out_check); + + return suite; +} diff --git a/source4/torture/ndr/nbt.c b/source4/torture/ndr/nbt.c new file mode 100644 index 0000000..ec5cb90 --- /dev/null +++ b/source4/torture/ndr/nbt.c @@ -0,0 +1,253 @@ +/* + Unix SMB/CIFS implementation. + test suite for nbt ndr operations + + Copyright (C) Guenther Deschner 2010-2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "torture/ndr/proto.h" + +static const uint8_t netlogon_logon_request_req_data[] = { + 0x00, 0x00, 0x57, 0x49, 0x4e, 0x39, 0x38, 0x00, 0x47, 0x44, 0x00, 0x5c, + 0x4d, 0x41, 0x49, 0x4c, 0x53, 0x4c, 0x4f, 0x54, 0x5c, 0x54, 0x45, 0x4d, + 0x50, 0x5c, 0x4e, 0x45, 0x54, 0x4c, 0x4f, 0x47, 0x4f, 0x4e, 0x00, 0x01, + 0x01, 0x00, 0xff, 0xff +}; + +static bool netlogon_logon_request_req_check(struct torture_context *tctx, + struct nbt_netlogon_packet *r) +{ + torture_assert_int_equal(tctx, r->command, LOGON_REQUEST, "command"); + torture_assert_str_equal(tctx, r->req.logon0.computer_name, "WIN98", "computer name"); + torture_assert_str_equal(tctx, r->req.logon0.user_name, "GD", "user_name"); + torture_assert_str_equal(tctx, r->req.logon0.mailslot_name, "\\MAILSLOT\\TEMP\\NETLOGON", "mailslot_name"); + torture_assert_int_equal(tctx, r->req.logon0.request_count, 1, "request_count"); + torture_assert_int_equal(tctx, r->req.logon0.lmnt_token, 1, "lmnt_token"); + torture_assert_int_equal(tctx, r->req.logon0.lm20_token, 0xffff, "lm20_token"); + + return true; +} + +static const uint8_t netlogon_logon_request_resp_data[] = { + 0x06, 0x00, 0x5c, 0x5c, 0x4d, 0x54, 0x48, 0x45, 0x4c, 0x45, 0x4e, 0x41, + 0x00, 0xff, 0xff +}; + +static bool netlogon_logon_request_resp_check(struct torture_context *tctx, + struct nbt_netlogon_response2 *r) +{ + torture_assert_int_equal(tctx, r->command, LOGON_RESPONSE2, "command"); + torture_assert_str_equal(tctx, r->pdc_name, "\\\\MTHELENA", "pdc_name"); + torture_assert_int_equal(tctx, r->lm20_token, 0xffff, "lm20_token"); + + return true; +} + +static const uint8_t netlogon_samlogon_response_data[] = { +/* 0x04, 0x74, 0x17, 0x00, 0x00, 0x00, 0xfd, 0x33, 0x00, 0x00, 0x03, 0x13, */ + 0x17, 0x00, 0x00, 0x00, 0xfd, 0x33, 0x00, 0x00, 0x03, 0x13, + 0x44, 0xcd, 0x1c, 0x00, 0x4c, 0x46, 0xa6, 0x21, 0xe9, 0xd6, 0xb9, 0xb1, + 0x2f, 0xe9, 0x07, 0x77, 0x32, 0x6b, 0x38, 0x64, 0x6f, 0x6d, 0x03, 0x62, + 0x65, 0x72, 0x06, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, 0xc0, 0x18, 0x08, 0x67, 0x64, 0x77, 0x32, 0x6b, 0x38, 0x72, + 0x32, 0xc0, 0x18, 0x07, 0x57, 0x32, 0x4b, 0x38, 0x44, 0x4f, 0x4d, 0x00, + 0x08, 0x47, 0x44, 0x57, 0x32, 0x4b, 0x38, 0x52, 0x32, 0x00, 0x00, 0x17, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x2d, 0x53, 0x69, 0x74, 0x65, 0x2d, 0x4e, 0x61, 0x6d, 0x65, 0x00, + 0xc0, 0x51, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, +}; + +static bool netlogon_samlogon_response_check(struct torture_context *tctx, + struct netlogon_samlogon_response *r) +{ + struct GUID guid; + torture_assert_ntstatus_ok(tctx, GUID_from_string("cd441303-001c-464c-a621-e9d6b9b12fe9", &guid), ""); + + torture_assert_int_equal(tctx, r->ntver, 5, "ntver"); + torture_assert_int_equal(tctx, r->data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX, "command"); + torture_assert_int_equal(tctx, r->data.nt5_ex.sbz, 0, "sbz"); + torture_assert_int_equal(tctx, r->data.nt5_ex.server_type, 0x000033fd, "server_type"); + torture_assert_guid_equal(tctx, r->data.nt5_ex.domain_uuid, guid, "domain_uuid"); + torture_assert_str_equal(tctx, r->data.nt5_ex.forest, "w2k8dom.ber.redhat.com", "forest"); + torture_assert_str_equal(tctx, r->data.nt5_ex.dns_domain, "w2k8dom.ber.redhat.com", "dns_domain"); + torture_assert_str_equal(tctx, r->data.nt5_ex.pdc_dns_name, "gdw2k8r2.w2k8dom.ber.redhat.com", "pdc_dns_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.domain_name, "W2K8DOM", "domain_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.pdc_name, "GDW2K8R2", "pdc_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.user_name, "", "user_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.server_site, "Default-First-Site-Name", "server_site"); + torture_assert_str_equal(tctx, r->data.nt5_ex.client_site, "Default-First-Site-Name", "client_site"); + torture_assert_int_equal(tctx, r->data.nt5_ex.sockaddr_size, 0, "sockaddr_size"); + /* sockaddr: struct nbt_sockaddr + * sockaddr_family : 0x00000000 (0) + * pdc_ip : (null) + * remaining : DATA_BLOB length=0 */ + torture_assert_int_equal(tctx, r->data.nt5_ex.nt_version, 5, "nt_version"); + /* next_closest_site NULL */ + torture_assert_int_equal(tctx, r->data.nt5_ex.lmnt_token, 0xffff, "lmnt_token"); + torture_assert_int_equal(tctx, r->data.nt5_ex.lm20_token, 0xffff, "lm20_token"); + + return true; +} + +static const uint8_t nbt_netlogon_packet_data[] = { + 0x12, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x4e, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x4e, 0x00, + 0x59, 0x00, 0x24, 0x00, 0x00, 0x00, 0x5c, 0x4d, 0x41, 0x49, 0x4c, 0x53, + 0x4c, 0x4f, 0x54, 0x5c, 0x4e, 0x45, 0x54, 0x5c, 0x47, 0x45, 0x54, 0x44, + 0x43, 0x35, 0x32, 0x45, 0x41, 0x41, 0x38, 0x43, 0x30, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x9c, 0x4e, 0x59, 0xff, + 0xe1, 0xa0, 0x39, 0xac, 0x29, 0xa6, 0xe2, 0xda, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff +}; + +static bool nbt_netlogon_packet_check(struct torture_context *tctx, + struct nbt_netlogon_packet *r) +{ + torture_assert_int_equal(tctx, r->command, LOGON_SAM_LOGON_REQUEST, "command"); + torture_assert_int_equal(tctx, r->req.logon.request_count, 0, "request_count"); + torture_assert_str_equal(tctx, r->req.logon.computer_name, "LENNY", "computer_name"); + torture_assert_str_equal(tctx, r->req.logon.user_name, "LENNY$", "user_name"); + torture_assert_str_equal(tctx, r->req.logon.mailslot_name, "\\MAILSLOT\\NET\\GETDC52EAA8C0", "mailslot_name"); + torture_assert_int_equal(tctx, r->req.logon.acct_control, 0x00000080, "acct_control"); + torture_assert_int_equal(tctx, r->req.logon.sid_size, 24, "sid_size"); + torture_assert_int_equal(tctx, r->req.logon._pad.length, 2, "_pad.length"); + torture_assert_sid_equal(tctx, &r->req.logon.sid, dom_sid_parse_talloc(tctx, "S-1-5-21-4284042908-2889457889-3672286761"), "sid"); + torture_assert_int_equal(tctx, r->req.logon.nt_version, NETLOGON_NT_VERSION_1, "nt_version"); + torture_assert_int_equal(tctx, r->req.logon.lmnt_token, 0xffff, "lmnt_token"); + torture_assert_int_equal(tctx, r->req.logon.lm20_token, 0xffff, "lm20_token"); + + return true; +} + +static const uint8_t nbt_netlogon_packet_logon_primary_query_data[] = { + 0x07, 0x00, 0x58, 0x50, 0x44, 0x41, 0x54, 0x45, 0x56, 0x2d, 0x50, 0x52, + 0x4f, 0x00, 0x5c, 0x4d, 0x41, 0x49, 0x4c, 0x53, 0x4c, 0x4f, 0x54, 0x5c, + 0x4e, 0x45, 0x54, 0x5c, 0x47, 0x45, 0x54, 0x44, 0x43, 0x38, 0x31, 0x37, + 0x00, 0x00, 0x58, 0x00, 0x50, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, + 0x45, 0x00, 0x56, 0x00, 0x2d, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff +}; + +static bool nbt_netlogon_packet_logon_primary_query_check(struct torture_context *tctx, + struct nbt_netlogon_packet *r) +{ + torture_assert_int_equal(tctx, r->command, LOGON_PRIMARY_QUERY, "command"); + torture_assert_str_equal(tctx, r->req.pdc.computer_name, "XPDATEV-PRO", "computer_name"); + torture_assert_str_equal(tctx, r->req.pdc.mailslot_name, "\\MAILSLOT\\NET\\GETDC817", "mailslot_name"); + torture_assert_int_equal(tctx, r->req.pdc._pad.length, 1, "_pad.length"); + torture_assert_int_equal(tctx, r->req.pdc._pad.data[0], 0, "_pad.data"); + torture_assert_str_equal(tctx, r->req.pdc.unicode_name, "XPDATEV-PRO", "unicode_name"); + torture_assert_int_equal(tctx, r->req.pdc.nt_version, 0x0000000b, "nt_version"); + torture_assert_int_equal(tctx, r->req.pdc.lmnt_token, 0xffff, "lmnt_token"); + torture_assert_int_equal(tctx, r->req.pdc.lm20_token, 0xffff, "lm20_token"); + + return true; +} + +static const uint8_t netlogon_samlogon_response_data2[] = { +/* 0x04, 0x77, 0x17, 0x00, 0x00, 0x00, 0xfd, 0x33, 0x00, 0x00, 0x55, 0xaf,*/ + 0x17, 0x00, 0x00, 0x00, 0xfd, 0x33, 0x00, 0x00, 0x55, 0xaf, + 0x8d, 0x13, 0x8c, 0x91, 0x70, 0x41, 0x9d, 0x46, 0xd4, 0xd5, 0x04, 0x90, + 0xaa, 0x13, 0x03, 0x62, 0x6c, 0x61, 0x04, 0x62, 0x61, 0x73, 0x65, 0x00, + 0xc0, 0x18, 0x0a, 0x57, 0x32, 0x4b, 0x38, 0x52, 0x32, 0x2d, 0x32, 0x31, + 0x39, 0xc0, 0x18, 0x03, 0x42, 0x4c, 0x41, 0x00, 0x0a, 0x57, 0x32, 0x4b, + 0x38, 0x52, 0x32, 0x2d, 0x32, 0x31, 0x39, 0x00, 0x0a, 0x77, 0x32, 0x30, + 0x31, 0x32, 0x72, 0x32, 0x2d, 0x6c, 0x36, 0x05, 0x62, 0x61, 0x73, 0x65, + 0x2e, 0x00, 0x17, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x46, + 0x69, 0x72, 0x73, 0x74, 0x2d, 0x53, 0x69, 0x74, 0x65, 0x2d, 0x4e, 0x61, + 0x6d, 0x65, 0x00, 0xc0, 0x54, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff +}; + +static bool netlogon_samlogon_response_check2(struct torture_context *tctx, + struct netlogon_samlogon_response *r) +{ + struct GUID guid; + torture_assert_ntstatus_ok(tctx, GUID_from_string("138daf55-918c-4170-9d46-d4d50490aa13", &guid), ""); + + torture_assert_int_equal(tctx, r->ntver, 5, "ntver"); + torture_assert_int_equal(tctx, r->data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX, "command"); + torture_assert_int_equal(tctx, r->data.nt5_ex.sbz, 0, "sbz"); + torture_assert_int_equal(tctx, r->data.nt5_ex.server_type, 0x000033fd, "server_type"); + torture_assert_guid_equal(tctx, r->data.nt5_ex.domain_uuid, guid, "domain_uuid"); + torture_assert_str_equal(tctx, r->data.nt5_ex.forest, "bla.base", "forest"); + torture_assert_str_equal(tctx, r->data.nt5_ex.dns_domain, "bla.base", "dns_domain"); + torture_assert_str_equal(tctx, r->data.nt5_ex.pdc_dns_name, "W2K8R2-219.bla.base", "pdc_dns_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.domain_name, "BLA", "domain_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.pdc_name, "W2K8R2-219", "pdc_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.user_name, "w2012r2-l6.base.", "user_name"); + torture_assert_str_equal(tctx, r->data.nt5_ex.server_site, "Default-First-Site-Name", "server_site"); + torture_assert_str_equal(tctx, r->data.nt5_ex.client_site, "Default-First-Site-Name", "client_site"); + torture_assert_int_equal(tctx, r->data.nt5_ex.sockaddr_size, 0, "sockaddr_size"); + /* + * sockaddr: struct nbt_sockaddr + * sockaddr_family : 0x00000000 (0) + * pdc_ip : (null) + * remaining : DATA_BLOB length=0 + */ + torture_assert_int_equal(tctx, r->data.nt5_ex.nt_version, 5, "nt_version"); + /* next_closest_site NULL */ + torture_assert_int_equal(tctx, r->data.nt5_ex.lmnt_token, 0xffff, "lmnt_token"); + torture_assert_int_equal(tctx, r->data.nt5_ex.lm20_token, 0xffff, "lm20_token"); + + return true; +} + + +struct torture_suite *ndr_nbt_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "nbt"); + + torture_suite_add_ndr_pull_test(suite, nbt_netlogon_packet, netlogon_logon_request_req_data, netlogon_logon_request_req_check); + + torture_suite_add_ndr_pull_test(suite, + nbt_netlogon_packet, + nbt_netlogon_packet_logon_primary_query_data, + nbt_netlogon_packet_logon_primary_query_check); + + torture_suite_add_ndr_pull_test(suite, nbt_netlogon_response2, netlogon_logon_request_resp_data, netlogon_logon_request_resp_check); + + torture_suite_add_ndr_pull_test(suite, + netlogon_samlogon_response, + netlogon_samlogon_response_data, + netlogon_samlogon_response_check); + + torture_suite_add_ndr_pull_validate_test(suite, + netlogon_samlogon_response, + netlogon_samlogon_response_data, + netlogon_samlogon_response_check); + + torture_suite_add_ndr_pull_validate_test(suite, + nbt_netlogon_packet, + nbt_netlogon_packet_data, + nbt_netlogon_packet_check); + + torture_suite_add_ndr_pull_validate_test(suite, + nbt_netlogon_packet, + nbt_netlogon_packet_logon_primary_query_data, + nbt_netlogon_packet_logon_primary_query_check); + + torture_suite_add_ndr_pull_validate_test(suite, + netlogon_samlogon_response, + netlogon_samlogon_response_data2, + netlogon_samlogon_response_check2); + + return suite; +} diff --git a/source4/torture/ndr/ndr.c b/source4/torture/ndr/ndr.c new file mode 100644 index 0000000..0faef1a --- /dev/null +++ b/source4/torture/ndr/ndr.c @@ -0,0 +1,831 @@ +/* + Unix SMB/CIFS implementation. + test suite for winreg ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "torture/ndr/proto.h" +#include "../lib/util/dlinklist.h" +#include "param/param.h" +#include "librpc/gen_ndr/ndr_misc.h" + +struct ndr_pull_test_data { + DATA_BLOB data; + DATA_BLOB data_context; + size_t struct_size; + ndr_pull_flags_fn_t pull_fn; + ndr_push_flags_fn_t push_fn; + ndr_print_fn_t print_fn; + ndr_print_function_t print_function; + ndr_flags_type ndr_flags; + libndr_flags flags; + enum ndr_err_code ndr_err; +}; + +static enum ndr_err_code torture_ndr_push_struct_blob_flags(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, ndr_flags_type flags, libndr_flags ndr_flags, const void *p, ndr_push_flags_fn_t fn) +{ + struct ndr_push *ndr; + ndr = ndr_push_init_ctx(mem_ctx); + NDR_ERR_HAVE_NO_MEMORY(ndr); + + ndr->flags |= ndr_flags; + + NDR_CHECK(fn(ndr, flags, p)); + + *blob = ndr_push_blob(ndr); + talloc_steal(mem_ctx, blob->data); + talloc_free(ndr); + + return NDR_ERR_SUCCESS; +} + +static bool torture_ndrdump(struct torture_context *tctx, + struct ndr_pull *ndr, + const struct ndr_pull_test_data *data, + ndr_flags_type flags, + void *ds, + const char *name) +{ + struct ndr_print *ndr_print; + const char *name_raw; + ndr_flags_type ndr_flags = data->ndr_flags | flags; + + ndr_print = talloc_zero(tctx, struct ndr_print); + torture_assert(tctx, ndr_print, "out of memory"); + + if (DEBUGLEVEL >= 10) { + ndr_print->print = ndr_print_debug_helper; + } else { + ndr_print->print = ndr_print_string_helper; + } + + ndr_print->depth = 1; + + torture_assert(tctx, ndr_flags, "no flags have been set"); + + if (ndr_flags & (NDR_BUFFERS|NDR_SCALARS)) { + data->print_fn(ndr_print, name, ds); + } else { + data->print_function(ndr_print, name, ndr_flags, ds); + } + + name_raw = talloc_asprintf(tctx, "%s (RAW DATA)", name); + torture_assert(tctx, name_raw, "out of memory"); + + ndr_print_DATA_BLOB(ndr_print, name_raw, data->data); + + talloc_free(ndr_print); + + return true; +} + +static bool wrap_ndr_pullpush_test(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*check_fn) (struct torture_context *ctx, void *data) = test->fn; + const struct ndr_pull_test_data *data = (const struct ndr_pull_test_data *)test->data; + struct ndr_pull *ndr = ndr_pull_init_blob(&(data->data), tctx); + void *ds = talloc_zero_size(ndr, data->struct_size); + bool ret = true; + uint32_t highest_ofs; + + torture_assert(tctx, data, "out of memory"); + torture_assert(tctx, ndr, "out of memory"); + torture_assert(tctx, ds, "out of memory"); + + ndr->flags |= data->flags; + + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + + torture_assert_ndr_success(tctx, data->pull_fn(ndr, data->ndr_flags, ds), + "pulling"); + + if (ndr->offset > ndr->relative_highest_offset) { + highest_ofs = ndr->offset; + } else { + highest_ofs = ndr->relative_highest_offset; + } + + torture_assert(tctx, highest_ofs == ndr->data_size, + talloc_asprintf(tctx, + "%d unread bytes", ndr->data_size - highest_ofs)); + + if (check_fn != NULL) { + ret = check_fn(tctx, ds); + } else { + ret = true; + } + + torture_ndrdump(tctx, ndr, data, data->ndr_flags, ds, "ds"); + + if (data->push_fn != NULL) { + DATA_BLOB outblob; + torture_assert_ndr_success(tctx, torture_ndr_push_struct_blob_flags(&outblob, ndr, data->ndr_flags, ndr->flags, ds, data->push_fn), "pushing"); + torture_assert_data_blob_equal(tctx, outblob, data->data, "ndr push compare"); + } + + talloc_free(ndr); + return ret; +} + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pullpush_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + ndr_push_flags_fn_t push_fn, + ndr_print_fn_t print_fn, + ndr_print_function_t print_function, + const char *db_name, + DATA_BLOB db, + size_t struct_size, + ndr_flags_type ndr_flags, + libndr_flags flags, + const char *check_fn_name, + bool (*check_fn) (struct torture_context *ctx, void *data)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + struct ndr_pull_test_data *data; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, check_fn_name); + test->description = talloc_asprintf(test, "db:%s", + db_name); + test->run = wrap_ndr_pullpush_test; + + data = talloc_zero(test, struct ndr_pull_test_data); + data->data = db; + data->ndr_flags = ndr_flags; + data->flags = flags; + data->struct_size = struct_size; + data->pull_fn = pull_fn; + data->push_fn = push_fn; + data->print_fn = print_fn; + data->print_function = print_function; + + test->data = data; + test->fn = check_fn; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + + +static bool wrap_ndr_inout_pull_test(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*check_fn) (struct torture_context *ctx, void *data) = test->fn; + const struct ndr_pull_test_data *data = (const struct ndr_pull_test_data *)test->data; + void *ds = talloc_zero_size(tctx, data->struct_size); + struct ndr_pull *ndr; + uint32_t highest_ofs; + bool ret = false; + + torture_assert(tctx, data, "out of memory"); + torture_assert(tctx, ds, "out of memory"); + + /* handle NDR_IN context */ + + ndr = ndr_pull_init_blob(&(data->data_context), tctx); + torture_assert(tctx, ndr, "ndr init failed"); + + ndr->flags |= data->flags; + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + + torture_assert_ndr_success(tctx, + data->pull_fn(ndr, NDR_IN, ds), + "ndr pull of context failed"); + + if (ndr->offset > ndr->relative_highest_offset) { + highest_ofs = ndr->offset; + } else { + highest_ofs = ndr->relative_highest_offset; + } + + torture_assert(tctx, highest_ofs == ndr->data_size, + talloc_asprintf(tctx, "%d unread bytes", ndr->data_size - highest_ofs)); + + torture_ndrdump(tctx, ndr, data, NDR_IN, ds, "ds"); + + talloc_free(ndr); + + /* handle NDR_OUT */ + + ndr = ndr_pull_init_blob(&(data->data), tctx); + torture_assert(tctx, ndr, "ndr init failed"); + + ndr->flags |= data->flags; + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + + torture_assert_ndr_success(tctx, + data->pull_fn(ndr, NDR_OUT, ds), + "ndr pull failed"); + + if (ndr->offset > ndr->relative_highest_offset) { + highest_ofs = ndr->offset; + } else { + highest_ofs = ndr->relative_highest_offset; + } + + torture_assert(tctx, highest_ofs == ndr->data_size, + talloc_asprintf(tctx, "%d unread bytes", ndr->data_size - highest_ofs)); + + if (check_fn) { + ret = check_fn(tctx, ds); + } else { + ret = true; + } + + torture_ndrdump(tctx, ndr, data, NDR_OUT, ds, "ds"); + + talloc_free(ndr); + + return ret; +} + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_inout_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + ndr_print_function_t print_function, + const char *db_in_name, + DATA_BLOB db_in, + const char *db_out_name, + DATA_BLOB db_out, + size_t struct_size, + libndr_flags flags, + const char *check_fn_name, + bool (*check_fn) (struct torture_context *ctx, void *data)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + struct ndr_pull_test_data *data; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, check_fn_name); + test->description = talloc_asprintf(test, "db_in:%s db_out:%s", + db_in_name, + db_out_name); + test->run = wrap_ndr_inout_pull_test; + data = talloc_zero(test, struct ndr_pull_test_data); + data->data = db_out; + data->data_context = db_in; + data->ndr_flags = 0; + data->flags = flags; + data->struct_size = struct_size; + data->pull_fn = pull_fn; + data->print_function = print_function; + test->data = data; + test->fn = check_fn; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + +static bool wrap_ndr_pull_invalid_data_test(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + const struct ndr_pull_test_data *data = (const struct ndr_pull_test_data *)test->data; + struct ndr_pull *ndr = ndr_pull_init_blob(&(data->data), tctx); + void *ds = talloc_zero_size(ndr, data->struct_size); + bool ret = true; + + torture_assert(tctx, data, "out of memory"); + torture_assert(tctx, ndr, "out of memory"); + torture_assert(tctx, ds, "out of memory"); + + ndr->flags |= data->flags; + + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + + torture_assert_ndr_err_equal( + tctx, + data->pull_fn(ndr, data->ndr_flags, ds), + NDR_ERR_BUFSIZE, + "pulling invalid data"); + + talloc_free(ndr); + return ret; +} + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_invalid_data_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + const char *db_name, + DATA_BLOB db, + size_t struct_size, + ndr_flags_type ndr_flags, + libndr_flags flags, + enum ndr_err_code ndr_err) +{ + struct torture_test *test; + struct torture_tcase *tcase; + struct ndr_pull_test_data *data; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, db_name); + test->description = NULL; + test->run = wrap_ndr_pull_invalid_data_test; + + data = talloc_zero(test, struct ndr_pull_test_data); + data->data = db; + data->ndr_flags = ndr_flags; + data->flags = flags; + data->struct_size = struct_size; + data->pull_fn = pull_fn; + data->ndr_err = ndr_err; + + test->data = data; + test->fn = NULL; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + +static bool test_check_string_terminator(struct torture_context *tctx) +{ + struct ndr_pull *ndr; + DATA_BLOB blob; + TALLOC_CTX *mem_ctx = tctx; + + /* Simple test */ + blob = strhex_to_data_blob(tctx, "0000"); + + ndr = ndr_pull_init_blob(&blob, mem_ctx); + + torture_assert_ndr_success(tctx, ndr_check_string_terminator(ndr, 1, 2), + "simple check_string_terminator test failed"); + + torture_assert(tctx, ndr->offset == 0, + "check_string_terminator did not reset offset"); + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_check_string_terminator(ndr, 1, 3))) { + torture_fail(tctx, "check_string_terminator checked beyond string boundaries"); + } + + torture_assert(tctx, ndr->offset == 0, + "check_string_terminator did not reset offset"); + + talloc_free(ndr); + + blob = strhex_to_data_blob(tctx, "11220000"); + ndr = ndr_pull_init_blob(&blob, mem_ctx); + + torture_assert_ndr_success(tctx, + ndr_check_string_terminator(ndr, 4, 1), + "check_string_terminator failed to recognize terminator"); + + torture_assert_ndr_success(tctx, + ndr_check_string_terminator(ndr, 3, 1), + "check_string_terminator failed to recognize terminator"); + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_check_string_terminator(ndr, 2, 1))) { + torture_fail(tctx, "check_string_terminator erroneously reported terminator"); + } + + torture_assert(tctx, ndr->offset == 0, + "check_string_terminator did not reset offset"); + return true; +} + +static bool test_guid_from_string_valid(struct torture_context *tctx) +{ + /* FIXME */ + return true; +} + +static bool test_guid_from_string_null(struct torture_context *tctx) +{ + struct GUID guid; + torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER, + GUID_from_string(NULL, &guid), + "NULL failed"); + return true; +} + +static bool test_guid_from_string_invalid(struct torture_context *tctx) +{ + struct GUID g1; + bool failed = false; + int i; + const char *bad_guids[] = { + "bla", + "", + /* + "00000001-0002-0003-0405-060708090a0b", correct + */ + "00000001-0002-0003-0405-060708090a0b1", /* too long */ + "00000001-0002-0003-0405-060708090a0", /* too short */ + "00000001-0002-0003-0405--060708090a0", /* negative */ + "00000001-0002-0003--0405-060708090a0", /* negative */ + "-0000001-0002-0003-0405-060708090a0b", /* negative */ + "-0000001-0002-0003-04-5-060708090a0b", /* negative */ + "d0000001-0002-0003-0405-060708090a-b", /* negative */ + "00000001- -2-0003-0405-060708090a0b", /* negative, space */ + "00000001-0002-0003-0405- 060708090a0", /* whitespace */ + " 0000001-0002-0003--0405-060708090a0", /* whitespace */ + "00000001-0002-0003--0405-060708090a ", /* whitespace */ + "0000001-00002-0003-04050-60708090a0b", /* misshapen */ + "00000010-0002-0003-04050-60708090a0b", /* misshapen */ + "00000001-0002-0003-0405-0z0708090a0b", /* bad char */ + "00000001-00x2-0x03-0405-060708090a0b", /* bad char (00x) */ + "0x000001-0002-0003-0405-060708090a0b", /* 0x char */ + "00000001-0x02-0x03-0405-060708090a0b", /* 0x char */ + }; + + for (i = 0; i < ARRAY_SIZE(bad_guids); i++) { + NTSTATUS status = GUID_from_string(bad_guids[i], &g1); + if (! NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + torture_comment(tctx, "bad guid %s parsed as OK\n", + bad_guids[i]); + failed = true; + } + } + if (failed) { + torture_fail(tctx, "wrongly allowing invalid guids"); + } + return true; +} + +static bool test_guid_from_string(struct torture_context *tctx) +{ + struct GUID g1, exp; + /* we are asserting all these guids are valid and equal */ + const char *guids[5] = { + "00000001-0002-0003-0405-060708090a0b", + "{00000001-0002-0003-0405-060708090a0b}", + "{00000001-0002-0003-0405-060708090a0B}", /* mixed */ + "00000001-0002-0003-0405-060708090A0B", /* upper */ + "01000000020003000405060708090a0b", /* hex string */ + }; + int i; + + torture_assert_ntstatus_ok(tctx, + GUID_from_string(guids[0], &g1), + "invalid return code"); + exp.time_low = 1; + exp.time_mid = 2; + exp.time_hi_and_version = 3; + exp.clock_seq[0] = 4; + exp.clock_seq[1] = 5; + exp.node[0] = 6; + exp.node[1] = 7; + exp.node[2] = 8; + exp.node[3] = 9; + exp.node[4] = 10; + exp.node[5] = 11; + + for (i = 1; i < ARRAY_SIZE(guids); i++) { + torture_assert_ntstatus_ok(tctx, + GUID_from_string(guids[i], &g1), + "invalid return code"); + torture_assert(tctx, GUID_equal(&g1, &exp), + "UUID parsed incorrectly"); + } + return true; +} + +static bool test_guid_from_data_blob(struct torture_context *tctx) +{ + struct GUID g1, exp; + const uint8_t bin[16] = { + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b }; + const char *hexstr = "01000000020003000405060708090a0b"; + const DATA_BLOB blobs[2] = { + data_blob_const(bin, sizeof(bin)), + data_blob_string_const(hexstr), + }; + int i; + + exp.time_low = 1; + exp.time_mid = 2; + exp.time_hi_and_version = 3; + exp.clock_seq[0] = 4; + exp.clock_seq[1] = 5; + exp.node[0] = 6; + exp.node[1] = 7; + exp.node[2] = 8; + exp.node[3] = 9; + exp.node[4] = 10; + exp.node[5] = 11; + + for (i = 1; i < ARRAY_SIZE(blobs); i++) { + torture_assert_ntstatus_ok(tctx, + GUID_from_data_blob(&blobs[i], &g1), + "invalid return code"); + torture_assert(tctx, GUID_equal(&g1, &exp), + "UUID parsed incorrectly"); + } + + return true; +} + +static bool test_guid_string_valid(struct torture_context *tctx) +{ + struct GUID g; + g.time_low = 1; + g.time_mid = 2; + g.time_hi_and_version = 3; + g.clock_seq[0] = 4; + g.clock_seq[1] = 5; + g.node[0] = 6; + g.node[1] = 7; + g.node[2] = 8; + g.node[3] = 9; + g.node[4] = 10; + g.node[5] = 11; + torture_assert_str_equal(tctx, "00000001-0002-0003-0405-060708090a0b", + GUID_string(tctx, &g), + "parsing guid failed"); + return true; +} + +static bool test_guid_string2_valid(struct torture_context *tctx) +{ + struct GUID g; + g.time_low = 1; + g.time_mid = 2; + g.time_hi_and_version = 3; + g.clock_seq[0] = 4; + g.clock_seq[1] = 5; + g.node[0] = 6; + g.node[1] = 7; + g.node[2] = 8; + g.node[3] = 9; + g.node[4] = 10; + g.node[5] = 11; + torture_assert_str_equal(tctx, "{00000001-0002-0003-0405-060708090a0b}", + GUID_string2(tctx, &g), + "parsing guid failed"); + return true; +} + +static bool test_guid_into_blob(struct torture_context *tctx) +{ + enum ndr_err_code ndr_err; + static const char exp_guid[16] = + { 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x0, + 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb }; + DATA_BLOB exp = data_blob_const(exp_guid, 16); + char ndr_guid[16] = + { 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0 }; + DATA_BLOB b = data_blob_const(ndr_guid, 16); + struct GUID guid; + guid.time_low = 1; + guid.time_mid = 2; + guid.time_hi_and_version = 3; + guid.clock_seq[0] = 4; + guid.clock_seq[1] = 5; + guid.node[0] = 6; + guid.node[1] = 7; + guid.node[2] = 8; + guid.node[3] = 9; + guid.node[4] = 10; + guid.node[5] = 11; + + ndr_err = ndr_push_struct_into_fixed_blob(&b, &guid, + (ndr_push_flags_fn_t)ndr_push_GUID); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, + "wrong NDR error"); + torture_assert_data_blob_equal(tctx, b, exp, + "GUID packed wrongly"); + + return true; +} + +/* Really a test of ndr_push_struct_into_fixed_blob error handling */ +static bool test_guid_into_long_blob(struct torture_context *tctx) +{ + enum ndr_err_code ndr_err; + char ndr_guid[17] = + { 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 }; + DATA_BLOB b = data_blob_const(ndr_guid, 17); + struct GUID guid; + guid.time_low = 1; + guid.time_mid = 2; + guid.time_hi_and_version = 3; + guid.clock_seq[0] = 4; + guid.clock_seq[1] = 5; + guid.node[0] = 6; + guid.node[1] = 7; + guid.node[2] = 8; + guid.node[3] = 9; + guid.node[4] = 10; + guid.node[5] = 11; + + torture_assert(tctx, b.data != NULL, "data_blob_talloc failed"); + ndr_err = ndr_push_struct_into_fixed_blob( + &b, &guid, (ndr_push_flags_fn_t)ndr_push_GUID); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_BUFSIZE, + "wrong NDR error"); + + return true; +} + +static bool test_guid_into_short_blob(struct torture_context *tctx) +{ + enum ndr_err_code ndr_err; + char ndr_guid[15] = + { 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0 }; + DATA_BLOB b = data_blob_const(ndr_guid, 15); + struct GUID guid; + guid.time_low = 1; + guid.time_mid = 2; + guid.time_hi_and_version = 3; + guid.clock_seq[0] = 4; + guid.clock_seq[1] = 5; + guid.node[0] = 6; + guid.node[1] = 7; + guid.node[2] = 8; + guid.node[3] = 9; + guid.node[4] = 10; + guid.node[5] = 11; + + ndr_err = ndr_push_struct_into_fixed_blob( + &b, &guid, (ndr_push_flags_fn_t)ndr_push_GUID); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_BUFSIZE, + "wrong NDR error"); + + return true; +} + +static bool test_compare_uuid(struct torture_context *tctx) +{ + struct GUID g1, g2; + ZERO_STRUCT(g1); ZERO_STRUCT(g2); + torture_assert_int_equal(tctx, 0, GUID_compare(&g1, &g2), + "GUIDs not equal"); + g1.time_low = 1; + torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2), + "GUID diff invalid"); + + g1.time_low = 10; + torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2), + "GUID diff invalid"); + + g1.time_low = 0; + g1.clock_seq[1] = 20; + torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2), + "GUID diff invalid"); + + g1.time_low = ~0; + torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2), + "GUID diff invalid"); + + g1.time_low = 0; + g2.time_low = ~0; + torture_assert_int_equal(tctx, -1, GUID_compare(&g1, &g2), + "GUID diff invalid"); + return true; +} + +static bool test_syntax_id_from_string(struct torture_context *tctx) +{ + struct ndr_syntax_id s, exp; + const char *id = "00000001-0002-0003-0405-060708090a0b/0x12345678"; + + exp.uuid.time_low = 1; + exp.uuid.time_mid = 2; + exp.uuid.time_hi_and_version = 3; + exp.uuid.clock_seq[0] = 4; + exp.uuid.clock_seq[1] = 5; + exp.uuid.node[0] = 6; + exp.uuid.node[1] = 7; + exp.uuid.node[2] = 8; + exp.uuid.node[3] = 9; + exp.uuid.node[4] = 10; + exp.uuid.node[5] = 11; + exp.if_version = 0x12345678; + + torture_assert(tctx, + ndr_syntax_id_from_string(id, &s), + "invalid return code"); + torture_assert(tctx, ndr_syntax_id_equal(&s, &exp), + "NDR syntax id parsed incorrectly"); + return true; +} + +struct torture_suite *torture_local_ndr(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "ndr"); + + torture_suite_add_suite(suite, ndr_dcerpc_suite(suite)); + torture_suite_add_suite(suite, ndr_winreg_suite(suite)); + torture_suite_add_suite(suite, ndr_atsvc_suite(suite)); + torture_suite_add_suite(suite, ndr_lsa_suite(suite)); + torture_suite_add_suite(suite, ndr_epmap_suite(suite)); + torture_suite_add_suite(suite, ndr_dfs_suite(suite)); + torture_suite_add_suite(suite, ndr_dfsblob_suite(suite)); + torture_suite_add_suite(suite, ndr_netlogon_suite(suite)); + torture_suite_add_suite(suite, ndr_drsuapi_suite(suite)); + torture_suite_add_suite(suite, ndr_spoolss_suite(suite)); + torture_suite_add_suite(suite, ndr_winspool_suite(suite)); + torture_suite_add_suite(suite, ndr_ntprinting_suite(suite)); + torture_suite_add_suite(suite, ndr_samr_suite(suite)); + torture_suite_add_suite(suite, ndr_drsblobs_suite(suite)); + torture_suite_add_suite(suite, ndr_dnsp_suite(suite)); + torture_suite_add_suite(suite, ndr_nbt_suite(suite)); + torture_suite_add_suite(suite, ndr_ntlmssp_suite(suite)); + torture_suite_add_suite(suite, ndr_backupkey_suite(suite)); + torture_suite_add_suite(suite, ndr_witness_suite(suite)); + torture_suite_add_suite(suite, ndr_clusapi_suite(suite)); + torture_suite_add_suite(suite, ndr_negoex_suite(suite)); + torture_suite_add_suite(suite, ndr_string_suite(suite)); + torture_suite_add_suite(suite, ndr_krb5pac_suite(suite)); + torture_suite_add_suite(suite, ndr_cabinet_suite(suite)); + torture_suite_add_suite(suite, ndr_charset_suite(suite)); + torture_suite_add_suite(suite, ndr_svcctl_suite(suite)); + torture_suite_add_suite(suite, ndr_ODJ_suite(suite)); + + torture_suite_add_simple_test(suite, "string terminator", + test_check_string_terminator); + + torture_suite_add_simple_test(suite, "guid_from_string_null", + test_guid_from_string_null); + + torture_suite_add_simple_test(suite, "guid_from_string", + test_guid_from_string); + + torture_suite_add_simple_test(suite, "guid_from_data_blob", + test_guid_from_data_blob); + + torture_suite_add_simple_test(suite, "guid_from_string_invalid", + test_guid_from_string_invalid); + + torture_suite_add_simple_test(suite, "guid_string_valid", + test_guid_string_valid); + + torture_suite_add_simple_test(suite, "guid_string2_valid", + test_guid_string2_valid); + + torture_suite_add_simple_test(suite, "guid_from_string_valid", + test_guid_from_string_valid); + + torture_suite_add_simple_test(suite, "compare_uuid", + test_compare_uuid); + + torture_suite_add_simple_test(suite, "guid_into_blob", + test_guid_into_blob); + + torture_suite_add_simple_test(suite, "guid_into_short_blob", + test_guid_into_short_blob); + + torture_suite_add_simple_test(suite, "guid_into_long_blob", + test_guid_into_long_blob); + + torture_suite_add_simple_test(suite, "syntax_id_from_string", + test_syntax_id_from_string); + + return suite; +} + diff --git a/source4/torture/ndr/ndr.h b/source4/torture/ndr/ndr.h new file mode 100644 index 0000000..82ccc69 --- /dev/null +++ b/source4/torture/ndr/ndr.h @@ -0,0 +1,249 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __TORTURE_NDR_H__ +#define __TORTURE_NDR_H__ + +#include "torture/torture.h" +#include "librpc/ndr/libndr.h" +#include "libcli/security/security.h" + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pullpush_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + ndr_push_flags_fn_t push_fn, + ndr_print_fn_t print_fn, + ndr_print_function_t print_function, + const char *db_name, + DATA_BLOB db, + size_t struct_size, + ndr_flags_type ndr_flags, + libndr_flags flags, + const char *check_fn_name, + bool (*check_fn) (struct torture_context *, void *data)); + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_inout_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + ndr_print_function_t print_fn, + const char *db_in_name, + DATA_BLOB db_in, + const char *db_out_name, + DATA_BLOB db_out, + size_t struct_size, + libndr_flags flags, + const char *check_fn_name, + bool (*check_fn) (struct torture_context *ctx, void *data)); + +_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_invalid_data_test( + struct torture_suite *suite, + const char *name, + ndr_pull_flags_fn_t pull_fn, + const char *db_name, + DATA_BLOB db, + size_t struct_size, + ndr_flags_type ndr_flags, + libndr_flags flags, + enum ndr_err_code ndr_err); + +#define torture_suite_add_ndr_pull_test(suite,name,data,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + NULL, \ + (ndr_print_fn_t)ndr_print_ ## name, \ + NULL, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + NDR_SCALARS|NDR_BUFFERS, 0, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_invalid_data_test(suite,name,data,ndr_err) \ + _torture_suite_add_ndr_pull_invalid_data_test( \ + suite, \ + #name, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + NDR_SCALARS|NDR_BUFFERS, 0, \ + ndr_err); + +#define torture_suite_add_ndr_pull_fn_test(suite,name,data,flags,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name "_" #flags, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + NULL, \ + NULL, \ + (ndr_print_function_t)ndr_print_ ## name, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + flags, 0, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_fn_test_flags(suite,name,data,flags,flags2,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name "_" #flags "_" #flags2, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + NULL, \ + NULL, \ + (ndr_print_function_t)ndr_print_ ## name, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + flags, flags2, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_validate_test(suite,name,data,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name "_VALIDATE", \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_push_flags_fn_t)ndr_push_ ## name, \ + (ndr_print_fn_t)ndr_print_ ## name, \ + NULL, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + NDR_SCALARS|NDR_BUFFERS, 0, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_validate_test_blob(suite,name,data_blob,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name "_VALIDATE", \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_push_flags_fn_t)ndr_push_ ## name, \ + (ndr_print_fn_t)ndr_print_ ## name, \ + NULL, \ + #data_blob, \ + data_blob, \ + sizeof(struct name), \ + NDR_SCALARS|NDR_BUFFERS, 0, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_validate_test_b64(suite,name,tname,b64,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name "_" tname, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_push_flags_fn_t)ndr_push_ ## name, \ + (ndr_print_fn_t)ndr_print_ ## name, \ + NULL, \ + #b64, \ + base64_decode_data_blob_talloc(suite, b64), \ + sizeof(struct name), \ + NDR_SCALARS|NDR_BUFFERS, 0, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pullpush_fn_test_flags(suite,name,data,flags,flags2,check_fn) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pullpush_test(suite, #name, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_push_flags_fn_t)ndr_push_ ## name, \ + NULL, \ + (ndr_print_function_t)ndr_print_ ## name, \ + #data, \ + data_blob_const(data, sizeof(data)), \ + sizeof(struct name), \ + flags, flags2, \ + #check_fn, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_io_test(suite,name,data_in,data_out,check_fn_out) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn_out; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pull_inout_test(suite, #name "_INOUT", \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_print_function_t)ndr_print_ ## name, \ + #data_in, \ + data_blob_const(data_in, sizeof(data_in)), \ + #data_out, \ + data_blob_const(data_out, sizeof(data_out)), \ + sizeof(struct name), \ + 0, \ + #check_fn_out, \ + check_fn_anon); \ + } while(0) + +#define torture_suite_add_ndr_pull_io_test_flags(suite,name,data_in,data_out,flags,check_fn_out) \ + do { \ + bool (*check_fn_typed) (struct torture_context *, struct name *) = \ + check_fn_out; \ + bool (*check_fn_anon) (struct torture_context *, void *) = \ + (bool (*) (struct torture_context *, void *)) check_fn_typed; \ + _torture_suite_add_ndr_pull_inout_test(suite, #name "_INOUT_" #flags, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## name, \ + (ndr_print_function_t)ndr_print_ ## name, \ + #data_in, \ + data_blob_const(data_in, sizeof(data_in)), \ + #data_out, \ + data_blob_const(data_out, sizeof(data_out)), \ + sizeof(struct name), \ + flags, \ + #check_fn_out, \ + check_fn_anon); \ + } while(0) + +#endif /* __TORTURE_NDR_H__ */ diff --git a/source4/torture/ndr/negoex.c b/source4/torture/ndr/negoex.c new file mode 100644 index 0000000..1cd2d54 --- /dev/null +++ b/source4/torture/ndr/negoex.c @@ -0,0 +1,100 @@ +/* + Unix SMB/CIFS implementation. + test suite for negoex ndr operations + + Copyright (C) Guenther Deschner 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_negoex.h" +#include "torture/ndr/proto.h" + +static const uint8_t negoex_MESSAGE_ARRAY_data[] = { + 0x4e, 0x45, 0x47, 0x4f, 0x45, 0x58, 0x54, 0x53, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x06, 0xcb, 0x30, 0x71, 0x62, 0x20, 0x1b, 0x6a, 0x40, 0x9e, 0x35, 0x14, + 0xc2, 0x6b, 0x17, 0x73, 0xba, 0x25, 0xdd, 0x80, 0x91, 0xfb, 0xae, 0x2c, + 0x68, 0x4b, 0x99, 0x28, 0xf0, 0x3c, 0x3e, 0xf3, 0xe2, 0xcf, 0x60, 0xa3, + 0x29, 0xee, 0xa0, 0xf9, 0xb1, 0x10, 0x4b, 0x56, 0xc3, 0x83, 0xc7, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x33, 0x53, 0x0d, 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, + 0x78, 0x6e, 0xc3, 0x08, 0x4e, 0x45, 0x47, 0x4f, 0x45, 0x58, 0x54, 0x53, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x06, 0xcb, 0x30, 0x71, 0x62, 0x20, 0x1b, 0x6a, + 0x40, 0x9e, 0x35, 0x14, 0xc2, 0x6b, 0x17, 0x73, 0x5c, 0x33, 0x53, 0x0d, + 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, 0x78, 0x6e, 0xc3, 0x08, + 0x40, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x30, 0x56, 0xa0, 0x54, + 0x30, 0x52, 0x30, 0x27, 0x80, 0x25, 0x30, 0x23, 0x31, 0x21, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x27, 0x80, 0x25, 0x30, + 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4b, 0x65, 0x79 +}; + +static bool negoex_MESSAGE_ARRAY_check(struct torture_context *tctx, + struct negoex_MESSAGE_ARRAY *r) +{ + struct GUID guid; + struct negoex_MESSAGE m; + + torture_assert_int_equal(tctx, r->count, 2, "count"); + + m = r->messages[0]; + + torture_assert_str_equal(tctx, m.signature, "NEGOEXTS", "signature"); + torture_assert_int_equal(tctx, m.type, NEGOEX_MESSAGE_TYPE_ACCEPTOR_NEGO, "type"); + torture_assert_int_equal(tctx, m.sequence_number, 0, "sequence_number"); + torture_assert_int_equal(tctx, m.header_length, 96, "header_length"); + torture_assert_int_equal(tctx, m.message_length, 112, "message_length"); + GUID_from_string("7130cb06-2062-6a1b-409e-3514c26b1773", &guid); + torture_assert_guid_equal(tctx, m.conversation_id, guid, "conversation_id"); + /* torture_assert_int_equal(tctx, m.p.nego.random, ba25dd8091fbae2c684b9928f03c3ef3e2cf60a329eea0f9b1104b56c383c732, "p.nego.random"); */ + torture_assert_int_equal(tctx, m.p.nego.protocol_version, 0, "p.nego.protocol_version"); + torture_assert_int_equal(tctx, m.p.nego.auth_schemes.count, 1, "p.nego.auth_schemes.count"); + GUID_from_string("0d53335c-f9ea-4d0d-b2ec-4ae3786ec308", &guid); + torture_assert_guid_equal(tctx, m.p.nego.auth_schemes.array[0].guid, guid, "p.nego.auth_schemes.array[0].guid"); + torture_assert_int_equal(tctx, m.p.nego.extensions.count, 0, "p.nego.extensions.count"); + + m = r->messages[1]; + + torture_assert_str_equal(tctx, m.signature, "NEGOEXTS", "signature"); + torture_assert_int_equal(tctx, m.type, NEGOEX_MESSAGE_TYPE_ACCEPTOR_META_DATA, "type"); + torture_assert_int_equal(tctx, m.sequence_number, 1, "sequence_number"); + torture_assert_int_equal(tctx, m.header_length, 64, "header_length"); + torture_assert_int_equal(tctx, m.message_length, 152, "message_length"); + GUID_from_string("7130cb06-2062-6a1b-409e-3514c26b1773", &guid); + torture_assert_guid_equal(tctx, m.conversation_id, guid, "conversation_id"); + GUID_from_string("0d53335c-f9ea-4d0d-b2ec-4ae3786ec308", &guid); + torture_assert_guid_equal(tctx, m.p.exchange.auth_scheme.guid, guid, "auth_scheme.guid"); + torture_assert_int_equal(tctx, m.p.exchange.exchange.blob.length, 88, "exchange.blob.length"); + + return true; +} + +struct torture_suite *ndr_negoex_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "negoex"); + + torture_suite_add_ndr_pull_validate_test(suite, negoex_MESSAGE_ARRAY, + negoex_MESSAGE_ARRAY_data, + negoex_MESSAGE_ARRAY_check); + + return suite; +} diff --git a/source4/torture/ndr/netlogon.c b/source4/torture/ndr/netlogon.c new file mode 100644 index 0000000..bfd7a61 --- /dev/null +++ b/source4/torture/ndr/netlogon.c @@ -0,0 +1,825 @@ +/* + Unix SMB/CIFS implementation. + test suite for netlogon ndr operations + + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "torture/ndr/proto.h" + +static const uint8_t netrserverauthenticate3_in_data[] = { + 0xb0, 0x2e, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x4e, 0x00, 0x41, 0x00, + 0x54, 0x00, 0x49, 0x00, 0x56, 0x00, 0x45, 0x00, 0x2d, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x2e, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x2e, 0x00, 0x42, 0x00, 0x41, 0x00, 0x53, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x68, 0x8e, 0x3c, 0xdf, 0x23, 0x02, 0xb1, 0x51, 0xff, 0xff, 0x07, 0x60 +}; + +static bool netrserverauthenticate3_in_check(struct torture_context *tctx, + struct netr_ServerAuthenticate3 *r) +{ + uint8_t cred_expected[8] = { 0x68, 0x8e, 0x3c, 0xdf, 0x23, 0x02, 0xb1, 0x51 }; + torture_assert_str_equal(tctx, r->in.server_name, "\\\\NATIVE-DC.NATIVE.BASE", "server name"); + torture_assert_str_equal(tctx, r->in.account_name, "NATIVE-2K$", "account name"); + torture_assert_int_equal(tctx, r->in.secure_channel_type, 2, "secure channel type"); + torture_assert_str_equal(tctx, r->in.computer_name, "NATIVE-2K", "computer name"); + torture_assert_int_equal(tctx, *r->in.negotiate_flags, 0x6007ffff, "negotiate flags"); + torture_assert_mem_equal(tctx, cred_expected, r->in.credentials->data, 8, "credentials"); + return true; +} + +static const uint8_t netrserverauthenticate3_out_data[] = { + 0x22, 0x0c, 0x86, 0x8a, 0xe9, 0x92, 0x93, 0xc9, 0xff, 0xff, 0x07, 0x60, + 0x54, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool netrserverauthenticate3_out_check(struct torture_context *tctx, + struct netr_ServerAuthenticate3 *r) +{ + uint8_t cred_expected[8] = { 0x22, 0x0c, 0x86, 0x8a, 0xe9, 0x92, 0x93, 0xc9 }; + torture_assert_mem_equal(tctx, cred_expected, r->out.return_credentials->data, 8, "return_credentials"); + torture_assert_int_equal(tctx, *r->out.negotiate_flags, 0x6007ffff, "negotiate flags"); + torture_assert_int_equal(tctx, *r->out.rid, 0x454, "rid"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t netrserverreqchallenge_in_data[] = { + 0xb0, 0x2e, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x4e, 0x00, 0x41, 0x00, + 0x54, 0x00, 0x49, 0x00, 0x56, 0x00, 0x45, 0x00, 0x2d, 0x00, 0x44, 0x00, + 0x43, 0x00, 0x2e, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x2e, 0x00, 0x42, 0x00, 0x41, 0x00, 0x53, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0xa3, 0x2c, 0xa2, 0x95, 0x40, 0xcc, 0xb7, 0xbb +}; + +static bool netrserverreqchallenge_in_check(struct torture_context *tctx, + struct netr_ServerReqChallenge *r) +{ + uint8_t cred_expected[8] = { 0xa3, 0x2c, 0xa2, 0x95, 0x40, 0xcc, 0xb7, 0xbb }; + torture_assert_str_equal(tctx, r->in.server_name, "\\\\NATIVE-DC.NATIVE.BASE", "server name"); + torture_assert_str_equal(tctx, r->in.computer_name, "NATIVE-2K", "account name"); + torture_assert_mem_equal(tctx, cred_expected, r->in.credentials->data, 8, "credentials"); + + return true; +} + +static const uint8_t netrserverreqchallenge_out_data[] = { + 0x22, 0xfc, 0xc1, 0x17, 0xc0, 0xae, 0x27, 0x8e, 0x00, 0x00, 0x00, 0x00 +}; + +static bool netrserverreqchallenge_out_check(struct torture_context *tctx, + struct netr_ServerReqChallenge *r) +{ + uint8_t cred_expected[8] = { 0x22, 0xfc, 0xc1, 0x17, 0xc0, 0xae, 0x27, 0x8e }; + torture_assert_mem_equal(tctx, cred_expected, r->out.return_credentials->data, 8, "return_credentials"); + torture_assert_ntstatus_ok(tctx, r->out.result, "return code"); + + return true; +} + +static const uint8_t netrlogonsamlogon_w2k_in_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x52, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x54, 0x00, 0x48, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x08, 0xaf, 0x72, 0x50, 0xa0, 0x5b, 0x50, 0x19, + 0x02, 0xc3, 0x39, 0x4d, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xad, 0xde, 0x00, 0x00, 0xef, 0xbe, 0x00, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x02, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x1c, 0x00, 0x02, 0x00, 0x31, 0xeb, 0xf4, 0x68, 0x62, 0x93, 0xfe, 0x38, + 0x51, 0xc1, 0x1d, 0x41, 0x0a, 0xbd, 0x5d, 0xdf, 0xe3, 0x4f, 0x76, 0x7f, + 0x19, 0x12, 0xcd, 0xfe, 0x9c, 0x68, 0xed, 0x9b, 0x1e, 0x9c, 0x66, 0xf6, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x6d, 0x00, 0x74, 0x00, + 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, + 0x06, 0x00 +}; + +static bool netrlogonsamlogon_w2k_in_check(struct torture_context *tctx, + struct netr_LogonSamLogon *r) +{ + uint8_t credential_expected[8] = { 0x08, 0xaf, 0x72, 0x50, 0xa0, 0x5b, 0x50, 0x19 }; + uint8_t return_authenticator_expected[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t lmpassword_expected[16] = { 0x31, 0xeb, 0xf4, 0x68, 0x62, 0x93, 0xfe, 0x38, 0x51, 0xc1, 0x1d, 0x41, 0x0a, 0xbd, 0x5d, 0xdf }; + uint8_t ntpassword_expected[16] = { 0xe3, 0x4f, 0x76, 0x7f, 0x19, 0x12, 0xcd, 0xfe, 0x9c, 0x68, 0xed, 0x9b, 0x1e, 0x9c, 0x66, 0xf6 }; + + torture_assert_str_equal(tctx, r->in.server_name, "\\\\W2KSRV", "server_name"); + torture_assert_str_equal(tctx, r->in.computer_name, "MTHELENA", "computer_name"); + torture_assert_mem_equal(tctx, r->in.credential->cred.data, credential_expected, 8, "credential"); +/* torture_assert_int_equal(tctx, r->in.credential->timestamp, 0, "credential.timestamp"); */ + torture_assert_mem_equal(tctx, r->in.return_authenticator->cred.data, return_authenticator_expected, 8, "return_authenticator.cred.data"); + torture_assert_int_equal(tctx, r->in.return_authenticator->timestamp, 0, "return_authenticator.timestamp"); + torture_assert_int_equal(tctx, r->in.logon_level, NetlogonInteractiveInformation, "logon_level"); + torture_assert(tctx, r->in.logon, "logon NULL pointer"); + torture_assert(tctx, r->in.logon->password, "logon->password NULL pointer"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.domain_name.length, 12, "domain_name.length"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.domain_name.size, 12, "domain_name.size"); + torture_assert_str_equal(tctx, r->in.logon->password->identity_info.domain_name.string, "W2KDOM", "domain_name.string"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.parameter_control, 0, "parameter_control"); + torture_assert_u64_equal(tctx, r->in.logon->password->identity_info.logon_id, 0xbeef0000dead, "logon_id"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.account_name.length, 26, "account_name.length"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.account_name.size, 26, "account_name.size"); + torture_assert_str_equal(tctx, r->in.logon->password->identity_info.account_name.string, "administrator", "account_name.string"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.workstation.length, 20, "workstation.length"); + torture_assert_int_equal(tctx, r->in.logon->password->identity_info.workstation.size, 20, "workstation.size"); + torture_assert_str_equal(tctx, r->in.logon->password->identity_info.workstation.string, "\\\\mthelena", "workstation.string"); + torture_assert_mem_equal(tctx, r->in.logon->password->lmpassword.hash, lmpassword_expected, 16, "lmpassword"); + torture_assert_mem_equal(tctx, r->in.logon->password->ntpassword.hash, ntpassword_expected, 16, "ntpassword"); + torture_assert_int_equal(tctx, r->in.validation_level, 6, "validation_level"); + + return true; +} + +#if 0 +static const uint8_t netrlogonsamlogon_w2k_out_data[] = { + 0x6c, 0xdb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0xc0 +}; + +static bool netrlogonsamlogon_w2k_out_check(struct torture_context *tctx, + struct netr_LogonSamLogon *r) +{ + uint8_t return_authenticator_expected[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + torture_assert_mem_equal(tctx, r->out.return_authenticator->cred.data, return_authenticator_expected, 8, "return_authenticator.cred.data"); + torture_assert_int_equal(tctx, r->out.return_authenticator->timestamp, 0, "return_authenticator.timestamp"); + torture_assert(tctx, r->out.validation, "validation NULL pointer"); + torture_assert(tctx, (r->out.validation->sam6 == NULL), "sam6 not NULL"); + torture_assert_int_equal(tctx, *r->out.authoritative, 1, "authoritative"); + torture_assert_ntstatus_equal(tctx, r->out.result, NT_STATUS_INVALID_INFO_CLASS, "unexpected result"); + + return true; +} +#endif + +static const uint8_t netrlogongetdomaininfo_in_data[] = { + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x64, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x54, 0x00, 0x48, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x22, 0xbd, 0x03, 0x67, 0x3d, 0xdf, 0x17, 0xd3, 0x98, 0x81, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool netrlogongetdomaininfo_in_check(struct torture_context *tctx, + struct netr_LogonGetDomainInfo *r) +{ + return true; +} + +static const uint8_t netrlogongetdomaininfo_out_data[] = { + 0x81, 0x3f, 0x80, 0x20, 0x4b, 0x4a, 0x18, 0x93, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x32, 0x00, 0x34, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x32, 0x00, 0x34, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x99, 0x7c, 0x28, 0xfd, + 0x3b, 0xc8, 0x03, 0x4d, 0x84, 0x9e, 0x30, 0xc4, 0xf0, 0x62, 0xe2, 0xd3, + 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, + 0x36, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x80, 0x9f, 0x75, 0x68, 0x0c, 0x07, 0x89, 0x1a, 0xd7, 0x39, 0x71, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x12, 0x00, 0x28, 0x00, 0x02, 0x00, 0x30, 0x00, 0x32, 0x00, + 0x2c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x7c, 0x28, 0xfd, 0x3b, 0xc8, 0x03, 0x4d, 0x84, 0x9e, 0x30, 0xc4, + 0xf0, 0x62, 0xe2, 0xd3, 0x30, 0x00, 0x02, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x34, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x32, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x32, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x05, 0xee, 0xb6, 0x98, 0x1b, 0xf2, 0x46, 0x5d, + 0x27, 0x50, 0x57, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x80, 0x9f, 0x75, 0x68, 0x0c, 0x07, 0x89, 0x1a, + 0xd7, 0x39, 0x71, 0x13, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool netrlogongetdomaininfo_out_check_common(struct torture_context *tctx, + struct netr_LogonGetDomainInfo *r) +{ + struct GUID guid; + struct dom_sid sid; + struct netr_OneDomainInfo p; + + p = r->out.info->domain_info->primary_domain; + + torture_assert_str_equal(tctx, + p.domainname.string, + "W2K16DOM", "domainname.string"); + torture_assert_str_equal(tctx, + p.dns_domainname.string, + "w2k16.dom.ber.redhat.com.", "dns_domainname.string"); + torture_assert_str_equal(tctx, + p.dns_forestname.string, + "w2k16.dom.ber.redhat.com.", "dns_forestname.string"); + GUID_from_string("fd287c99-c83b-4d03-849e-30c4f062e2d3", &guid); + torture_assert_guid_equal(tctx, + p.domain_guid, + guid, + "domain_guid"); + string_to_sid(&sid, "S-1-5-21-1752539008-445187852-326187479"); + torture_assert_sid_equal(tctx, + p.domain_sid, + &sid, + "domain_sid"); + torture_assert_int_equal(tctx, + p.trust_extension.length, + 0, + "trust_extension.length"); + torture_assert_int_equal(tctx, + p.trust_extension.size, + 0, + "trust_extension.size"); + torture_assert(tctx, + p.trust_extension.info == NULL, + "trust_extension.info"); + /* dummy_string2, dummy_string3, dummy_string4, + dummy_long1, dummy_long2, dummy_long3, dummy_long4 */ + + torture_assert_int_equal(tctx, + r->out.info->domain_info->trusted_domain_count, 2, + "trusted_domain_count"); + + /* trusted domain 0 */ + + p = r->out.info->domain_info->trusted_domains[0]; + + torture_assert_str_equal(tctx, + p.domainname.string, + "W2K12DOM", "domainname.string"); + torture_assert_str_equal(tctx, + p.dns_domainname.string, + "w2k12dom.ber.redhat.com", "dns_domainname.string"); + torture_assert_str_equal(tctx, + p.dns_forestname.string, + NULL, "dns_forestname.string"); + guid = GUID_zero(); + torture_assert_guid_equal(tctx, + p.domain_guid, + guid, + "domain_guid"); + string_to_sid(&sid, "S-1-5-21-2562125317-1564930587-1029132327"); + torture_assert_sid_equal(tctx, + p.domain_sid, + &sid, + "domain_sid"); + torture_assert_int_equal(tctx, + p.trust_extension.length, + 16, + "trust_extension.length"); + torture_assert_int_equal(tctx, + p.trust_extension.size, + 16, + "trust_extension.size"); + torture_assert(tctx, + p.trust_extension.info, + "trust_extension.info"); + torture_assert_int_equal(tctx, + p.trust_extension.info->length, 8, + "trust_extension.info->length"); + torture_assert_int_equal(tctx, + p.trust_extension.info->dummy, 0, + "trust_extension.info->dummy"); + torture_assert_int_equal(tctx, + p.trust_extension.info->size, 8, + "trust_extension.info->size"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.flags, + NETR_TRUST_FLAG_OUTBOUND, + "trust_extension.info->info.flags"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.parent_index, 0, + "trust_extension.info->info.parent_index"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.trust_type, + LSA_TRUST_TYPE_UPLEVEL, + "trust_extension.info->info.trust_type"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.trust_attributes, + LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE | + LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION, + "trust_extension.info->info.trust_attributes"); + /* dummy_string2, dummy_string3, dummy_string4, + dummy_long1, dummy_long2, dummy_long3, dummy_long4 */ + + /* trusted domain 1 */ + + p = r->out.info->domain_info->trusted_domains[1]; + + torture_assert_str_equal(tctx, + p.domainname.string, + "W2K16DOM", "domainname.string"); + torture_assert_str_equal(tctx, + p.dns_domainname.string, + "w2k16.dom.ber.redhat.com", "dns_domainname.string"); + torture_assert_str_equal(tctx, + p.dns_forestname.string, + NULL, "dns_forestname.string"); + GUID_from_string("fd287c99-c83b-4d03-849e-30c4f062e2d3", &guid); + torture_assert_guid_equal(tctx, + p.domain_guid, + guid, + "domain_guid"); + string_to_sid(&sid, "S-1-5-21-1752539008-445187852-326187479"); + torture_assert_sid_equal(tctx, + p.domain_sid, + &sid, + "domain_sid"); + torture_assert_int_equal(tctx, + p.trust_extension.length, + 16, + "trust_extension.length"); + torture_assert_int_equal(tctx, + p.trust_extension.size, + 16, + "trust_extension.size"); + torture_assert(tctx, + p.trust_extension.info, + "trust_extension.info"); + torture_assert_int_equal(tctx, + p.trust_extension.info->length, 8, + "trust_extension.info->length"); + torture_assert_int_equal(tctx, + p.trust_extension.info->dummy, 0, + "trust_extension.info->dummy"); + torture_assert_int_equal(tctx, + p.trust_extension.info->size, 8, + "trust_extension.info->size"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.flags, + NETR_TRUST_FLAG_IN_FOREST | NETR_TRUST_FLAG_TREEROOT | + NETR_TRUST_FLAG_PRIMARY | NETR_TRUST_FLAG_NATIVE, + "trust_extension.info->info.flags"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.parent_index, 0, + "trust_extension.info->info.parent_index"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.trust_type, + LSA_TRUST_TYPE_UPLEVEL, + "trust_extension.info->info.trust_type"); + torture_assert_int_equal(tctx, + p.trust_extension.info->info.trust_attributes, 0, + "trust_extension.info->info.trust_attributes"); + /* dummy_string2, dummy_string3, dummy_string4, + dummy_long1, dummy_long2, dummy_long3, dummy_long4 */ + + torture_assert_int_equal(tctx, + r->out.info->domain_info->lsa_policy.policy_size, 0, + "lsa_policy.policy_size"); + torture_assert(tctx, + r->out.info->domain_info->lsa_policy.policy == NULL, + "lsa_policy.policy"); + torture_assert_str_equal(tctx, + r->out.info->domain_info->dns_hostname.string, NULL, + "dns_hostname"); + /* dummy_string2, dummy_string3, dummy_string4 */ + torture_assert_int_equal(tctx, + r->out.info->domain_info->workstation_flags, 0, + "workstation_flags"); + + return true; +} + +static bool netrlogongetdomaininfo_out_check(struct torture_context *tctx, + struct netr_LogonGetDomainInfo *r) +{ + uint8_t return_authenticator_expected[8] = { 0x81, 0x3f, 0x80, 0x20, 0x4b, 0x4a, 0x18, 0x93 }; + bool ok; + + torture_assert_mem_equal(tctx, + r->out.return_authenticator->cred.data, + return_authenticator_expected, 8, + "return_authenticator.cred.data"); + /* torture_assert_int_equal(tctx, r->out.return_authenticator->timestamp, 0, "return_authenticator.timestamp"); */ + + ok = netrlogongetdomaininfo_out_check_common(tctx, r); + if (!ok) { + return false; + } + + torture_assert_int_equal(tctx, + r->out.info->domain_info->supported_enc_types, + 0x0000001f, + "supported_enc_types"); + /* dummy_long3, dummy_long4 */ + + return true; +} + +static bool netrlogongetdomaininfo_out_check64(struct torture_context *tctx, + struct netr_LogonGetDomainInfo *r) +{ + uint8_t return_authenticator_expected[8] = { 0x5c, 0x69, 0xfe, 0xcf, 0x9b, 0xd5, 0x00, 0xa0 }; + bool ok; + + torture_assert_mem_equal(tctx, + r->out.return_authenticator->cred.data, + return_authenticator_expected, 8, + "return_authenticator.cred.data"); + /* torture_assert_int_equal(tctx, r->out.return_authenticator->timestamp, 0, "return_authenticator.timestamp"); */ + + ok = netrlogongetdomaininfo_out_check_common(tctx, r); + if (!ok) { + return false; + } + + torture_assert_int_equal(tctx, + r->out.info->domain_info->supported_enc_types, + 0xffffffff, + "supported_enc_types"); + /* dummy_long3, dummy_long4 */ + + return true; +} + +static const uint8_t netrlogongetdomaininfo_in_data64[] = { + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x47, 0x00, 0x44, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x44, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x81, 0x19, 0x6e, 0x66, + 0x55, 0x71, 0x21, 0x4b, 0x42, 0x61, 0x82, 0x5d, 0x81, 0x19, 0x6e, 0x66, + 0x55, 0x71, 0x21, 0x4b, 0x42, 0x61, 0x82, 0x5d, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t netrlogongetdomaininfo_out_data64[] = { + 0x5c, 0x69, 0xfe, 0xcf, 0x9b, 0xd5, 0x00, 0xa0, 0x33, 0x68, 0x82, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x7c, 0x28, 0xfd, + 0x3b, 0xc8, 0x03, 0x4d, 0x84, 0x9e, 0x30, 0xc4, 0xf0, 0x62, 0xe2, 0xd3, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x80, 0x9f, 0x75, 0x68, 0x0c, 0x07, 0x89, 0x1a, 0xd7, 0x39, 0x71, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x99, 0x7c, 0x28, 0xfd, 0x3b, 0xc8, 0x03, 0x4d, + 0x84, 0x9e, 0x30, 0xc4, 0xf0, 0x62, 0xe2, 0xd3, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, 0x32, 0x00, 0x44, 0x00, + 0x4f, 0x00, 0x4d, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x05, 0xee, 0xb6, 0x98, 0x1b, 0xf2, 0x46, 0x5d, 0x27, 0x50, 0x57, 0x3d, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, + 0x36, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0x80, 0x9f, 0x75, 0x68, 0x0c, 0x07, 0x89, 0x1a, + 0xd7, 0x39, 0x71, 0x13, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t netrlogongetdomaininfo_in_data64_osversion[] = { + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x65, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x6d, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x6b, 0x00, 0x79, 0x00, 0x77, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x43, 0x00, 0x4c, 0x00, 0x49, 0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x4d, 0x33, 0xf7, 0x0d, 0xe7, 0xeb, 0x15, 0x4b, 0xd4, 0xe9, 0x4b, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x1c, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x2c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x43, 0x00, 0x4c, 0x00, 0x49, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2e, 0x00, + 0x65, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x68, 0x00, 0x2e, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x6b, 0x00, 0x79, 0x00, 0x77, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x69, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xee, 0x42, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb0, 0x4a, 0x02, 0xae, 0x6e, 0x01, 0x00, 0x00, 0x80, 0x4a, 0x16, 0xae, + 0x6e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x96, 0xcc, 0x46, 0xff, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xc8, 0x4a, 0x16, 0xae, 0x6e, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xec, 0x2f, 0x80, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x2f, 0x80, 0x6d, 0x00, 0x00, 0x00, + 0xa0, 0x4a, 0x16, 0xae, 0x6e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x79, 0xad, + 0x6e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf4, 0xed, 0x2f, 0x80, 0x6d, 0x00, 0x00, 0x00, 0x60, 0xe8, 0x2f, 0x80, + 0x6d, 0x00, 0x00, 0x00, 0xe0, 0xea, 0x2f, 0x80, 0x6d, 0x00, 0x00, 0x00, + 0x20, 0xec, 0x2f, 0x80, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x20, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x73, 0x00, 0x65, 0x00 +}; + +static bool netrlogongetdomaininfo_in_check_osversion(struct torture_context *tctx, + struct netr_LogonGetDomainInfo *r) +{ + return true; +} + +struct torture_suite *ndr_netlogon_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "netlogon"); + + torture_suite_add_ndr_pull_fn_test(suite, + netr_ServerReqChallenge, + netrserverreqchallenge_in_data, + NDR_IN, + netrserverreqchallenge_in_check); + torture_suite_add_ndr_pull_fn_test(suite, + netr_ServerReqChallenge, + netrserverreqchallenge_out_data, + NDR_OUT, + netrserverreqchallenge_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, + netr_ServerAuthenticate3, + netrserverauthenticate3_in_data, + NDR_IN, + netrserverauthenticate3_in_check); + torture_suite_add_ndr_pull_fn_test(suite, + netr_ServerAuthenticate3, + netrserverauthenticate3_out_data, + NDR_OUT, + netrserverauthenticate3_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, + netr_LogonSamLogon, + netrlogonsamlogon_w2k_in_data, + NDR_IN, + netrlogonsamlogon_w2k_in_check); +#if 0 + /* samba currently fails to parse a validation level 6 samlogon reply + * from w2k and other servers - gd */ + torture_suite_add_ndr_pull_io_test(suite, + netr_LogonSamLogon, + netrlogonsamlogon_w2k_in_data, + netrlogonsamlogon_w2k_out_data, + netrlogonsamlogon_w2k_out_check); +#endif + torture_suite_add_ndr_pull_fn_test(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data, + NDR_IN, + netrlogongetdomaininfo_in_check); + torture_suite_add_ndr_pull_io_test(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data, + netrlogongetdomaininfo_out_data, + netrlogongetdomaininfo_out_check); + + torture_suite_add_ndr_pull_fn_test_flags(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data64, + NDR_IN, + LIBNDR_FLAG_NDR64, + netrlogongetdomaininfo_in_check); + torture_suite_add_ndr_pull_io_test_flags(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data64, + netrlogongetdomaininfo_out_data64, + LIBNDR_FLAG_NDR64, + netrlogongetdomaininfo_out_check64); + + torture_suite_add_ndr_pull_fn_test_flags(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data64_osversion, + NDR_IN, + LIBNDR_FLAG_NDR64, + netrlogongetdomaininfo_in_check_osversion); +#if 0 + /* currently fails, most likely due to pointer value calculations - gd */ + torture_suite_add_ndr_pullpush_fn_test_flags(suite, + netr_LogonGetDomainInfo, + netrlogongetdomaininfo_in_data64_osversion, + NDR_IN, + LIBNDR_FLAG_NDR64, + netrlogongetdomaininfo_in_check_osversion); +#endif + + return suite; +} diff --git a/source4/torture/ndr/ntlmssp.c b/source4/torture/ndr/ntlmssp.c new file mode 100644 index 0000000..4127ce8 --- /dev/null +++ b/source4/torture/ndr/ntlmssp.c @@ -0,0 +1,296 @@ +/* + Unix SMB/CIFS implementation. + test suite for ntlmssp ndr operations + + Copyright (C) Guenther Deschner 2010,2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_ntlmssp.h" +#include "torture/ndr/proto.h" + +static const uint8_t ntlmssp_NEGOTIATE_MESSAGE_data[] = { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x97, 0x82, 0x08, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb0, 0x1d, + 0x00, 0x00, 0x00, 0x0f +}; + +static bool ntlmssp_NEGOTIATE_MESSAGE_check(struct torture_context *tctx, + struct NEGOTIATE_MESSAGE *r) +{ + torture_assert_str_equal(tctx, r->Signature, "NTLMSSP", "Signature"); + torture_assert_int_equal(tctx, r->MessageType, NtLmNegotiate, "MessageType"); + torture_assert_int_equal(tctx, r->NegotiateFlags, 0xe2088297, "NegotiateFlags"); + torture_assert_int_equal(tctx, r->DomainNameLen, 0, "DomainNameLen"); + torture_assert_int_equal(tctx, r->DomainNameMaxLen, 0, "DomainNameMaxLen"); + torture_assert(tctx, r->DomainName == NULL, "DomainName"); + torture_assert_int_equal(tctx, r->WorkstationLen, 0, "WorkstationLen"); + torture_assert_int_equal(tctx, r->WorkstationMaxLen, 0, "WorkstationMaxLen"); + torture_assert(tctx, r->Workstation == NULL, "Workstation"); + torture_assert_int_equal(tctx, r->Version.version.ProductMajorVersion, NTLMSSP_WINDOWS_MAJOR_VERSION_6, "ProductMajorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductMinorVersion, NTLMSSP_WINDOWS_MINOR_VERSION_1, "ProductMinorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductBuild, 0x1db0, "ProductBuild"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[0], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[1], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[2], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.NTLMRevisionCurrent, NTLMSSP_REVISION_W2K3, "NTLMRevisionCurrent"); + + return true; +} + +static const uint8_t ntlmssp_CHALLENGE_MESSAGE_data[] = { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x38, 0x00, 0x00, 0x00, 0x95, 0x82, 0x89, 0xe2, + 0xed, 0xc8, 0x2b, 0x7d, 0x2e, 0xd7, 0xd0, 0xd9, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x78, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x41, 0x00, + 0x4d, 0x00, 0x42, 0x00, 0x41, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x53, 0x00, + 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x41, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x4d, 0x00, 0x54, 0x00, 0x48, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x45, 0x00, + 0x4e, 0x00, 0x41, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x03, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x2e, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool ntlmssp_CHALLENGE_MESSAGE_check(struct torture_context *tctx, + struct CHALLENGE_MESSAGE *r) +{ + uint8_t chal[8] = { 0xed, 0xc8, 0x2b, 0x7d, 0x2e, 0xd7, 0xd0, 0xd9 }; + uint8_t data[8] = { 0 }; + + torture_assert_str_equal(tctx, r->Signature, "NTLMSSP", "Signature"); + torture_assert_int_equal(tctx, r->MessageType, NtLmChallenge, "MessageType"); + torture_assert_int_equal(tctx, r->TargetNameLen, 10, "TargetNameLen"); + torture_assert_int_equal(tctx, r->TargetNameMaxLen, 10, "TargetNameMaxLen"); + torture_assert_str_equal(tctx, r->TargetName, "SAMBA", "TargetName"); + torture_assert_int_equal(tctx, r->NegotiateFlags, 0xe2898295, "NegotiateFlags"); + torture_assert_mem_equal(tctx, r->ServerChallenge, chal, 8, "ServerChallenge"); + torture_assert_mem_equal(tctx, r->Reserved, data, 8, "Reserved"); + torture_assert_int_equal(tctx, r->TargetInfoLen, 120, "TargetInfoLen"); + torture_assert_int_equal(tctx, r->TargetInfoMaxLen, 120, "TargetInfoMaxLen"); + torture_assert_int_equal(tctx, r->TargetInfo->count, 5, "TargetInfo->count"); + + torture_assert_int_equal(tctx, r->TargetInfo->pair[0].AvId, MsvAvNbDomainName, "AvId"); + torture_assert_int_equal(tctx, r->TargetInfo->pair[0].AvLen, 10, "AvLen"); + torture_assert_str_equal(tctx, r->TargetInfo->pair[0].Value.AvNbDomainName, "SAMBA", "AvNbDomainName"); + + torture_assert_int_equal(tctx, r->TargetInfo->pair[1].AvId, MsvAvNbComputerName, "AvId"); + torture_assert_int_equal(tctx, r->TargetInfo->pair[1].AvLen, 16, "AvLen"); + torture_assert_str_equal(tctx, r->TargetInfo->pair[1].Value.AvNbComputerName, "MTHELENA", "AvNbComputerName"); + + torture_assert_int_equal(tctx, r->TargetInfo->pair[2].AvId, MsvAvDnsDomainName, "AvId"); + torture_assert_int_equal(tctx, r->TargetInfo->pair[2].AvLen, 28, "AvLen"); + torture_assert_str_equal(tctx, r->TargetInfo->pair[2].Value.AvDnsDomainName, "ber.redhat.com", "AvDnsDomainName"); + + torture_assert_int_equal(tctx, r->TargetInfo->pair[3].AvId, MsvAvDnsComputerName, "AvId"); + torture_assert_int_equal(tctx, r->TargetInfo->pair[3].AvLen, 46, "AvLen"); + torture_assert_str_equal(tctx, r->TargetInfo->pair[3].Value.AvDnsComputerName, "mthelena.ber.redhat.com", "AvDnsComputerName"); + + torture_assert_int_equal(tctx, r->TargetInfo->pair[4].AvId, MsvAvEOL, "AvId"); + torture_assert_int_equal(tctx, r->TargetInfo->pair[4].AvLen, 0, "AvLen"); + + torture_assert_int_equal(tctx, r->Version.version.ProductMajorVersion, NTLMSSP_WINDOWS_MAJOR_VERSION_6, "ProductMajorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductMinorVersion, NTLMSSP_WINDOWS_MINOR_VERSION_1, "ProductMinorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductBuild, 0, "ProductBuild"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[0], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[1], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[2], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.NTLMRevisionCurrent, NTLMSSP_REVISION_W2K3, "NTLMRevisionCurrent"); + + return true; +} + +static const uint8_t ntlmssp_AUTHENTICATE_MESSAGE_data[] = { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x0e, 0x01, + 0xa4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xb2, 0x01, 0x00, 0x00, + 0x15, 0x82, 0x88, 0xe2, 0x06, 0x01, 0xb0, 0x1d, 0x00, 0x00, 0x00, 0x0f, + 0x50, 0xe2, 0xb2, 0xa7, 0xf5, 0x83, 0x3e, 0xda, 0x71, 0xa7, 0xe8, 0x6e, + 0x95, 0x1e, 0x3a, 0x57, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, + 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xcf, 0xfb, 0x39, + 0x5a, 0xb3, 0x4c, 0x58, 0x86, 0x35, 0xa3, 0xe7, 0x1e, 0x00, 0x98, 0x43, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x79, 0x02, 0x77, + 0x1e, 0x54, 0xcb, 0x01, 0x3c, 0x21, 0x0a, 0xe9, 0xde, 0x61, 0xc0, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x53, 0x00, 0x41, 0x00, + 0x4d, 0x00, 0x42, 0x00, 0x41, 0x00, 0x01, 0x00, 0x10, 0x00, 0x4d, 0x00, + 0x54, 0x00, 0x48, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, + 0x41, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, + 0x2e, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x08, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0a, 0xfd, 0x3b, 0x2c, + 0xad, 0x43, 0x46, 0x8b, 0x49, 0x01, 0x6c, 0xa5, 0xf3, 0xbc, 0xd2, 0x13, + 0xbb, 0x70, 0xe2, 0x65, 0x96, 0xba, 0x0d, 0x8d, 0x5d, 0x31, 0xe6, 0x47, + 0x94, 0x61, 0xed, 0x28, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x1a, 0x00, 0x63, 0x00, 0x69, 0x00, 0x66, 0x00, 0x73, 0x00, + 0x2f, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xa4, 0x23, 0xd4, 0x5c, 0x16, 0x52, 0x8d, 0x56, 0x34, 0x2d, + 0x1c, 0xff, 0x86, 0x17, 0xc9, 0x4f +}; + +static bool ntlmssp_AUTHENTICATE_MESSAGE_check(struct torture_context *tctx, + struct AUTHENTICATE_MESSAGE *r) +{ + uint8_t lm_challenge_response[24] = { 0 }; + struct NTLMv2_RESPONSE v2; + struct AV_PAIR_LIST AvPairs; + uint8_t Response[16] = { + 0x38, 0xcf, 0xfb, 0x39, 0x5a, 0xb3, 0x4c, 0x58, + 0x86, 0x35, 0xa3, 0xe7, 0x1e, 0x00, 0x98, 0x43 + }; + uint8_t ChallengeFromClient[8] = { + 0x3c, 0x21, 0x0a, 0xe9, 0xde, 0x61, 0xc0, 0x7e + }; + uint8_t MachineId[32] = { + 0x0a, 0xfd, 0x3b, 0x2c, 0xad, 0x43, 0x46, 0x8b, + 0x49, 0x01, 0x6c, 0xa5, 0xf3, 0xbc, 0xd2, 0x13, + 0xbb, 0x70, 0xe2, 0x65, 0x96, 0xba, 0x0d, 0x8d, + 0x5d, 0x31, 0xe6, 0x47, 0x94, 0x61, 0xed, 0x28 + }; + uint8_t EncryptedRandomSessionKey[16] = { + 0xA4, 0x23, 0xD4, 0x5C, 0x16, 0x52, 0x8D, 0x56, + 0x34, 0x2D, 0x1C, 0xFF, 0x86, 0x17, 0xC9, 0x4F + }; + + torture_assert_str_equal(tctx, r->Signature, "NTLMSSP", "Signature"); + torture_assert_int_equal(tctx, r->MessageType, NtLmAuthenticate, "MessageType"); + torture_assert_int_equal(tctx, r->LmChallengeResponseLen, 24, "LmChallengeResponseLen"); + torture_assert_int_equal(tctx, r->LmChallengeResponseMaxLen, 24, "LmChallengeResponseMaxLen"); + torture_assert_mem_equal(tctx, r->LmChallengeResponse->v1.Response, lm_challenge_response, 24, "LmChallengeResponse"); + + torture_assert_int_equal(tctx, r->NtChallengeResponseLen, 270, "NtChallengeResponseLen"); + torture_assert_int_equal(tctx, r->NtChallengeResponseMaxLen, 270, "NtChallengeResponseMaxLen"); + + v2 = r->NtChallengeResponse->v2; + + torture_assert_mem_equal(tctx, v2.Response, Response, 16, "v2.Response"); + torture_assert_int_equal(tctx, v2.Challenge.RespType, 1, "RespType"); + torture_assert_int_equal(tctx, v2.Challenge.HiRespType, 1, "HiRespType"); + torture_assert_int_equal(tctx, v2.Challenge.Reserved1, 0, "Reserved1"); + torture_assert_int_equal(tctx, v2.Challenge.Reserved2, 0, "Reserved2"); + /* TimeStamp : Tue Sep 14 17:06:53 2010 CEST */ + torture_assert_mem_equal(tctx, v2.Challenge.ChallengeFromClient, ChallengeFromClient, 8, "v2.Challenge.ChallengeFromClient"); + torture_assert_int_equal(tctx, v2.Challenge.Reserved3, 0, "Reserved3"); + + AvPairs = v2.Challenge.AvPairs; + + torture_assert_int_equal(tctx, AvPairs.count, 8, "AvPairs.count"); + + torture_assert_int_equal(tctx, AvPairs.pair[0].AvId, MsvAvNbDomainName, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[0].AvLen, 10, "AvLen"); + torture_assert_str_equal(tctx, AvPairs.pair[0].Value.AvNbDomainName, "SAMBA", "Value.AvNbDomainName"); + + torture_assert_int_equal(tctx, AvPairs.pair[1].AvId, MsvAvNbComputerName, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[1].AvLen, 16, "AvLen"); + torture_assert_str_equal(tctx, AvPairs.pair[1].Value.AvNbComputerName, "MTHELENA", "Value.AvNbComputerName"); + + torture_assert_int_equal(tctx, AvPairs.pair[2].AvId, MsvAvDnsDomainName, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[2].AvLen, 28, "AvLen"); + torture_assert_str_equal(tctx, AvPairs.pair[2].Value.AvDnsDomainName, "ber.redhat.com", "Value.AvDnsDomainName"); + + torture_assert_int_equal(tctx, AvPairs.pair[3].AvId, MsvAvDnsComputerName, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[3].AvLen, 46, "AvLen"); + torture_assert_str_equal(tctx, AvPairs.pair[3].Value.AvDnsComputerName, "mthelena.ber.redhat.com", "Value.AvDnsComputerName"); + + torture_assert_int_equal(tctx, AvPairs.pair[4].AvId, MsvAvSingleHost, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[4].AvLen, 48, "AvLen"); + torture_assert_int_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.Size, 48, "Value.AvSingleHost.Size"); + torture_assert_int_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.Z4, 0, "Value.AvSingleHost.Z4"); + torture_assert_int_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.token_info.Flags, 0, "Value.AvSingleHost.token_info.Flags"); + torture_assert_int_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.token_info.TokenIL, 0x00003000, "Value.AvSingleHost.token_info.TokenIL"); + torture_assert_mem_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.token_info.MachineId, MachineId, 32, "Value.AvSingleHost.token_info.MachineId"); + torture_assert_int_equal(tctx, AvPairs.pair[4].Value.AvSingleHost.remaining.length, 0, "Value.AvSingleHost.remaining.length"); + + torture_assert_int_equal(tctx, AvPairs.pair[5].AvId, MsvChannelBindings, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[5].AvLen, 16, "AvLen"); + torture_assert_mem_equal(tctx, AvPairs.pair[5].Value.ChannelBindings, lm_challenge_response, 16, "Value.ChannelBindings"); + + torture_assert_int_equal(tctx, AvPairs.pair[6].AvId, MsvAvTargetName, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[6].AvLen, 26, "AvLen"); + torture_assert_str_equal(tctx, AvPairs.pair[6].Value.AvTargetName, "cifs/mthelena", "Value.AvTargetName"); + + torture_assert_int_equal(tctx, AvPairs.pair[7].AvId, MsvAvEOL, "AvId"); + torture_assert_int_equal(tctx, AvPairs.pair[7].AvLen, 0, "AvLen"); + + torture_assert_int_equal(tctx, r->DomainNameLen, 14, "DomainNameLen"); + torture_assert_int_equal(tctx, r->DomainNameMaxLen, 14, "DomainNameMaxLen"); + torture_assert_str_equal(tctx, r->DomainName, "W2K8DOM", "DomainName"); + + torture_assert_int_equal(tctx, r->UserNameLen, 26, "UserNameLen"); + torture_assert_int_equal(tctx, r->UserNameMaxLen, 26, "UserNameMaxLen"); + torture_assert_str_equal(tctx, r->UserName, "Administrator", "UserName"); + + torture_assert_int_equal(tctx, r->WorkstationLen, 12, "WorkstationLen"); + torture_assert_int_equal(tctx, r->WorkstationMaxLen, 12, "WorkstationMaxLen"); + torture_assert_str_equal(tctx, r->Workstation, "W2K8R2", "Workstation"); + + torture_assert_int_equal(tctx, r->EncryptedRandomSessionKeyLen, 16, "EncryptedRandomSessionKeyLen"); + torture_assert_int_equal(tctx, r->EncryptedRandomSessionKeyMaxLen, 16, "EncryptedRandomSessionKeyMaxLen"); + torture_assert_mem_equal(tctx, r->EncryptedRandomSessionKey->data, EncryptedRandomSessionKey, 16, "EncryptedRandomSessionKeyMaxLen"); + + torture_assert_int_equal(tctx, r->NegotiateFlags, 0xe2888215, "NegotiateFlags"); + + torture_assert_int_equal(tctx, r->Version.version.ProductMajorVersion, NTLMSSP_WINDOWS_MAJOR_VERSION_6, "ProductMajorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductMinorVersion, NTLMSSP_WINDOWS_MINOR_VERSION_1, "ProductMinorVersion"); + torture_assert_int_equal(tctx, r->Version.version.ProductBuild, 0x1db0, "ProductBuild"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[0], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[1], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.Reserved[2], 0x00, "Reserved"); + torture_assert_int_equal(tctx, r->Version.version.NTLMRevisionCurrent, NTLMSSP_REVISION_W2K3, "NTLMRevisionCurrent"); + + return true; +} + +struct torture_suite *ndr_ntlmssp_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ntlmssp"); + + torture_suite_add_ndr_pull_test(suite, NEGOTIATE_MESSAGE, ntlmssp_NEGOTIATE_MESSAGE_data, ntlmssp_NEGOTIATE_MESSAGE_check); + torture_suite_add_ndr_pull_test(suite, CHALLENGE_MESSAGE, ntlmssp_CHALLENGE_MESSAGE_data, ntlmssp_CHALLENGE_MESSAGE_check); + torture_suite_add_ndr_pull_test(suite, AUTHENTICATE_MESSAGE, ntlmssp_AUTHENTICATE_MESSAGE_data, ntlmssp_AUTHENTICATE_MESSAGE_check); + + torture_suite_add_ndr_pull_validate_test(suite, + NEGOTIATE_MESSAGE, + ntlmssp_NEGOTIATE_MESSAGE_data, + ntlmssp_NEGOTIATE_MESSAGE_check); + + torture_suite_add_ndr_pull_validate_test(suite, + CHALLENGE_MESSAGE, + ntlmssp_CHALLENGE_MESSAGE_data, + ntlmssp_CHALLENGE_MESSAGE_check); + + return suite; +} diff --git a/source4/torture/ndr/ntprinting.c b/source4/torture/ndr/ntprinting.c new file mode 100644 index 0000000..d5e7e90 --- /dev/null +++ b/source4/torture/ndr/ntprinting.c @@ -0,0 +1,655 @@ +/* + Unix SMB/CIFS implementation. + test suite for ntprinting ndr operations + + Copyright (C) Guenther Deschner 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_ntprinting.h" +#include "torture/ndr/proto.h" +#include "param/param.h" + +static const uint8_t ntprinting_printer_data[] = { + 0x48, 0x10, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x0e, 0x03, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x13, 0xb8, 0x4e, 0x00, 0x4b, 0x79, 0x6f, + 0x63, 0x65, 0x72, 0x61, 0x2d, 0x35, 0x30, 0x30, 0x00, 0x6b, 0x79, 0x6f, + 0x63, 0x65, 0x72, 0x61, 0x2d, 0x35, 0x30, 0x30, 0x00, 0x53, 0x61, 0x6d, + 0x62, 0x61, 0x20, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x50, + 0x6f, 0x72, 0x74, 0x00, 0x6b, 0x79, 0x6f, 0x63, 0x65, 0x72, 0x61, 0x2d, + 0x35, 0x30, 0x30, 0x00, 0x4b, 0x79, 0x6f, 0x63, 0x65, 0x72, 0x61, 0x20, + 0x54, 0x61, 0x73, 0x6b, 0x41, 0x6c, 0x66, 0x61, 0x20, 0x35, 0x30, 0x30, + 0x63, 0x69, 0x00, 0x62, 0x75, 0x6c, 0x6c, 0x70, 0x65, 0x6e, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x52, 0x41, 0x57, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x5c, 0x69, 0x72, 0x6f, 0x62, + 0x6f, 0x74, 0x5c, 0x4b, 0x79, 0x6f, 0x63, 0x65, 0x72, 0x61, 0x2d, 0x35, + 0x30, 0x30, 0x00, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x00, 0x01, 0x04, + 0x00, 0x06, 0xdc, 0x00, 0x60, 0x08, 0x01, 0x00, 0x01, 0x00, 0xea, 0x0a, + 0x6f, 0x08, 0x64, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x58, 0x02, 0x02, 0x00, + 0x01, 0x00, 0x58, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x53, 0xff, + 0x81, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x08, + 0x00, 0x00, 0x50, 0x52, 0x49, 0x56, 0xe2, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x27, 0x10, 0x27, 0x10, 0x27, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x54, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x10, 0x00, 0x5c, 0x4b, + 0x03, 0x00, 0x68, 0x43, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x75, + 0xbf, 0xbb, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x53, 0x4d, + 0x54, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 0x03, 0x6b, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x2d, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x4a, 0x43, + 0x4c, 0x54, 0x72, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x4d, 0x65, + 0x64, 0x69, 0x75, 0x6d, 0x00, 0x4a, 0x43, 0x4c, 0x48, 0x61, 0x6c, 0x66, + 0x74, 0x6f, 0x6e, 0x65, 0x00, 0x47, 0x72, 0x61, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x00, 0x4a, 0x43, 0x4c, 0x52, 0x65, 0x64, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x47, + 0x72, 0x65, 0x65, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x00, 0x4e, 0x6f, + 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x42, 0x6c, 0x75, 0x65, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, + 0x48, 0x75, 0x65, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00, 0x4e, 0x6f, + 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x48, 0x75, 0x65, 0x52, 0x65, 0x64, + 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x48, 0x75, 0x65, + 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, + 0x4a, 0x43, 0x4c, 0x48, 0x75, 0x65, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x00, + 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x48, 0x75, 0x65, 0x43, + 0x79, 0x61, 0x6e, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, + 0x48, 0x75, 0x65, 0x42, 0x6c, 0x75, 0x65, 0x00, 0x4e, 0x6f, 0x6e, 0x65, + 0x00, 0x4a, 0x43, 0x4c, 0x48, 0x75, 0x65, 0x4d, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x61, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x4c, + 0x69, 0x67, 0x68, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x61, 0x6d, 0x6d, + 0x61, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x61, 0x73, 0x74, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4a, 0x43, 0x4c, + 0x53, 0x61, 0x74, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x4e, + 0x6f, 0x6e, 0x65, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x00, 0x36, 0x30, 0x30, 0x64, 0x70, 0x69, 0x00, 0x4b, 0x43, + 0x45, 0x63, 0x6f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x4f, 0x66, 0x66, + 0x00, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x00, + 0x43, 0x4d, 0x59, 0x4b, 0x00, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x72, 0x65, + 0x70, 0x72, 0x6f, 0x64, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x43, 0x49, 0x45, + 0x00, 0x50, 0x72, 0x6e, 0x44, 0x65, 0x66, 0x00, 0x50, 0x61, 0x67, 0x65, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x00, 0x4f, 0x6e, 0x00, 0x50, 0x61, + 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x00, 0x4c, 0x65, 0x74, 0x74, 0x65, + 0x72, 0x00, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x64, 0x67, + 0x65, 0x00, 0x00, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x6c, 0x6f, 0x74, + 0x00, 0x2a, 0x55, 0x73, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x54, 0x72, 0x61, + 0x79, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x4d, 0x65, 0x64, 0x69, 0x61, + 0x54, 0x79, 0x70, 0x65, 0x00, 0x41, 0x75, 0x74, 0x6f, 0x00, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x00, 0x4e, 0x6f, 0x6e, 0x65, + 0x00, 0x4b, 0x43, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x00, 0x4e, 0x6f, + 0x6e, 0x65, 0x00, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4b, 0x43, 0x50, 0x75, + 0x6e, 0x63, 0x68, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4b, 0x43, 0x42, + 0x6f, 0x6f, 0x6b, 0x6c, 0x65, 0x74, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, + 0x4b, 0x43, 0x46, 0x6f, 0x6c, 0x64, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, + 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x00, 0x46, 0x61, 0x6c, 0x73, 0x65, + 0x00, 0x4a, 0x6f, 0x67, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x44, 0x75, + 0x70, 0x6c, 0x65, 0x78, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4b, 0x43, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x00, 0x50, 0x72, 0x6e, 0x44, + 0x65, 0x66, 0x00, 0x4b, 0x6d, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x00, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x4b, + 0x43, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x00, 0x63, 0x75, 0x70, 0x73, 0x4a, 0x6f, 0x62, + 0x48, 0x6f, 0x6c, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x00, 0x6e, 0x6f, + 0x2d, 0x68, 0x6f, 0x6c, 0x64, 0x00, 0x63, 0x75, 0x70, 0x73, 0x4a, 0x6f, + 0x62, 0x53, 0x68, 0x65, 0x65, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x00, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x63, 0x75, 0x70, 0x73, 0x4a, 0x6f, + 0x62, 0x53, 0x68, 0x65, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x00, 0x6e, + 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x53, 0x50, 0x55, 0x43, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x72, 0x69, + 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, + 0x61, 0x5c, 0x54, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, 0x53, 0x69, + 0x7a, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xce, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, + 0x5c, 0x54, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x00, 0x03, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xce, + 0x00, 0x43, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x4c, + 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, + 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x61, + 0x00, 0x73, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, + 0x00, 0x20, 0x00, 0x33, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x43, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x34, 0x00, 0x00, 0x00, 0x4c, + 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x00, 0x70, 0x00, 0x61, + 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, + 0x00, 0x79, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x5c, 0x54, + 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, 0x4d, 0x61, 0x70, 0x53, 0x69, + 0x7a, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, + 0x5c, 0x54, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, 0x4d, 0x61, 0x70, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x50, 0x46, 0x37, + 0x30, 0x30, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x37, 0x30, + 0x30, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x37, 0x30, 0x30, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x37, 0x30, 0x30, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x46, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, + 0x61, 0x74, 0x61, 0x5c, 0x54, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, + 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x44, + 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x5c, 0x54, 0x72, + 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6d, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, + 0x64, 0x00, 0x03, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x50, 0x46, + 0x37, 0x30, 0x30, 0x41, 0x00, 0x00, 0x50, 0x46, 0x37, 0x30, 0x30, 0x42, + 0x00, 0x00, 0x50, 0x46, 0x37, 0x30, 0x30, 0x43, 0x00, 0x00, 0x50, 0x46, + 0x37, 0x30, 0x30, 0x44, 0x00, 0x00, 0x4d, 0x46, 0x31, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, 0x6c, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, 0x6c, 0x65, 0x72, 0x5c, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x49, 0x00, 0x52, 0x00, 0x4f, + 0x00, 0x42, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, 0x6c, 0x65, 0x72, 0x5c, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x49, + 0x00, 0x52, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, 0x6c, + 0x65, 0x72, 0x5c, 0x75, 0x4e, 0x43, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x49, + 0x00, 0x52, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x5c, + 0x00, 0x6b, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, + 0x00, 0x61, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t ntprinting_printer_data_latin1[] = { + 0x48, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x94, 0x46, 0x50, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x94, 0xee, 0xb9, 0x50, 0x00, 0x53, 0x30, 0x42, + 0x43, 0x00, 0x53, 0x30, 0x42, 0x43, 0x00, 0x53, + 0x61, 0x6d, 0x62, 0x61, 0x20, 0x50, 0x72, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x20, 0x50, 0x6f, 0x72, + 0x74, 0x00, 0x48, 0x50, 0x20, 0x44, 0x65, 0x73, + 0x69, 0x67, 0x6e, 0x6a, 0x65, 0x74, 0x20, 0x38, + 0x30, 0x30, 0x50, 0x53, 0x20, 0x34, 0x32, 0x20, + 0x62, 0x79, 0x20, 0x48, 0x50, 0x00, 0x22, 0x20, + 0x53, 0x41, 0x4c, 0x41, 0x20, 0x44, 0x41, 0x20, + 0x52, 0x45, 0x43, 0x45, 0x50, 0xc7, 0xc3, 0x4f, + 0x20, 0x44, 0x41, 0x20, 0x43, 0x4f, 0x4e, 0x53, + 0x54, 0x52, 0x55, 0xc7, 0xc3, 0x4f, 0x20, 0x2d, + 0x20, 0x52, 0x41, 0x4e, 0x44, 0x30, 0x20, 0x4c, + 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x00, 0x55, + 0x54, 0x47, 0x43, 0x41, 0x20, 0x00, 0x00, 0x77, + 0x69, 0x6e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, + 0x52, 0x41, 0x57, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x5c, 0x5c, 0x4c, 0x4f, 0x43, 0x41, 0x4c, + 0x48, 0x4f, 0x53, 0x54, 0x5c, 0x53, 0x30, 0x42, + 0x43, 0x00, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x00, 0x01, 0x04, 0x00, 0x04, 0xdc, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x64, 0x00, 0x01, 0x00, 0x0f, 0x00, 0xfc, + 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x47, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, + 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, + 0x44, 0x61, 0x74, 0x61, 0x5c, 0x44, 0x72, 0x76, + 0x50, 0x61, 0x70, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x73, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x61, + 0xc2, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x50, + 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x72, + 0x69, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, + 0x5c, 0x44, 0x72, 0x76, 0x44, 0x65, 0x76, 0x4d, + 0x6f, 0x64, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x42, 0x02, 0x00, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x36, 0x00, + 0x36, 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x33, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, 0x31, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x37, 0x00, 0x33, 0x00, + 0x37, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x35, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x35, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x36, 0x00, + 0x32, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x32, 0x00, + 0x31, 0x00, 0x36, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x36, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x35, 0x00, + 0x35, 0x00, 0x33, 0x00, 0x35, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x36, 0x00, 0x33, 0x00, 0x31, 0x00, + 0x37, 0x00, 0x32, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x31, 0x00, + 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x37, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x30, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x3b, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, + 0x3b, 0x00, 0x32, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, + 0x3b, 0x00, 0x32, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, + 0x3b, 0x00, 0x32, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, + 0x3b, 0x00, 0x32, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, + 0x3b, 0x00, 0x32, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x44, + 0x61, 0x74, 0x61, 0x5c, 0x44, 0x72, 0x76, 0x45, + 0x57, 0x53, 0x49, 0x50, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, + 0x6f, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, 0x6c, + 0x65, 0x72, 0x5c, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x53, + 0x00, 0x30, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x73, 0x53, + 0x70, 0x6f, 0x6f, 0x6c, 0x65, 0x72, 0x5c, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x53, 0x00, 0x36, 0x00, 0x30, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x50, 0x00, 0x53, 0x00, + 0x36, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, 0x6f, 0x6f, + 0x6c, 0x65, 0x72, 0x5c, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x53, 0x00, 0x36, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x30, 0x00, 0x50, 0x00, + 0x53, 0x00, 0x36, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x44, 0x73, 0x53, 0x70, + 0x6f, 0x6f, 0x6c, 0x65, 0x72, 0x5c, 0x75, 0x4e, + 0x43, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x5c, 0x00, + 0x5c, 0x00, 0x53, 0x00, 0x36, 0x00, 0x30, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x50, 0x00, 0x53, 0x00, + 0x36, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x53, 0x00, + 0x30, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool ntprinting_printer_check(struct torture_context *tctx, + struct ntprinting_printer *r) +{ + torture_assert_int_equal(tctx, r->info.attributes, 0x00081048, "attributes"); + torture_assert_int_equal(tctx, r->info.priority, 1, "priority"); + torture_assert_int_equal(tctx, r->info.default_priority, 1, "default_priority"); + torture_assert_int_equal(tctx, r->info.starttime, 0, "startime"); + torture_assert_int_equal(tctx, r->info.untiltime, 0, "untiltime"); + torture_assert_int_equal(tctx, r->info.status, 0, "status"); + torture_assert_int_equal(tctx, r->info.cjobs, 5, "cjobs"); + torture_assert_int_equal(tctx, r->info.averageppm, 0, "averageppm"); + torture_assert_int_equal(tctx, r->info.changeid, 0x09030e9d, "changeid"); + torture_assert_int_equal(tctx, r->info.c_setprinter, 0, "c_setprinter"); + torture_assert_int_equal(tctx, r->info.setuptime, 0x4eb81324, "setuptime"); + torture_assert_str_equal(tctx, r->info.servername, "", "servername"); + torture_assert_str_equal(tctx, r->info.printername, "Kyocera-500", "printername"); + torture_assert_str_equal(tctx, r->info.sharename, "kyocera-500", "sharename"); + torture_assert_str_equal(tctx, r->info.portname, "Samba Printer Port", "portname"); + torture_assert_str_equal(tctx, r->info.drivername, "kyocera-500", "drivername"); + torture_assert_str_equal(tctx, r->info.comment, "Kyocera TaskAlfa 500ci", "comment"); + torture_assert_str_equal(tctx, r->info.location, "bullpen", "comment"); + torture_assert_str_equal(tctx, r->info.sepfile, "", "sepfile"); + torture_assert_str_equal(tctx, r->info.printprocessor, "winprint", "printprocessor"); + torture_assert_str_equal(tctx, r->info.datatype, "RAW", "datatype"); + torture_assert_str_equal(tctx, r->info.parameters, "", "parameters"); + + torture_assert(tctx, r->devmode, "devmode"); + torture_assert_str_equal(tctx, r->devmode->devicename, "\\\\irobot\\Kyocera-500", "devicename"); + torture_assert_str_equal(tctx, r->devmode->formname, "Letter", "formname"); + torture_assert_int_equal(tctx, r->devmode->specversion, 0x0401, "specversion"); + torture_assert_int_equal(tctx, r->devmode->driverversion, 0x0600, "driverversion"); + torture_assert_int_equal(tctx, r->devmode->size, 0x00dc, "size"); + torture_assert_int_equal(tctx, r->devmode->driverextra, 0x0860, "driverextra"); + torture_assert_int_equal(tctx, r->devmode->orientation, 1, "orientation"); + torture_assert_int_equal(tctx, r->devmode->papersize, 1, "papersize"); + torture_assert_int_equal(tctx, r->devmode->paperlength, 0x0aea, "paperlength"); + torture_assert_int_equal(tctx, r->devmode->paperwidth, 0x086f, "paperwidth"); + torture_assert_int_equal(tctx, r->devmode->scale, 0x0064, "scale"); + torture_assert_int_equal(tctx, r->devmode->copies, 1, "copies"); + torture_assert_int_equal(tctx, r->devmode->defaultsource, 0x000f, "defaultsource"); + torture_assert_int_equal(tctx, r->devmode->printquality, 0x0258, "printquality"); + torture_assert_int_equal(tctx, r->devmode->color, 2, "color"); + torture_assert_int_equal(tctx, r->devmode->duplex, 1, "duplex"); + torture_assert_int_equal(tctx, r->devmode->yresolution, 0x0258, "yresolution"); + torture_assert_int_equal(tctx, r->devmode->ttoption, 2, "ttoption"); + torture_assert_int_equal(tctx, r->devmode->collate, 1, "collate"); + torture_assert_int_equal(tctx, r->devmode->logpixels, 0, "logpixels"); + torture_assert_int_equal(tctx, r->devmode->fields, 0x0381ff53, "fields"); + torture_assert_int_equal(tctx, r->devmode->bitsperpel, 0, "bitsperpel"); + torture_assert_int_equal(tctx, r->devmode->pelswidth, 0, "pelswidth"); + torture_assert_int_equal(tctx, r->devmode->pelsheight, 0, "pelsheight"); + torture_assert_int_equal(tctx, r->devmode->displayflags, 1, "displayflags"); + torture_assert_int_equal(tctx, r->devmode->displayfrequency, 0, "displayfrequency"); + torture_assert_int_equal(tctx, r->devmode->icmmethod, 1, "icmmethod"); + torture_assert_int_equal(tctx, r->devmode->icmintent, 2, "icmintent"); + torture_assert_int_equal(tctx, r->devmode->mediatype, 0x00000101, "mediatype"); + torture_assert_int_equal(tctx, r->devmode->dithertype, 0, "dithertype"); + torture_assert_int_equal(tctx, r->devmode->reserved1, 0, "reserved1"); + torture_assert_int_equal(tctx, r->devmode->reserved2, 0, "reserved2"); + torture_assert_int_equal(tctx, r->devmode->panningwidth, 0, "panningwidth"); + torture_assert_int_equal(tctx, r->devmode->panningheight, 0, "panningheight"); + + torture_assert(tctx, r->devmode->nt_dev_private, "nt_dev_private"); + torture_assert_int_equal(tctx, r->devmode->nt_dev_private->length, 2144, "nt_dev_private->length"); + + torture_assert_int_equal(tctx, r->count, 11, "count"); + + torture_assert_int_equal(tctx, r->printer_data[0].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[0].name, "PrinterDriverData", "name"); + torture_assert_int_equal(tctx, r->printer_data[0].type, 0, "type"); + torture_assert_int_equal(tctx, r->printer_data[0].data.length, 0, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[1].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[1].name, "PrinterDriverData\\TrayFormSize", "name"); + torture_assert_int_equal(tctx, r->printer_data[1].type, 4, "type"); + torture_assert_int_equal(tctx, r->printer_data[1].data.length, 4, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[2].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[2].name, "PrinterDriverData\\TrayFormTable", "name"); + torture_assert_int_equal(tctx, r->printer_data[2].type, 3, "type"); + torture_assert_int_equal(tctx, r->printer_data[2].data.length, 206, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[3].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[3].name, "PrinterDriverData\\TrayFormMapSize", "name"); + torture_assert_int_equal(tctx, r->printer_data[3].type, 4, "type"); + torture_assert_int_equal(tctx, r->printer_data[3].data.length, 4, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[4].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[4].name, "PrinterDriverData\\TrayFormMap", "name"); + torture_assert_int_equal(tctx, r->printer_data[4].type, 3, "type"); + torture_assert_int_equal(tctx, r->printer_data[4].data.length, 57, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[5].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[5].name, "PrinterDriverData\\TrayFormKeywordSize", "name"); + torture_assert_int_equal(tctx, r->printer_data[5].type, 4, "type"); + torture_assert_int_equal(tctx, r->printer_data[5].data.length, 4, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[6].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[6].name, "PrinterDriverData\\TrayFormKeyword", "name"); + torture_assert_int_equal(tctx, r->printer_data[6].type, 3, "type"); + torture_assert_int_equal(tctx, r->printer_data[6].data.length, 38, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[7].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[7].name, "DsSpooler", "name"); + torture_assert_int_equal(tctx, r->printer_data[7].type, 0, "type"); + torture_assert_int_equal(tctx, r->printer_data[7].data.length, 0, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[8].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[8].name, "DsSpooler\\serverName", "name"); + torture_assert_int_equal(tctx, r->printer_data[8].type, 1, "type"); + torture_assert_int_equal(tctx, r->printer_data[8].data.length, 14, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[9].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[9].name, "DsSpooler\\shortServerName", "name"); + torture_assert_int_equal(tctx, r->printer_data[9].type, 1, "type"); + torture_assert_int_equal(tctx, r->printer_data[9].data.length, 14, "data.length"); + + torture_assert_int_equal(tctx, r->printer_data[10].ptr, 1, "ptr"); + torture_assert_str_equal(tctx, r->printer_data[10].name, "DsSpooler\\uNCName", "name"); + torture_assert_int_equal(tctx, r->printer_data[10].type, 1, "type"); + torture_assert_int_equal(tctx, r->printer_data[10].data.length, 42, "data.length"); + + return true; +} + +static bool ntprinting_printer_latin1_check(struct torture_context *tctx) +{ + enum ndr_err_code ndr_err; + struct ntprinting_printer r; + DATA_BLOB blob; + bool ok; + + ok = lpcfg_do_global_parameter(tctx->lp_ctx, "dos charset", "CP1252"); + if (!ok) { + torture_comment(tctx, "Could not set 'dos charset' option.\n"); + return false; + } + reload_charcnv(tctx->lp_ctx); + + ZERO_STRUCT(r); + r.info.string_flags = LIBNDR_FLAG_STR_ASCII; + + blob = data_blob_const(ntprinting_printer_data_latin1, + sizeof(ntprinting_printer_data_latin1)); + + ndr_err = ndr_pull_struct_blob(&blob, tctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_ntprinting_printer); + + torture_assert_ndr_success(tctx, + ndr_err, + "ndr_pull_ntprinting_printer"); +#if 0 + NDR_PRINT_DEBUG(1, ntprinting_printer, &r); +#endif + torture_assert_str_equal(tctx, + r.info.printername, + "S0BC", + "printername"); + /* latin1 encoding check */ + torture_assert_str_equal(tctx, + r.info.comment, + "\" SALA DA RECEPÇÃO DA CONSTRUÇÃO - RAND0 LOCATIO", + "comment"); + torture_assert_str_equal(tctx, + r.info.location, + "UTGCA ", + "location"); + + return true; +} + +struct torture_suite *ndr_ntprinting_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ntprinting"); + + torture_suite_add_simple_test(suite, + "ntprinting latin1 check", + ntprinting_printer_latin1_check); + + torture_suite_add_ndr_pull_test(suite, + ntprinting_printer, + ntprinting_printer_data, + ntprinting_printer_check); + + /* pullpush not working atm. + torture_suite_add_ndr_pull_validate_test(suite, + ntprinting_printer, + data_blob_const(ntprinting_printer_data, sizeof(ntprinting_printer_data)), + ntprinting_printer_check); + */ + return suite; +} diff --git a/source4/torture/ndr/odj.c b/source4/torture/ndr/odj.c new file mode 100644 index 0000000..78d4725 --- /dev/null +++ b/source4/torture/ndr/odj.c @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + test suite for Windows Offline Join files + + Copyright (C) Guenther Deschner 2021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_ODJ.h" +#include "torture/ndr/proto.h" + +static const uint8_t ODJ_PROVISION_DATA_data[] = { + 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xb0, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x18, 0x03, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x9c, 0x4f, 0x93, + 0x20, 0xfc, 0x4f, 0x93, 0x90, 0xbf, 0x50, 0x93, 0x60, 0xbf, 0x50, 0x93, + 0x10, 0x00, 0x12, 0x00, 0x40, 0x7f, 0x4f, 0x93, 0x2e, 0x00, 0x30, 0x00, + 0x60, 0xf3, 0x4f, 0x93, 0x2e, 0x00, 0x30, 0x00, 0x50, 0xf4, 0x4f, 0x93, + 0x96, 0x1a, 0x76, 0xc0, 0x42, 0xe2, 0xc2, 0x4e, 0x91, 0x60, 0x7a, 0x66, + 0xa4, 0xac, 0x49, 0x29, 0x00, 0x85, 0x4f, 0x93, 0x30, 0xa1, 0x4f, 0x93, + 0x80, 0xf1, 0x4f, 0x93, 0x01, 0x00, 0x00, 0x00, 0x96, 0x1a, 0x76, 0xc0, + 0x42, 0xe2, 0xc2, 0x4e, 0x91, 0x60, 0x7a, 0x66, 0xa4, 0xac, 0x49, 0x29, + 0x10, 0xfd, 0x4f, 0x93, 0x40, 0xf5, 0x4f, 0x93, 0xfd, 0xf3, 0x03, 0xe0, + 0xf0, 0xf9, 0x4f, 0x93, 0xb0, 0xfd, 0x4f, 0x93, 0x04, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x75, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x77, 0x00, 0x75, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0xe8, 0xf2, 0x4b, 0x12, 0x6a, 0xe6, 0x55, 0x41, + 0x43, 0xa4, 0xb4, 0x74, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x64, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x63, 0x00, 0x2e, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, + 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, + 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, + 0x35, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, + 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, + 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x69, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x69, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x04, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x04, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x76, 0x1c, 0x63, + 0x89, 0x52, 0x21, 0x43, 0xbc, 0x9e, 0x80, 0xf8, 0x43, 0xf8, 0x68, 0xc3, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xcf, 0x0c, 0xfc, + 0xfa, 0x7f, 0x4a, 0x47, 0x86, 0x11, 0x69, 0xff, 0xe2, 0x69, 0x64, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, + 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x08, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x9c, 0x4f, 0x93, 0x20, 0xfc, 0x4f, 0x93, + 0x90, 0xbf, 0x50, 0x93, 0x60, 0xbf, 0x50, 0x93, 0x10, 0x00, 0x12, 0x00, + 0x40, 0x7f, 0x4f, 0x93, 0x2e, 0x00, 0x30, 0x00, 0x60, 0xf3, 0x4f, 0x93, + 0x2e, 0x00, 0x30, 0x00, 0x50, 0xf4, 0x4f, 0x93, 0x96, 0x1a, 0x76, 0xc0, + 0x42, 0xe2, 0xc2, 0x4e, 0x91, 0x60, 0x7a, 0x66, 0xa4, 0xac, 0x49, 0x29, + 0x00, 0x85, 0x4f, 0x93, 0x30, 0xa1, 0x4f, 0x93, 0x80, 0xf1, 0x4f, 0x93, + 0x01, 0x00, 0x00, 0x00, 0x96, 0x1a, 0x76, 0xc0, 0x42, 0xe2, 0xc2, 0x4e, + 0x91, 0x60, 0x7a, 0x66, 0xa4, 0xac, 0x49, 0x29, 0x10, 0xfd, 0x4f, 0x93, + 0x40, 0xf5, 0x4f, 0x93, 0xfd, 0xf3, 0x03, 0xe0, 0xf0, 0xf9, 0x4f, 0x93, + 0xb0, 0xfd, 0x4f, 0x93, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x00, 0x75, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x75, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x31, 0x00, + 0x39, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x2e, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, + 0xe8, 0xf2, 0x4b, 0x12, 0x6a, 0xe6, 0x55, 0x41, 0x43, 0xa4, 0xb4, 0x74, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x64, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, 0x63, 0x00, 0x2e, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x36, 0x00, + 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x31, 0x00, 0x39, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x2e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x68, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x75, 0x00, 0x6c, 0x00, + 0x74, 0x00, 0x2d, 0x00, 0x46, 0x00, 0x69, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x2d, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x75, 0x00, 0x6c, 0x00, + 0x74, 0x00, 0x2d, 0x00, 0x46, 0x00, 0x69, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x2d, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x45, 0x06, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x53, 0x00, 0x2d, 0x00, + 0x31, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x2d, 0x00, 0x33, 0x00, 0x30, 0x00, 0x36, 0x00, 0x39, 0x00, 0x36, 0x00, + 0x37, 0x00, 0x32, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x31, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x36, 0x00, 0x31, 0x00, 0x34, 0x00, 0x38, 0x00, + 0x35, 0x00, 0x38, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x39, 0x00, + 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x39, 0x00, 0x35, 0x00, 0x35, 0x00, + 0x38, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x36, 0x00, 0x30, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool ODJ_PROVISION_DATA_check(struct torture_context *tctx, + struct ODJ_PROVISION_DATA_serialized_ptr *r) +{ + return true; +} + +struct torture_suite *ndr_ODJ_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ODJ"); + + torture_suite_add_ndr_pull_test(suite, + ODJ_PROVISION_DATA_serialized_ptr, + ODJ_PROVISION_DATA_data, + ODJ_PROVISION_DATA_check); + return suite; +} diff --git a/source4/torture/ndr/samr.c b/source4/torture/ndr/samr.c new file mode 100644 index 0000000..9f2f8ee --- /dev/null +++ b/source4/torture/ndr/samr.c @@ -0,0 +1,355 @@ +/* + Unix SMB/CIFS implementation. + test suite for samr ndr operations + + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_samr.h" +#include "torture/ndr/proto.h" + +static const uint8_t samr_connect5_in_data[] = { + 0xa8, 0x71, 0x0e, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x79, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x62, 0x00, + 0x61, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x61, 0x00, + 0x72, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x2e, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_connect5_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xe9, 0xb9, 0x40, + 0x50, 0x94, 0xa5, 0x4d, 0xb1, 0x9b, 0x3a, 0x32, 0xd0, 0xd4, 0x45, 0x0b, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_opendomain_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xc9, 0xe9, 0xb9, 0x40, 0x50, 0x94, 0xa5, 0x4d, + 0xb1, 0x9b, 0x3a, 0x32, 0xd0, 0xd4, 0x45, 0x0b, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x15, 0x00, 0x00, 0x00, 0xfa, 0x50, 0x5e, 0x04, 0x12, 0x95, 0x23, 0x02, + 0xe5, 0xa3, 0xd0, 0x03 +}; + +static const uint8_t samr_opendomain_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x16, 0x2b, 0x52, 0x19, 0x4a, 0x47, + 0x9e, 0x88, 0x8b, 0xe8, 0x93, 0xe6, 0xbf, 0x36, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_lookupnamesindomain_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x16, 0x2b, 0x52, 0x19, 0x4a, 0x47, + 0x9e, 0x88, 0x8b, 0xe8, 0x93, 0xe6, 0xbf, 0x36, 0x01, 0x00, 0x00, 0x00, + 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x14, 0x00, 0x60, 0x6f, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x58, 0x00, 0x50, 0x00, + 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x50, 0x00, 0x32, 0x00, + 0x24, 0x00 +}; + +static const uint8_t samr_lookupnamesindomain_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xeb, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_openuser_in_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x16, 0x2b, 0x52, 0x19, 0x4a, 0x47, + 0x9e, 0x88, 0x8b, 0xe8, 0x93, 0xe6, 0xbf, 0x36, 0xb0, 0x00, 0x00, 0x00, + 0xeb, 0x03, 0x00, 0x00 +}; + +static const uint8_t samr_openuser_out_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_queryuserinfo_in_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d, 0x10, 0x00 +}; + +static const uint8_t samr_queryuserinfo_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_setuserinfo_in_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d, 0x10, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_setuserinfo_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_setuserinfo2_in_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d, 0x1a, 0x00, 0x1a, 0x00, + 0xe9, 0x83, 0x46, 0xc5, 0x1b, 0xc6, 0xd9, 0x97, 0xaf, 0x87, 0xf4, 0xb4, + 0xd4, 0x58, 0x0f, 0xfe, 0x00, 0x86, 0x28, 0x09, 0xc6, 0x76, 0x77, 0x85, + 0xd7, 0xa1, 0xf6, 0xb1, 0xe4, 0xed, 0xe1, 0xca, 0x6a, 0xf8, 0x44, 0x2a, + 0x11, 0x10, 0x16, 0x70, 0x9c, 0x55, 0x03, 0x0f, 0xfe, 0xdb, 0x41, 0xb7, + 0xed, 0x10, 0xc9, 0x3c, 0x8b, 0x28, 0xe6, 0x94, 0x6e, 0x69, 0xc2, 0x9d, + 0x81, 0x22, 0xe1, 0xc8, 0xb3, 0xb8, 0xc0, 0x81, 0x50, 0x2f, 0xf3, 0xad, + 0x2b, 0x2e, 0xfe, 0xf6, 0x58, 0x14, 0x2d, 0x33, 0x4a, 0x74, 0x58, 0x4c, + 0xfe, 0x38, 0xe6, 0x21, 0xc3, 0x65, 0x29, 0xc3, 0xc7, 0x77, 0xb7, 0x1c, + 0xfe, 0x0b, 0x22, 0xb7, 0x2b, 0xab, 0x3e, 0x9b, 0xd1, 0x8f, 0xd9, 0x07, + 0x14, 0x65, 0x37, 0x35, 0x9d, 0x08, 0xdb, 0x81, 0x7d, 0x8d, 0x96, 0x96, + 0x9d, 0x14, 0x8d, 0xeb, 0x4f, 0x0b, 0x68, 0xee, 0xf1, 0x64, 0xf4, 0x27, + 0x75, 0x13, 0xaf, 0x1a, 0x41, 0xc8, 0x96, 0x98, 0xe2, 0x8c, 0x33, 0x98, + 0x4b, 0xa3, 0x98, 0xa9, 0xdd, 0xfe, 0x37, 0x86, 0xdd, 0x18, 0xea, 0x77, + 0x44, 0xfc, 0xba, 0xdb, 0xfd, 0xfb, 0x40, 0x01, 0x65, 0x05, 0x1e, 0x73, + 0x93, 0x25, 0xc4, 0x73, 0xf7, 0x1b, 0xd9, 0xd8, 0xbc, 0xb4, 0xc4, 0x0c, + 0x47, 0x3c, 0xc3, 0x62, 0xd5, 0xf3, 0x0b, 0x00, 0x74, 0x75, 0x09, 0x7e, + 0x58, 0x40, 0x9c, 0x0b, 0x2f, 0x15, 0x26, 0xa9, 0xdc, 0xe6, 0xd2, 0x5a, + 0xf2, 0xef, 0xd2, 0x4d, 0x6f, 0x2f, 0x86, 0xe9, 0x95, 0xba, 0xfe, 0xe0, + 0x13, 0x7a, 0xb3, 0x01, 0x93, 0x23, 0x0f, 0x43, 0xf7, 0x40, 0x1c, 0xbe, + 0x2e, 0x25, 0x61, 0x35, 0xda, 0x5a, 0x43, 0x82, 0x3b, 0xff, 0x05, 0x08, + 0x7f, 0x64, 0xf2, 0xc9, 0xeb, 0x71, 0x34, 0x9c, 0x37, 0x77, 0xe9, 0x5a, + 0x23, 0x89, 0xdc, 0x88, 0xa5, 0x27, 0x16, 0x05, 0xc8, 0xf1, 0xbf, 0xbb, + 0xb3, 0xe7, 0xa8, 0x08, 0xf5, 0xe9, 0x46, 0xc9, 0x63, 0xb4, 0x5d, 0x11, + 0x38, 0x69, 0x49, 0x5d, 0x92, 0x95, 0x25, 0x35, 0x56, 0x4b, 0x3e, 0xba, + 0x6b, 0xb3, 0x99, 0x72, 0x70, 0x1c, 0xb5, 0x7e, 0x26, 0x5c, 0xbf, 0xd0, + 0xbf, 0xd8, 0x58, 0xf4, 0xeb, 0xc2, 0x37, 0xad, 0x98, 0x7d, 0xa8, 0x05, + 0x7e, 0xf7, 0x48, 0xd9, 0x73, 0x49, 0x39, 0xaa, 0x02, 0x75, 0x67, 0x5b, + 0x44, 0xda, 0xda, 0x01, 0xe6, 0x5b, 0x4e, 0x0a, 0x15, 0xe7, 0x63, 0x70, + 0x1c, 0x16, 0x73, 0x79, 0x24, 0x3d, 0x69, 0x30, 0x85, 0xb1, 0x50, 0x65, + 0xa1, 0x12, 0x73, 0xf1, 0xaf, 0x8d, 0xe9, 0x23, 0x25, 0x99, 0xa2, 0x8a, + 0x83, 0x6a, 0x39, 0x99, 0xe6, 0x6c, 0xd0, 0xe1, 0x58, 0x9a, 0xb2, 0x3f, + 0x02, 0x77, 0x48, 0x3a, 0xb0, 0x9e, 0x1a, 0x33, 0x51, 0x5e, 0xe2, 0x46, + 0xe6, 0x3d, 0x0f, 0x01, 0x64, 0xc6, 0xd1, 0xe9, 0x42, 0xf6, 0xe0, 0x38, + 0x1a, 0x33, 0xc7, 0x30, 0x80, 0xa6, 0x28, 0xc5, 0x18, 0xbb, 0xe0, 0x5a, + 0x8b, 0xf2, 0x33, 0x53, 0x4d, 0xbf, 0xe1, 0x4c, 0x98, 0x7e, 0x79, 0x1a, + 0x0a, 0xdc, 0xdc, 0x04, 0x2e, 0x58, 0x57, 0xba, 0xde, 0x09, 0xa1, 0xe0, + 0x5b, 0xfc, 0x38, 0x90, 0x58, 0x00, 0xf1, 0xa3, 0x9e, 0x3d, 0x51, 0x7c, + 0x1e, 0x50, 0xfa, 0x15, 0x55, 0xb2, 0xde, 0x8b, 0x27, 0xc2, 0xbe, 0xbf, + 0x27, 0xa4, 0x5b, 0x56, 0x38, 0x97, 0xbf, 0x3d, 0xe1, 0x73, 0x22, 0x98, + 0x9e, 0x25, 0x6d, 0x5d, 0xc5, 0x05, 0xdd, 0x72, 0x7d, 0x50, 0x06, 0x32, + 0xd8, 0x3f, 0x16, 0x13, 0x7e, 0x5e, 0xc9, 0x45, 0xf0, 0xa7, 0xc2, 0xeb, + 0xda, 0x79, 0xef, 0x62, 0x3a, 0x96, 0x58, 0x4a, 0xc7, 0xf8, 0xc1, 0xba, + 0x0b, 0x94, 0xb8, 0xd6, 0x78, 0x6d, 0xc1, 0x42, 0xff, 0xc4, 0x8f, 0x5f, + 0x3f, 0x21, 0xc1, 0x0f, 0x5f, 0x42, 0xc9, 0xbb, 0xfd, 0x0f, 0x71, 0xf6, + 0xcc, 0xfe, 0x81, 0x20, 0x00 +}; + +static const uint8_t samr_setuserinfo2_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_getuserpwinfo_in_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d +}; + +static const uint8_t samr_getuserpwinfo_out_data[] = { + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_closehandle_in_data[] = { + 0x02, 0x00, 0x00, 0x00, 0xd8, 0x6e, 0xec, 0x8c, 0xe1, 0x1f, 0x2d, 0x41, + 0x99, 0x53, 0x13, 0xe9, 0xa4, 0x51, 0xe8, 0x1d +}; + +static const uint8_t samr_closehandle_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t samr_changepassworduser3_w2k_in_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x52, 0x00, 0x56, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x64, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x8a, 0x5d, 0x1b, 0x5c, + 0xf4, 0xf8, 0xb6, 0x79, 0xe0, 0xbf, 0x34, 0x0a, 0x9b, 0x73, 0xdb, 0x34, + 0x6e, 0xbe, 0xe1, 0x1f, 0x75, 0x33, 0xff, 0x3c, 0xa1, 0xca, 0xc0, 0xa8, + 0xfa, 0x78, 0xbe, 0x93, 0x9d, 0xb7, 0x9e, 0x15, 0xad, 0x6e, 0x27, 0xb3, + 0x01, 0xdb, 0x6c, 0xb8, 0x5a, 0x2b, 0x73, 0xb9, 0x5a, 0x25, 0xd1, 0xf7, + 0x33, 0xe8, 0x7e, 0x97, 0x7e, 0xb4, 0x90, 0x89, 0x85, 0xc8, 0x20, 0x8d, + 0x25, 0x02, 0x68, 0xb9, 0xe4, 0x09, 0xd1, 0xd5, 0x50, 0x6e, 0x83, 0x0a, + 0x21, 0x45, 0x33, 0xb2, 0xe8, 0x3b, 0xb6, 0x5a, 0x8e, 0x3c, 0x36, 0x51, + 0xe9, 0xb5, 0x22, 0xfe, 0xeb, 0x19, 0x44, 0xa1, 0x01, 0xa9, 0x0c, 0x2c, + 0x5a, 0xd1, 0xfe, 0x69, 0x10, 0x86, 0xae, 0x78, 0x01, 0xaa, 0x5d, 0xbb, + 0x16, 0x02, 0x2b, 0xa5, 0x26, 0xcc, 0x7c, 0x9d, 0xc0, 0x6c, 0x9c, 0x24, + 0x5b, 0x8b, 0x34, 0xc3, 0xbc, 0x40, 0x5e, 0x3d, 0x7c, 0x2a, 0x55, 0x36, + 0xfd, 0x34, 0x08, 0x08, 0x05, 0xca, 0xd2, 0xd1, 0x0e, 0xe4, 0x8f, 0xea, + 0xfe, 0x3f, 0x7d, 0x56, 0x3c, 0x9a, 0x59, 0x68, 0x83, 0x89, 0x21, 0x3e, + 0x7d, 0x33, 0xfd, 0x73, 0x70, 0xa6, 0xec, 0x36, 0xe8, 0x9d, 0x62, 0x40, + 0x0e, 0x97, 0x3d, 0xb0, 0xc9, 0x9d, 0x11, 0xb1, 0x55, 0x51, 0xd0, 0x1d, + 0x72, 0xdb, 0xf1, 0x18, 0x0a, 0x39, 0x94, 0x0f, 0xf8, 0x9a, 0xba, 0x79, + 0x75, 0x37, 0x83, 0x8d, 0x1a, 0x8e, 0x8b, 0x38, 0x0a, 0x44, 0xf1, 0x46, + 0x60, 0xef, 0x8b, 0xd8, 0x83, 0xb7, 0x10, 0x91, 0x2a, 0x42, 0x09, 0x0f, + 0x6a, 0x43, 0xa9, 0xca, 0x44, 0x5d, 0x7f, 0x3b, 0x96, 0xde, 0xa9, 0xd3, + 0x69, 0x3f, 0x0e, 0x27, 0x52, 0x32, 0x54, 0xcf, 0x4c, 0xcb, 0xf1, 0x84, + 0xab, 0x13, 0x5e, 0x56, 0x18, 0x96, 0xce, 0x67, 0x4f, 0x73, 0xf8, 0xb2, + 0xde, 0x82, 0xa6, 0x0c, 0x15, 0x72, 0x73, 0x1a, 0x00, 0x9a, 0x54, 0x85, + 0x4d, 0x83, 0x6d, 0x78, 0x13, 0xc5, 0x7c, 0x86, 0xa9, 0x4b, 0x34, 0x54, + 0xbc, 0x40, 0x13, 0x9d, 0x99, 0x0d, 0xa4, 0xc8, 0xeb, 0x6e, 0xef, 0x3f, + 0x94, 0x6f, 0xc4, 0x4d, 0x2d, 0x13, 0x9f, 0xd6, 0x29, 0xc6, 0x55, 0xc1, + 0x73, 0x86, 0xe7, 0x77, 0xe2, 0x85, 0xb4, 0x03, 0xaf, 0xe2, 0x7a, 0x9c, + 0x62, 0x8e, 0xc7, 0xcb, 0xa5, 0x0c, 0x1f, 0xd5, 0xa2, 0x6b, 0x59, 0xb5, + 0xe7, 0xde, 0xf9, 0x1c, 0xa0, 0x96, 0x48, 0xcd, 0x20, 0x52, 0x23, 0x23, + 0xfb, 0x88, 0x91, 0xda, 0x64, 0x41, 0x24, 0xd4, 0x30, 0x1e, 0x92, 0x69, + 0x4e, 0xad, 0xb9, 0x41, 0x0f, 0x7f, 0x00, 0xdc, 0xdd, 0x17, 0xe8, 0x56, + 0xfc, 0xbd, 0x2f, 0x57, 0x46, 0x41, 0x5a, 0xab, 0xe8, 0xbc, 0x81, 0xc1, + 0xdf, 0x2b, 0x5b, 0xd0, 0xb8, 0x2b, 0x54, 0xaf, 0x8c, 0xd1, 0x1a, 0x91, + 0x93, 0x61, 0x21, 0xd7, 0x65, 0xc4, 0x3a, 0x8b, 0xf5, 0xb5, 0x4e, 0x9b, + 0xf9, 0xe5, 0x77, 0x59, 0x25, 0x60, 0xd0, 0xe4, 0x73, 0x58, 0x1b, 0x03, + 0x9c, 0xf4, 0x80, 0x82, 0xd1, 0xa2, 0x27, 0xe9, 0x60, 0x87, 0xfd, 0x7d, + 0x8f, 0x25, 0x6d, 0x66, 0x8c, 0xb3, 0x7e, 0x92, 0x6a, 0xae, 0x10, 0x10, + 0x29, 0xcc, 0x7a, 0xeb, 0x07, 0x6e, 0x82, 0x01, 0xd0, 0xee, 0xa0, 0xb3, + 0x2f, 0xf0, 0x9d, 0x4c, 0x82, 0x1d, 0x3e, 0x07, 0xf1, 0xbe, 0x64, 0x01, + 0x6a, 0xf8, 0x28, 0xb4, 0x08, 0x51, 0xf0, 0x01, 0x1b, 0x1b, 0x58, 0x5e, + 0x4b, 0x8c, 0x02, 0x92, 0xcb, 0x80, 0x26, 0x9d, 0x60, 0x33, 0xee, 0x6a, + 0x17, 0x39, 0x46, 0x3d, 0x10, 0x04, 0x6d, 0x60, 0x91, 0xdd, 0xb7, 0x6c, + 0xd5, 0x28, 0x36, 0x75, 0x32, 0xf8, 0xd7, 0xc3, 0xe7, 0x90, 0x82, 0xdc, + 0x2a, 0xe8, 0x72, 0x05, 0x95, 0x96, 0x07, 0x40, 0x10, 0x00, 0x02, 0x00, + 0xa7, 0x0f, 0x62, 0x18, 0xfb, 0xca, 0x87, 0x81, 0x92, 0x4a, 0x42, 0xa1, + 0x04, 0x9b, 0xf8, 0x65, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* +static const uint8_t samr_changepassworduser3_w2k_out_data[] = { + 0xbb, 0x00, 0x00, 0xc0 +}; +*/ + +static const uint8_t samr_changepassworduser3_w2k8r2_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xa6, 0x0a, 0xff, 0xde, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0xc0 +}; + +static bool samr_changepassworduser3_w2k8r2_out_check(struct torture_context *tctx, + struct samr_ChangePasswordUser3 *r) +{ + struct samr_DomInfo1 *dominfo = *r->out.dominfo; + struct userPwdChangeFailureInformation *reject = *r->out.reject; + + torture_assert_int_equal(tctx, dominfo->min_password_length, 7, "min_password_length"); + torture_assert_int_equal(tctx, dominfo->password_history_length, 0, "password_history_length"); + torture_assert_int_equal(tctx, dominfo->password_properties, DOMAIN_PASSWORD_COMPLEX, "password_properties"); + torture_assert_u64_equal(tctx, dominfo->max_password_age, 0xffffdeff0aa68000, "max_password_age"); + torture_assert_u64_equal(tctx, dominfo->min_password_age, 0x0000000000000000, "min_password_age"); + + torture_assert_int_equal(tctx, reject->extendedFailureReason, SAM_PWD_CHANGE_NOT_COMPLEX, "extendedFailureReason"); + torture_assert_int_equal(tctx, reject->filterModuleName.length, 0, "filterModuleName.length"); + torture_assert_int_equal(tctx, reject->filterModuleName.size, 0, "filterModuleName.size"); + + torture_assert_ntstatus_equal(tctx, r->out.result, NT_STATUS_PASSWORD_RESTRICTION, "result"); + + return true; +} + +struct torture_suite *ndr_samr_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "samr"); + + torture_suite_add_ndr_pull_fn_test(suite, samr_Connect5, samr_connect5_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_Connect5, samr_connect5_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_LookupNames, samr_lookupnamesindomain_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_LookupNames, samr_lookupnamesindomain_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_OpenDomain, samr_opendomain_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_OpenDomain, samr_opendomain_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_OpenUser, samr_openuser_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_OpenUser, samr_openuser_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_QueryUserInfo, samr_queryuserinfo_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_io_test(suite, samr_QueryUserInfo, samr_queryuserinfo_in_data, samr_queryuserinfo_out_data, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_SetUserInfo, samr_setuserinfo_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_SetUserInfo, samr_setuserinfo_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_SetUserInfo2, samr_setuserinfo2_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_SetUserInfo2, samr_setuserinfo2_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_GetUserPwInfo, samr_getuserpwinfo_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_GetUserPwInfo, samr_getuserpwinfo_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_Close, samr_closehandle_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, samr_Close, samr_closehandle_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, samr_ChangePasswordUser3, samr_changepassworduser3_w2k_in_data, NDR_IN, NULL); +#if 0 + /* Samba currently fails to parse a w2k reply */ + torture_suite_add_ndr_pull_fn_test(suite, samr_ChangePasswordUser3, samr_changepassworduser3_w2k_out_data, NDR_OUT, NULL); +#endif + torture_suite_add_ndr_pull_fn_test(suite, + samr_ChangePasswordUser3, + samr_changepassworduser3_w2k8r2_out_data, + NDR_OUT, + samr_changepassworduser3_w2k8r2_out_check); + + return suite; +} + diff --git a/source4/torture/ndr/spoolss.c b/source4/torture/ndr/spoolss.c new file mode 100644 index 0000000..1628665 --- /dev/null +++ b/source4/torture/ndr/spoolss.c @@ -0,0 +1,2064 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss ndr operations + + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2009-2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "librpc/gen_ndr/ndr_winspool.h" +#include "torture/ndr/proto.h" + +static const uint8_t openprinterex_req_data[] = { + 0xf0, 0xa8, 0x39, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x77, 0x00, 0x32, 0x00, + 0x6b, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0xc9, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0xf5, 0x89, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x66, 0x39, 0x00, + 0x78, 0xf5, 0x89, 0x00, 0x28, 0x0a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x58, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00 +}; + +static const uint8_t openprinterex_resp_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x9f, 0xf8, 0xb9, 0x70, 0x9e, 0x14, 0x6b, 0x47, + 0xb1, 0x95, 0x57, 0xe2, 0x90, 0x94, 0xfb, 0xdc, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t openprinterex_devmode_req_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x6f, 0x00, + 0x67, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00, 0x6d, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x5c, 0x00, 0x6b, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x2d, 0x00, 0x6d, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x2d, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x07, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7c, 0x07, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x2d, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x63, 0x00, 0x5c, 0x00, + 0x6b, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x2d, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x63, 0x00, 0x2d, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0xd8, 0x2a, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x01, 0x04, 0x00, 0x06, 0xdc, 0x00, 0xa0, 0x06, + 0x53, 0xff, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x02, 0x00, 0x0f, 0x00, 0xb0, 0x04, 0x01, 0x00, 0x01, 0x00, + 0xb0, 0x04, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x33, 0x00, 0xf3, 0xd8, 0x08, 0x02, 0x08, 0x34, 0x00, 0x0c, + 0x00, 0xf8, 0xfb, 0x0b, 0x08, 0x34, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x52, 0x49, 0x56, 0xe2, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x27, 0x10, 0x27, 0x10, 0x27, 0x00, 0x00, 0x10, 0x27, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x94, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x10, 0x00, + 0x28, 0x88, 0x04, 0x00, 0x50, 0x34, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0xc1, 0xf9, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, + 0x53, 0x4d, 0x54, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xb0, 0x01, + 0x6b, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x2d, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x31, + 0x32, 0x30, 0x30, 0x64, 0x70, 0x69, 0x00, 0x4b, 0x43, 0x45, 0x63, 0x6f, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x4f, 0x66, 0x66, 0x00, 0x53, 0x6d, + 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x54, 0x72, 0x75, 0x65, + 0x00, 0x50, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x00, 0x41, 0x34, + 0x00, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x00, + 0x00, 0x4c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x64, 0x67, 0x65, + 0x00, 0x00, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x6c, 0x6f, 0x74, 0x00, + 0x50, 0x46, 0x36, 0x30, 0x41, 0x00, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, + 0x79, 0x70, 0x65, 0x00, 0x50, 0x72, 0x6e, 0x44, 0x65, 0x66, 0x00, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x00, 0x4e, 0x6f, 0x6e, + 0x65, 0x00, 0x44, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x00, 0x4e, 0x6f, 0x6e, + 0x65, 0x00, 0x4b, 0x43, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x00, + 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x4b, 0x6d, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x00, 0x4b, 0x43, 0x53, 0x75, 0x70, 0x65, 0x72, 0x57, 0x61, 0x74, 0x65, + 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x50, + 0x61, 0x67, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x00, 0x4f, 0x6e, + 0x00, 0x4b, 0x4d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x44, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x63, 0x75, 0x70, 0x73, 0x4a, + 0x6f, 0x62, 0x48, 0x6f, 0x6c, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x00, + 0x6e, 0x6f, 0x2d, 0x68, 0x6f, 0x6c, 0x64, 0x00, 0x63, 0x75, 0x70, 0x73, + 0x4a, 0x6f, 0x62, 0x53, 0x68, 0x65, 0x65, 0x74, 0x73, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x00, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x63, 0x75, 0x70, 0x73, + 0x4a, 0x6f, 0x62, 0x53, 0x68, 0x65, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x64, + 0x00, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x02, 0x00, 0x00, 0x53, 0x50, 0x55, 0x43, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x10, 0x00, 0x02, 0x00, + 0xce, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x54, 0x00, 0x45, 0x00, + 0x52, 0x00, 0x4d, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x4c, 0x00, + 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x00, 0x00 +}; + +static const uint8_t closeprinter_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x9f, 0xf8, 0xb9, 0x70, 0x9e, 0x14, 0x6b, 0x47, + 0xb1, 0x95, 0x57, 0xe2, 0x90, 0x94, 0xfb, 0xdc +}; + +static const uint8_t closeprinter_resp_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t getprinter_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x7e, 0x6c, 0xfd, 0x7c, 0x90, 0x53, 0x4c, + 0xb8, 0x6f, 0x66, 0xb5, 0xff, 0x73, 0xd9, 0xac, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t getprinter_resp_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00 +}; + +static const uint8_t getprinterdata_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xbf, 0xee, 0x56, 0x27, 0x7f, 0xef, 0xf7, 0x42, + 0x84, 0x54, 0xd5, 0x7b, 0xec, 0xe3, 0xcc, 0x55, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x53, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x4a, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x75, 0x00, 0x73, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 +}; + +static const uint8_t getprinterdata_resp_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00 +}; + +static const uint8_t replyopenprinter_req_data[] = { + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x58, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t replyopenprinter_resp_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xef, 0x4a, 0x33, 0x05, 0x22, 0xf4, 0xc4, 0x4a, + 0xa2, 0xde, 0x52, 0x17, 0xa6, 0xc8, 0x19, 0xd0, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t RFFPCNEX_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xbf, 0xee, 0x56, 0x27, 0x7f, 0xef, 0xf7, 0x42, + 0x84, 0x54, 0xd5, 0x7b, 0xec, 0xe3, 0xcc, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x21, 0x26, 0x74, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x58, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x0d, 0x0b, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x0d, 0x0b, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x78, 0x0d, 0x0b, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x12, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x0d, 0x00 +}; + +static const uint8_t RFFPCNEX_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t RRPCN_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x9b, 0x8b, 0xe5, 0x32, 0x9a, 0xd5, 0x45, + 0xa8, 0x0a, 0x10, 0x30, 0x5b, 0x87, 0x6f, 0x69, 0x01, 0x00, 0x00, 0x00, + 0x44, 0x0d, 0x0b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t RRPCN_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0f, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x15, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x8c, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x17, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0f, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x15, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x8c, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x17, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x77, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x33, 0x00, + 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, + 0x4c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x57, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, + 0x31, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x20, 0x00, 0x50, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x64, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, 0x00, 0x00, 0xd6, 0x07, 0x07, 0x00, + 0x06, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x01, 0x00, 0x60, 0x03, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xd6, 0x07, 0x07, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x11, 0x00, + 0x0b, 0x00, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumforms_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x22, 0x5c, 0x46, 0xe5, 0x74, 0xa1, 0x9e, 0x46, + 0x95, 0x80, 0x19, 0xf1, 0xaa, 0x63, 0xc9, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumforms_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterdataex_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x22, 0x5c, 0x46, 0xe5, 0x74, 0xa1, 0x9e, 0x46, + 0x95, 0x80, 0x19, 0xf1, 0xaa, 0x63, 0xc9, 0x01, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x44, 0x00, 0x73, 0x00, + 0x44, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterdataex_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x06, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0xea, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterdataex_w2k8r2_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x62, 0x2a, 0xa4, 0x60, 0x12, 0x99, 0xea, 0x4f, + 0x88, 0xc9, 0xea, 0x0d, 0xb7, 0xc3, 0x61, 0x99, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x44, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x44, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x61, 0x00, 0x00, 0x00, 0x0c, 0x21, 0x00, 0x00 +}; + +static const uint8_t enumprinterdataex_w2k8r2_out_data[] = { + 0x0c, 0x21, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x18, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x14, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x34, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x30, 0x02, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x70, 0x04, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x90, 0x04, 0x00, 0x00, 0xfd, 0x01, 0x00, 0x00, 0x7a, 0x06, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x88, 0x06, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x78, 0x06, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x96, 0x06, 0x00, 0x00, 0xcc, 0x02, 0x00, 0x00, + 0x4e, 0x09, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x68, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x58, 0x09, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x09, 0x00, 0x00, + 0x10, 0x04, 0x00, 0x00, 0x78, 0x0d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x84, 0x0d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xac, 0x0d, 0x00, 0x00, 0xdc, 0x0b, 0x00, 0x00, 0x74, 0x19, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x88, 0x19, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x78, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x90, 0x19, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xa4, 0x19, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xcc, 0x19, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x19, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe8, 0x19, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd8, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xf4, 0x19, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe4, 0x19, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0xe4, 0x02, 0x00, 0x00, 0xd0, 0x1c, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe0, 0x1c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf8, 0x1c, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, + 0xb6, 0x1d, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe0, 0x1d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0x1d, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x1d, 0x00, 0x00, + 0x39, 0x01, 0x00, 0x00, 0x16, 0x1f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x28, 0x1f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x44, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x56, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x46, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, + 0x4d, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x4a, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x54, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x65, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x6f, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x30, 0x02, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x02, 0x80, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x97, 0xda, 0x0b, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x65, 0x00, 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x77, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfd, 0x01, 0x00, 0x00, 0x46, 0x00, 0x65, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x4b, 0x00, + 0x65, 0x00, 0x79, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x55, 0x6e, + 0x69, 0x74, 0x00, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, + 0x6c, 0x65, 0x64, 0x00, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, + 0x72, 0x79, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x73, + 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x00, 0x33, + 0x38, 0x34, 0x2d, 0x34, 0x39, 0x35, 0x4d, 0x42, 0x00, 0x0a, 0x50, 0x72, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x48, 0x61, 0x72, 0x64, 0x44, 0x69, 0x73, + 0x6b, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, + 0x0a, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, + 0x0a, 0x48, 0x50, 0x50, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, + 0x67, 0x68, 0x00, 0x54, 0x72, 0x75, 0x65, 0x00, 0x0a, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x73, 0x4d, 0x6f, 0x70, 0x69, 0x65, 0x72, 0x00, + 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, + 0x00, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x4e, 0x6f, 0x74, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x0a, 0x48, 0x50, + 0x4d, 0x65, 0x64, 0x69, 0x61, 0x50, 0x4d, 0x4c, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x00, 0x31, 0x35, 0x2d, 0x31, 0x39, 0x00, 0x0a, 0x48, 0x50, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x50, 0x4d, 0x4c, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x00, 0x30, 0x2d, 0x30, 0x00, 0x0a, 0x48, 0x50, + 0x50, 0x72, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x00, 0x48, 0x50, 0x43, 0x61, + 0x62, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0a, 0x48, + 0x50, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x48, 0x43, 0x4f, 0x00, 0x33, 0x30, 0x30, 0x30, 0x53, 0x74, 0x61, 0x63, + 0x6b, 0x65, 0x72, 0x2d, 0x43, 0x38, 0x30, 0x38, 0x34, 0x00, 0x0a, 0x48, + 0x50, 0x4d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x48, + 0x43, 0x4f, 0x4d, 0x61, 0x70, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x0a, + 0x48, 0x50, 0x4d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, + 0x48, 0x43, 0x4f, 0x50, 0x4d, 0x4c, 0x4d, 0x61, 0x70, 0x00, 0x4e, 0x6f, + 0x6e, 0x65, 0x00, 0x0a, 0x48, 0x50, 0x50, 0x44, 0x4c, 0x54, 0x79, 0x70, + 0x65, 0x00, 0x50, 0x44, 0x4c, 0x5f, 0x50, 0x53, 0x00, 0x0a, 0x53, 0x63, + 0x61, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x4c, 0x61, 0x72, 0x67, 0x65, + 0x50, 0x61, 0x70, 0x65, 0x72, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, + 0x6c, 0x65, 0x64, 0x00, 0x0a, 0x41, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x33, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x5f, 0x34, 0x36, 0x39, 0x39, 0x30, 0x30, + 0x00, 0x0a, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x4d, 0x65, 0x64, + 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x42, 0x69, 0x6e, 0x73, 0x00, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x73, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xc6, 0x97, 0xda, 0x0b, 0x44, 0x00, 0x65, 0x00, 0x70, 0x00, 0x65, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x46, 0x00, + 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x73, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x5a, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x4c, 0x00, 0x48, 0x00, + 0x4e, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x48, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x4c, 0x00, + 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x53, 0x00, 0x53, 0x00, + 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, + 0x53, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, + 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x5a, 0x00, 0x53, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, + 0x2e, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x4d, 0x00, 0x43, 0x00, 0x50, 0x00, 0x44, 0x00, 0x50, 0x00, + 0x53, 0x00, 0x2e, 0x00, 0x58, 0x00, 0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x53, 0x00, 0x43, 0x00, 0x4c, 0x00, + 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x54, 0x00, 0x44, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x46, 0x00, 0x4e, 0x00, + 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x4d, 0x00, 0x43, 0x00, + 0x50, 0x00, 0x44, 0x00, 0x32, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x43, 0x00, + 0x46, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, + 0x53, 0x00, 0x54, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, + 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x5a, 0x00, 0x45, 0x00, 0x56, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, + 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x43, 0x00, 0x4c, 0x00, + 0x48, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x49, 0x00, 0x44, 0x00, 0x52, 0x00, + 0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x49, 0x00, 0x4e, 0x00, + 0x57, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x49, 0x00, + 0x50, 0x00, 0x4d, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5a, 0x00, + 0x49, 0x00, 0x50, 0x00, 0x52, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, + 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x5a, 0x00, 0x49, 0x00, 0x50, 0x00, 0x54, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x5a, 0x00, 0x49, 0x00, 0x53, 0x00, 0x4e, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x4d, 0x00, 0x49, 0x00, 0x41, 0x00, + 0x50, 0x00, 0x49, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x4d, 0x00, 0x49, 0x00, + 0x4e, 0x00, 0x49, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x4f, 0x00, 0x49, 0x00, + 0x44, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x44, 0x00, + 0x50, 0x00, 0x53, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x50, 0x00, 0x52, 0x00, + 0x4f, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x42, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, + 0x50, 0x00, 0x53, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x45, 0x00, 0x41, 0x00, 0x43, 0x00, + 0x4c, 0x00, 0x48, 0x00, 0x4e, 0x00, 0x2e, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x50, 0x00, 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, + 0x49, 0x00, 0x50, 0x00, 0x54, 0x00, 0x2e, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x50, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x53, 0x00, + 0x43, 0x00, 0x48, 0x00, 0x4d, 0x00, 0x2e, 0x00, 0x47, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x75, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x54, 0x00, 0x52, 0x00, 0x41, 0x00, 0x59, 0x00, + 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x47, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x63, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x50, + 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, + 0x00, 0x20, 0x00, 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x20, + 0x00, 0x53, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x6e, 0x00, 0x75, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x46, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, + 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x54, + 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, + 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, + 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, + 0x00, 0x20, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, + 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x35, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x45, 0x00, 0x78, + 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, + 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x36, 0x00, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, + 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x4d, 0x00, 0x50, 0x00, 0x37, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x01, 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, + 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, + 0x00, 0x38, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, + 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x39, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x45, 0x00, 0x78, + 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, + 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x46, 0x00, 0x65, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x4d, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x61, 0x00, 0x43, 0x00, + 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x4d, 0x00, 0x45, 0x00, + 0x44, 0x00, 0x49, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x45, 0x00, 0x47, 0x00, 0x44, 0x00, 0x41, 0x00, + 0x54, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x6e, 0x00, + 0x73, 0x00, 0x70, 0x00, 0x65, 0x00, 0x63, 0x00, 0x69, 0x00, 0x66, 0x00, + 0x69, 0x00, 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x4c, 0x00, 0x65, + 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x68, 0x00, 0x65, + 0x00, 0x61, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x70, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x70, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, + 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x4c, 0x00, + 0x61, 0x00, 0x62, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x00, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x63, 0x00, 0x79, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, + 0x4c, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x3c, 0x00, 0x37, 0x00, 0x35, 0x00, 0x20, 0x00, 0x67, 0x00, 0x2f, 0x00, + 0x6d, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x48, + 0x00, 0x65, 0x00, 0x61, 0x00, 0x76, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x01, 0x00, 0x00, 0x43, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x20, 0x00, + 0x3e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x33, 0x00, 0x20, 0x00, 0x67, 0x00, + 0x2f, 0x00, 0x6d, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, + 0x00, 0x47, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x73, 0x00, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x20, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x76, 0x00, 0x79, 0x00, + 0x20, 0x00, 0x47, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x73, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x00, 0x00, 0x54, 0x00, 0x6f, + 0x00, 0x75, 0x00, 0x67, 0x00, 0x68, 0x00, 0x20, 0x00, 0x70, 0x00, 0x61, + 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, + 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x70, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x69, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x49, 0x00, 0x6e, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x44, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x36, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, 0x3a, 0x00, + 0x31, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x4d, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x49, 0x00, 0x6e, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x43, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, + 0x53, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe4, 0x02, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x54, 0x00, 0x61, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0xe4, 0x02, 0x50, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x20, 0x00, + 0x53, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x6e, 0x00, 0x75, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x46, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, + 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x35, 0x00, 0x29, 0x00, + 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, + 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, + 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x36, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, + 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x4d, 0x00, 0x50, 0x00, 0x37, 0x00, 0x29, 0x00, 0x00, 0x00, 0x4c, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, + 0x50, 0x00, 0x38, 0x00, 0x29, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, + 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x78, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x31, 0x00, + 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6f, 0x00, + 0x70, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x65, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x70, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xd1, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x4d, 0x00, 0x61, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x41, 0x75, 0x74, 0x6f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, + 0x61, 0x79, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, + 0x61, 0x79, 0x45, 0x78, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, + 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x6e, 0x76, 0x46, 0x65, 0x65, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x6d, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x77, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, + 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x41, 0x75, 0x74, 0x6f, 0x00, 0x4c, 0x45, 0x54, + 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x4d, 0x61, 0x6e, 0x75, 0x61, + 0x6c, 0x46, 0x65, 0x65, 0x64, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, + 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x31, 0x00, 0x4c, 0x45, + 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, + 0x32, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x33, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, + 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x34, 0x00, 0x4c, 0x45, + 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, + 0x35, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x36, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, + 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x37, 0x00, 0x4c, 0x45, + 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, + 0x38, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, + 0x54, 0x72, 0x61, 0x79, 0x39, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, + 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x31, + 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, + 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x32, 0x00, 0x4c, 0x45, 0x54, 0x54, + 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, + 0x74, 0x33, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, + 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x34, 0x00, 0x4c, 0x45, + 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, + 0x45, 0x78, 0x74, 0x35, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, + 0x48, 0x50, 0x00, 0x54, 0x72, 0x61, 0x79, 0x45, 0x78, 0x74, 0x36, 0x00, + 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, 0x3a, 0x48, 0x50, 0x00, 0x45, 0x6e, + 0x76, 0x46, 0x65, 0x65, 0x64, 0x00, 0x4c, 0x45, 0x54, 0x54, 0x45, 0x52, + 0x3a, 0x48, 0x50, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x4d, 0x00, 0x4d, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x21, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterkey_in_data2[] = { + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xcc, 0x89, 0x90, 0x8a, 0xfc, 0xca, 0x4c, + 0xa5, 0x44, 0xdc, 0x30, 0x10, 0x20, 0xd9, 0x8f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterkey_out_data2[] = { + 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterkey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x22, 0x5c, 0x46, 0xe5, 0x74, 0xa1, 0x9e, 0x46, + 0x95, 0x80, 0x19, 0xf1, 0xaa, 0x63, 0xc9, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0x11, + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t enumprinterkey_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00 +}; + +static const uint8_t FCPN_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x03, 0xfc, 0xcf, 0xe5, 0x98, 0xfd, 0x15, 0x4b, + 0xba, 0x28, 0x03, 0x70, 0x74, 0x35, 0x8d, 0x14 +}; + +static const uint8_t FCPN_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t replycloseprinter_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x60, 0xe4, 0xdf, 0x77, 0xb1, 0xbf, 0x43, 0x4f, + 0xbf, 0xb4, 0x58, 0x5c, 0x44, 0xc6, 0x3e, 0x09 +}; + +static const uint8_t replycloseprinter_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t getprinterdriverdir_in_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4e, 0x00, 0x54, 0x00, + 0x20, 0x00, 0x78, 0x00, 0x38, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0xac, 0x26, 0x00, 0x7f, 0xde, 0x15, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x69, 0x4d, 0x88, 0x7f, 0x94, 0xd2, 0xa9, 0x01, + 0xdb, 0xe4, 0x15, 0x5f, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xfc, 0xd2, 0xa9, 0x01, 0x7f, 0xe5, 0x15, 0x5f, 0xfc, 0xdb, 0xa9, 0x01, + 0x18, 0xac, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xce, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe8, 0xd2, 0xa9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x97, 0x7c, 0xf3, 0x77, + 0x40, 0x9e, 0x0a, 0x00, 0xe1, 0x67, 0xf3, 0x77, 0x18, 0x07, 0x08, 0x00, + 0xf9, 0x67, 0xf3, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x9e, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x98, 0xd0, 0xa9, 0x01, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xd2, 0xa9, 0x01, + 0x34, 0x5a, 0xf3, 0x77, 0x70, 0x95, 0xf7, 0x77, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0x73, 0xf3, 0x77, 0x08, 0x02, 0x00, 0x00 +}; + +static const uint8_t getprinterdriverdir_out_data[] = { + 0x04, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x78, 0x00, + 0x34, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x33, 0x00, 0x32, 0x00, + 0x58, 0x00, 0x38, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t addprinterdriverex_in_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x14, 0x00, 0x02, 0x00, 0x18, 0x00, 0x02, 0x00, + 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x02, 0x00, 0x00, 0x40, 0x2a, 0x7c, 0xdd, 0x68, 0xc2, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x0e, 0x02, 0x00, 0x05, 0x00, + 0x28, 0x00, 0x02, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x30, 0x00, 0x02, 0x00, + 0x34, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x65, 0x00, 0x77, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x50, 0x00, 0x61, 0x00, + 0x63, 0x00, 0x6b, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x20, 0x00, + 0x48, 0x00, 0x50, 0x00, 0x2d, 0x00, 0x47, 0x00, 0x4c, 0x00, 0x2f, 0x00, + 0x32, 0x00, 0x20, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, + 0x4e, 0x00, 0x54, 0x00, 0x20, 0x00, 0x78, 0x00, 0x38, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x34, 0x00, 0x5c, 0x00, + 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, + 0x5c, 0x00, 0x57, 0x00, 0x33, 0x00, 0x32, 0x00, 0x58, 0x00, 0x38, 0x00, + 0x36, 0x00, 0x5c, 0x00, 0x34, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x33, 0x00, 0x31, 0x00, 0x31, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x50, 0x00, + 0x4c, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, + 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, + 0x6f, 0x00, 0x78, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x57, 0x00, + 0x33, 0x00, 0x32, 0x00, 0x58, 0x00, 0x38, 0x00, 0x36, 0x00, 0x5c, 0x00, + 0x34, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, + 0x31, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x50, 0x00, 0x47, 0x00, + 0x4c, 0x00, 0x32, 0x00, 0x50, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x2e, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x44, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x78, 0x00, + 0x34, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x33, 0x00, 0x32, 0x00, + 0x58, 0x00, 0x38, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x34, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x5c, 0x00, 0x50, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, + 0x6f, 0x00, 0x78, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x57, 0x00, + 0x33, 0x00, 0x32, 0x00, 0x58, 0x00, 0x38, 0x00, 0x36, 0x00, 0x5c, 0x00, + 0x34, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, + 0x31, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x50, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x54, 0x00, 0x55, 0x00, 0x49, 0x00, 0x2e, 0x00, 0x48, 0x00, 0x4c, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, + 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x67, 0x00, 0x6f, 0x00, 0x2e, 0x00, + 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, + 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x2f, 0x00, 0x66, 0x00, 0x77, 0x00, 0x6c, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x6b, 0x00, 0x2f, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x6b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x33, 0x00, + 0x37, 0x00, 0x26, 0x00, 0x70, 0x00, 0x72, 0x00, 0x64, 0x00, 0x3d, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x37, 0x00, 0x39, 0x00, 0x38, 0x00, 0x26, 0x00, + 0x73, 0x00, 0x62, 0x00, 0x70, 0x00, 0x3d, 0x00, 0x50, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x68, 0x00, 0x70, 0x00, 0x68, 0x00, 0x65, 0x00, + 0x77, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x2d, 0x00, + 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x64, 0x00, 0x5f, 0x00, 0x68, 0x00, 0x70, 0x00, 0x37, 0x00, 0x33, 0x00, + 0x31, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00 +}; + +static const uint8_t getprinterdriver2_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x74, 0xc9, 0xc0, 0x9f, 0x5f, 0x6f, 0x46, + 0xbb, 0x14, 0x48, 0xe6, 0xb6, 0xa8, 0x47, 0x40, 0x00, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, + 0x73, 0x00, 0x20, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x88, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 +}; + +static bool getprinterdriver2_in_check(struct torture_context *tctx, + struct spoolss_GetPrinterDriver2 *r) +{ + torture_assert_str_equal(tctx, r->in.architecture, "Windows x64", "architecture"); + torture_assert_int_equal(tctx, r->in.level, 6, "level"); + torture_assert_int_equal(tctx, r->in.offered, 1160, "offered"); + torture_assert_int_equal(tctx, r->in.client_major_version, 3, "client_major_version"); + torture_assert_int_equal(tctx, r->in.client_minor_version, 2, "client_minor_version"); + + return true; +} + +static const uint8_t getprinterdriver2_out_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x88, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x58, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, + 0xa8, 0x03, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8c, 0xa3, 0xc5, 0x94, 0xc6, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0xb0, 0x1d, 0x01, 0x00, 0x06, 0x00, + 0x0c, 0x03, 0x00, 0x00, 0x8a, 0x02, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, + 0x4c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8c, 0xa3, 0xc5, 0x94, 0xc6, 0x01, 0x01, 0x40, 0xb0, 0x1d, + 0x01, 0x00, 0x06, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x48, 0x00, + 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, + 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, + 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x50, 0x00, 0x53, 0x00, 0x43, 0x00, + 0x52, 0x00, 0x49, 0x00, 0x50, 0x00, 0x54, 0x00, 0x2e, 0x00, 0x4e, 0x00, + 0x54, 0x00, 0x46, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, + 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, + 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x50, 0x00, 0x53, 0x00, + 0x5f, 0x00, 0x53, 0x00, 0x43, 0x00, 0x48, 0x00, 0x4d, 0x00, 0x2e, 0x00, + 0x47, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, + 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x52, 0x00, + 0x49, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x48, 0x00, 0x50, 0x00, 0x53, 0x00, + 0x37, 0x00, 0x2e, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, + 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, + 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, + 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, + 0x5c, 0x00, 0x52, 0x00, 0x49, 0x00, 0x50, 0x00, 0x53, 0x00, 0x55, 0x00, + 0x49, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, + 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, + 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, + 0x33, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x49, 0x00, 0x50, 0x00, 0x53, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x53, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, + 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, + 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x49, 0x00, + 0x43, 0x00, 0x46, 0x00, 0x47, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x58, 0x00, + 0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x68, 0x00, 0x72, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x6f, 0x00, 0x68, 0x00, 0x5f, 0x00, 0x61, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x6d, 0x00, 0x70, 0x00, + 0x35, 0x00, 0x30, 0x00, 0x36, 0x00, 0x33, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, + 0x67, 0x00, 0x6f, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x66, 0x00, + 0x77, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x6b, 0x00, 0x2f, 0x00, + 0x3f, 0x00, 0x4c, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x6b, 0x00, 0x49, 0x00, + 0x44, 0x00, 0x3d, 0x00, 0x34, 0x00, 0x37, 0x00, 0x26, 0x00, 0x70, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x3d, 0x00, 0x31, 0x00, 0x30, 0x00, 0x37, 0x00, + 0x39, 0x00, 0x38, 0x00, 0x26, 0x00, 0x73, 0x00, 0x62, 0x00, 0x70, 0x00, + 0x3d, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x00, 0x00, 0x52, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x6f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, + 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x50, 0x00, + 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, 0x49, 0x00, 0x50, 0x00, 0x54, 0x00, + 0x2e, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x50, 0x00, 0x00, 0x00, 0x5c, 0x00, + 0x5c, 0x00, 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, + 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, + 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, + 0x50, 0x00, 0x53, 0x00, 0x35, 0x00, 0x55, 0x00, 0x49, 0x00, 0x2e, 0x00, + 0x44, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, + 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, 0x5c, 0x00, 0x78, 0x00, + 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, 0x5c, 0x00, 0x52, 0x00, + 0x49, 0x00, 0x31, 0x00, 0x34, 0x00, 0x30, 0x00, 0x33, 0x00, 0x45, 0x00, + 0x33, 0x00, 0x2e, 0x00, 0x50, 0x00, 0x50, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x48, 0x00, 0x2d, 0x00, 0x57, 0x00, + 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x5c, 0x00, + 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x24, 0x00, + 0x5c, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x5c, 0x00, 0x33, 0x00, + 0x5c, 0x00, 0x50, 0x00, 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, 0x49, 0x00, + 0x50, 0x00, 0x54, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4c, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x52, 0x00, 0x69, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x68, 0x00, 0x20, 0x00, 0x41, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x69, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00, 0x50, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool getprinterdriver2_out_check(struct torture_context *tctx, + struct spoolss_GetPrinterDriver2 *r) +{ + torture_assert(tctx, r->out.info, "info"); + torture_assert_int_equal(tctx, r->out.info->info6.version, SPOOLSS_DRIVER_VERSION_200X, "version"); + torture_assert_str_equal(tctx, r->out.info->info6.driver_name, "Ricoh Aficio MP 5000 PS", "driver_name"); + torture_assert_str_equal(tctx, r->out.info->info6.architecture, "Windows x64", "architecture"); + torture_assert_str_equal(tctx, r->out.info->info6.driver_path, "\\\\RH-W2K8R2\\print$\\x64\\3\\PSCRIPT5.DLL", "driver_path"); + torture_assert_str_equal(tctx, r->out.info->info6.data_file, "\\\\RH-W2K8R2\\print$\\x64\\3\\RI1403E3.PPD", "data_file"); + torture_assert_str_equal(tctx, r->out.info->info6.config_file, "\\\\RH-W2K8R2\\print$\\x64\\3\\PS5UI.DLL", "config_file"); + torture_assert_str_equal(tctx, r->out.info->info6.help_file, "\\\\RH-W2K8R2\\print$\\x64\\3\\PSCRIPT.HLP", "help_file"); + torture_assert_str_equal(tctx, r->out.info->info6.help_file, "\\\\RH-W2K8R2\\print$\\x64\\3\\PSCRIPT.HLP", "help_file"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[0], "\\\\RH-W2K8R2\\print$\\x64\\3\\PSCRIPT.NTF", "dependent_files[0]"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[1], "\\\\RH-W2K8R2\\print$\\x64\\3\\PS_SCHM.GDL", "dependent_files[1]"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[2], "\\\\RH-W2K8R2\\print$\\x64\\3\\RICOHPS7.INI", "dependent_files[2]"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[3], "\\\\RH-W2K8R2\\print$\\x64\\3\\RIPSUI7.DLL", "dependent_files[3]"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[4], "\\\\RH-W2K8R2\\print$\\x64\\3\\RIPSRES7.DLL", "dependent_files[4]"); + torture_assert_str_equal(tctx, r->out.info->info6.dependent_files[5], "\\\\RH-W2K8R2\\print$\\x64\\3\\RICFG7.XML", "dependent_files[5]"); + torture_assert(tctx, r->out.info->info6.monitor_name == NULL, "monitor_name"); + torture_assert(tctx, r->out.info->info6.default_datatype == NULL, "default_datatype"); + torture_assert(tctx, r->out.info->info6.previous_names == NULL, "previous_names"); + /* driver_date : Wed Jun 21 02:00:00 2006 CEST */ + torture_assert_u64_equal(tctx, r->out.info->info6.driver_version, 0x000600011db04001ULL, "driver_version"); + torture_assert_str_equal(tctx, r->out.info->info6.manufacturer_name, "Ricoh", "manufacturer_name"); + torture_assert_str_equal(tctx, r->out.info->info6.manufacturer_url, "http://go.microsoft.com/fwlink/?LinkID=47&prd=10798&sbp=Printers", "manufacturer_url"); + torture_assert_str_equal(tctx, r->out.info->info6.hardware_id, "ricohricoh_aficio_mp5063", "hardware_id"); + torture_assert_str_equal(tctx, r->out.info->info6.provider, "Ricoh", "provider"); + torture_assert_int_equal(tctx, *r->out.needed, 1160, "needed"); + torture_assert_int_equal(tctx, *r->out.server_major_version, 0, "server_major_version"); + torture_assert_int_equal(tctx, *r->out.server_minor_version, 0, "server_minor_version"); + torture_assert_werr_ok(tctx, r->out.result, "result"); + + return true; +} + +static const uint8_t openprinterex_64_req_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x35, 0x00, + 0x5c, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x23, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, + 0x41, 0x00, 0x5c, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00 +}; + +static const uint8_t setprinter_64_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x13, 0xbe, 0x52, 0x2a, 0xe4, 0x67, 0xe8, 0x45, + 0x8b, 0xb2, 0xd4, 0x15, 0x55, 0xff, 0xbf, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, + 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, + 0x33, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, + 0x37, 0x00, 0x35, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x43, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, + 0x4c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x4a, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x20, 0x00, 0x32, 0x00, 0x35, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x20, 0x00, 0x50, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x36, 0x00, + 0x20, 0x00, 0x43, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, + 0x20, 0x00, 0x44, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x50, 0x00, 0x54, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, + 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x65, 0x00, 0x6e, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x50, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x64, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x6c, 0x00, + 0x61, 0x00, 0x20, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x70, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x41, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t getcoreprinterdrivers_64_req_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x44, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x45, 0x00, 0x41, 0x00, 0x33, 0x00, 0x37, 0x00, + 0x32, 0x00, 0x2d, 0x00, 0x44, 0x00, 0x44, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x2d, 0x00, 0x34, 0x00, 0x39, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x39, 0x00, 0x45, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x41, 0x00, + 0x36, 0x00, 0x33, 0x00, 0x33, 0x00, 0x35, 0x00, 0x41, 0x00, 0x46, 0x00, + 0x45, 0x00, 0x37, 0x00, 0x39, 0x00, 0x46, 0x00, 0x30, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x7b, 0x00, 0x44, 0x00, 0x32, 0x00, 0x30, 0x00, 0x45, 0x00, + 0x41, 0x00, 0x33, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x44, 0x00, + 0x44, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x39, 0x00, + 0x35, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x39, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x38, 0x00, 0x2d, 0x00, 0x41, 0x00, 0x36, 0x00, 0x33, 0x00, 0x33, 0x00, + 0x35, 0x00, 0x41, 0x00, 0x46, 0x00, 0x45, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x46, 0x00, 0x31, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x44, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x45, 0x00, 0x41, 0x00, 0x33, 0x00, 0x37, 0x00, + 0x32, 0x00, 0x2d, 0x00, 0x44, 0x00, 0x44, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x2d, 0x00, 0x34, 0x00, 0x39, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x39, 0x00, 0x45, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x41, 0x00, + 0x36, 0x00, 0x33, 0x00, 0x33, 0x00, 0x35, 0x00, 0x41, 0x00, 0x46, 0x00, + 0x45, 0x00, 0x37, 0x00, 0x39, 0x00, 0x46, 0x00, 0x32, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x7b, 0x00, 0x44, 0x00, 0x32, 0x00, 0x30, 0x00, 0x45, 0x00, + 0x41, 0x00, 0x33, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x44, 0x00, + 0x44, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x39, 0x00, + 0x35, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x39, 0x00, 0x45, 0x00, 0x44, 0x00, + 0x38, 0x00, 0x2d, 0x00, 0x41, 0x00, 0x36, 0x00, 0x33, 0x00, 0x33, 0x00, + 0x35, 0x00, 0x41, 0x00, 0x46, 0x00, 0x45, 0x00, 0x37, 0x00, 0x39, 0x00, + 0x46, 0x00, 0x33, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00 +}; + +static const uint8_t getcoreprinterdrivers_req_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x47, 0x00, 0x44, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, + 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x6e, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x65, 0x00, 0x6e, 0x00, + 0x73, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +}; + +static const uint8_t getcoreprinterdrivers_rep_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x07, 0x80, +}; + +static const uint8_t getcoreprinterdrivers_req_data_unknown_guid[] = { + 0x00, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x47, 0x00, 0x44, 0x00, + 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, + 0x44, 0x00, 0x43, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, + 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x62, 0x00, 0x38, 0x00, 0x62, 0x00, + 0x37, 0x00, 0x33, 0x00, 0x61, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2d, 0x00, + 0x65, 0x00, 0x35, 0x00, 0x66, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x34, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x61, 0x00, 0x62, 0x00, + 0x61, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x66, 0x00, 0x39, 0x00, 0x38, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x64, 0x00, 0x32, 0x00, 0x33, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x38, 0x00, 0x32, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00 +}; + +static const uint8_t getcoreprinterdrivers_rep_data_unknown_guid[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x07, 0x80 +}; + +static const uint8_t setjobnamedproperty_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x50, 0xdf, 0xe4, 0xce, 0x1a, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x53, 0x00, 0x70, 0x00, + 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x59, 0x00, 0x50, 0x00, 0x45, 0x00, 0x5f, 0x00, 0x50, 0x00, + 0x44, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x4b, 0x00, + 0x4e, 0x00, 0x4f, 0x00, 0x57, 0x00, 0x4e, 0x00, 0x00, 0x00 +}; + +static bool setjobnamedproperty_req_check(struct torture_context *tctx, + struct spoolss_SetJobNamedProperty *r) +{ + /* FIXME hPrinter */ + torture_assert_int_equal(tctx, r->in.JobId, 0x00000005, "JobId"); + torture_assert(tctx, r->in.pProperty, "pProperty"); + torture_assert_str_equal(tctx, r->in.pProperty->propertyName, SPLFILE_CONTENT_TYPE_PROP_NAME, "propertyName"); + torture_assert_int_equal(tctx, r->in.pProperty->propertyValue.ePropertyType, kRpcPropertyTypeString, "ePropertyType"); + torture_assert_str_equal(tctx, r->in.pProperty->propertyValue.value.propertyString, SPLFILE_CONTENT_TYPE_PDL_UNKNOWN, "propertyString"); + + return true; +} + +static const uint8_t setprinter_level_3_xpsp3_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x55, 0x94, 0xbe, 0x50, 0x28, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0xd1, 0xe9, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x00, 0x09, 0x18, 0x00, 0x30, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x24, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0xa4, 0xc0, 0x7d, 0x3b, + 0xcc, 0xce, 0x29, 0xa7, 0xd1, 0xc7, 0xe9, 0xd4, 0x50, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x26, 0x02, 0x00, 0x00, + 0x00, 0x09, 0x18, 0x00, 0x30, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x26, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool setprinter_level_3_xpsp3_req_check(struct torture_context *tctx, + struct spoolss_SetPrinter *r) +{ + struct GUID guid; + + torture_assert_ntstatus_ok(tctx, + GUID_from_string("0000053c-0000-0000-2b55-94be50280000", &guid), + "failed to parse GUID"); + torture_assert_int_equal(tctx, r->in.handle->handle_type, 0, "handle_type"); + torture_assert_guid_equal(tctx, r->in.handle->uuid, guid, "handle.uuid"); + + torture_assert(tctx, r->in.info_ctr, "info_ctr"); + torture_assert_int_equal(tctx, r->in.info_ctr->level, 3, "level"); + torture_assert_int_equal(tctx, r->in.info_ctr->info.info3->sec_desc_ptr, 0x06e9d108, "sec_desc_ptr"); + + torture_assert(tctx, r->in.devmode_ctr, "devmode_ctr"); + torture_assert_int_equal(tctx, r->in.devmode_ctr->_ndr_size, 0, "_ndr_size"); + torture_assert(tctx, r->in.devmode_ctr->devmode == NULL, "devmode"); + + torture_assert(tctx, r->in.secdesc_ctr, "secdesc_ctr"); + torture_assert_int_equal(tctx, r->in.secdesc_ctr->sd_size, 0x000000b4, "sd_size"); + torture_assert_int_equal(tctx, r->in.secdesc_ctr->sd->revision, SECURITY_DESCRIPTOR_REVISION_1, "revision"); + torture_assert_int_equal(tctx, r->in.secdesc_ctr->sd->type, 0x8004, "type"); + torture_assert(tctx, r->in.secdesc_ctr->sd, "sd"); + torture_assert(tctx, r->in.secdesc_ctr->sd->owner_sid == NULL, "owner_sid"); + torture_assert(tctx, r->in.secdesc_ctr->sd->group_sid == NULL, "group_sid"); + torture_assert(tctx, r->in.secdesc_ctr->sd->sacl == NULL, "sacl"); + torture_assert(tctx, r->in.secdesc_ctr->sd->dacl, "dacl"); + + return true; +} + +struct torture_suite *ndr_spoolss_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "spoolss"); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_OpenPrinterEx, openprinterex_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_OpenPrinterEx, openprinterex_resp_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncOpenPrinter, openprinterex_req_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncOpenPrinter, openprinterex_resp_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_OpenPrinterEx, openprinterex_devmode_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncOpenPrinter, openprinterex_devmode_req_data, NDR_IN, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ClosePrinter, closeprinter_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ClosePrinter, closeprinter_resp_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncClosePrinter, closeprinter_req_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncClosePrinter, closeprinter_resp_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinter, getprinter_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinter, getprinter_resp_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetPrinter, getprinter_req_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetPrinter, getprinter_resp_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinterData, getprinterdata_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_io_test(suite, spoolss_GetPrinterData, getprinterdata_req_data, getprinterdata_resp_data, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetPrinterData, getprinterdata_req_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncGetPrinterData, getprinterdata_req_data, getprinterdata_resp_data, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ReplyOpenPrinter, replyopenprinter_req_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ReplyOpenPrinter, replyopenprinter_resp_data, NDR_OUT, NULL ); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ReplyClosePrinter, replycloseprinter_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_ReplyClosePrinter, replycloseprinter_out_data, NDR_OUT, NULL ); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_RemoteFindFirstPrinterChangeNotifyEx, RFFPCNEX_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_RemoteFindFirstPrinterChangeNotifyEx, RFFPCNEX_out_data, NDR_OUT, NULL ); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_RouterRefreshPrinterChangeNotify, RRPCN_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_RouterRefreshPrinterChangeNotify, RRPCN_out_data, NDR_OUT, NULL ); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumForms, enumforms_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumForms, enumforms_out_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumForms, enumforms_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumForms, enumforms_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterDataEx, enumprinterdataex_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterDataEx, enumprinterdataex_out_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterDataEx, enumprinterdataex_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterDataEx, enumprinterdataex_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_io_test(suite, spoolss_EnumPrinterDataEx, enumprinterdataex_w2k8r2_in_data, enumprinterdataex_w2k8r2_out_data, NULL); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncEnumPrinterDataEx, enumprinterdataex_w2k8r2_in_data, enumprinterdataex_w2k8r2_out_data, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterKey, enumprinterkey_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterKey, enumprinterkey_out_data, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterKey, enumprinterkey_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterKey, enumprinterkey_out_data, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterKey, enumprinterkey_in_data2, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_EnumPrinterKey, enumprinterkey_out_data2, NDR_OUT, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterKey, enumprinterkey_in_data2, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncEnumPrinterKey, enumprinterkey_out_data2, NDR_OUT, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_FindClosePrinterNotify, FCPN_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, spoolss_FindClosePrinterNotify, FCPN_out_data, NDR_OUT, NULL ); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinterDriverDirectory, getprinterdriverdir_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_io_test(suite, spoolss_GetPrinterDriverDirectory, getprinterdriverdir_in_data, getprinterdriverdir_out_data, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetPrinterDriverDirectory, getprinterdriverdir_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncGetPrinterDriverDirectory, getprinterdriverdir_in_data, getprinterdriverdir_out_data, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_AddPrinterDriverEx, addprinterdriverex_in_data, NDR_IN, NULL ); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncAddPrinterDriver, addprinterdriverex_in_data, NDR_IN, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinterDriver2, getprinterdriver2_in_data, NDR_IN, getprinterdriver2_in_check); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetPrinterDriver, getprinterdriver2_in_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_io_test(suite, spoolss_GetPrinterDriver2, getprinterdriver2_in_data, getprinterdriver2_out_data, getprinterdriver2_out_check); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncGetPrinterDriver, getprinterdriver2_in_data, getprinterdriver2_out_data, NULL); + + torture_suite_add_ndr_pull_fn_test_flags(suite, spoolss_OpenPrinterEx, openprinterex_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + torture_suite_add_ndr_pull_fn_test_flags(suite, winspool_AsyncOpenPrinter, openprinterex_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + + torture_suite_add_ndr_pull_fn_test_flags(suite, spoolss_SetPrinter, setprinter_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + torture_suite_add_ndr_pull_fn_test_flags(suite, winspool_AsyncSetPrinter, setprinter_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + + torture_suite_add_ndr_pull_fn_test_flags(suite, spoolss_GetCorePrinterDrivers, getcoreprinterdrivers_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + torture_suite_add_ndr_pull_fn_test_flags(suite, winspool_AsyncGetCorePrinterDrivers, getcoreprinterdrivers_64_req_data, NDR_IN, LIBNDR_FLAG_NDR64, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetCorePrinterDrivers, getcoreprinterdrivers_req_data, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetCorePrinterDrivers, getcoreprinterdrivers_req_data, NDR_IN, NULL); + + torture_suite_add_ndr_pull_io_test(suite, spoolss_GetCorePrinterDrivers, getcoreprinterdrivers_req_data, getcoreprinterdrivers_rep_data, NULL); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncGetCorePrinterDrivers, getcoreprinterdrivers_req_data, getcoreprinterdrivers_rep_data, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetCorePrinterDrivers, getcoreprinterdrivers_req_data_unknown_guid, NDR_IN, NULL); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncGetCorePrinterDrivers, getcoreprinterdrivers_req_data_unknown_guid, NDR_IN, NULL); + + torture_suite_add_ndr_pull_io_test(suite, spoolss_GetCorePrinterDrivers, getcoreprinterdrivers_req_data_unknown_guid, getcoreprinterdrivers_rep_data_unknown_guid, NULL); + torture_suite_add_ndr_pull_io_test(suite, winspool_AsyncGetCorePrinterDrivers, getcoreprinterdrivers_req_data_unknown_guid, getcoreprinterdrivers_rep_data_unknown_guid, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_SetJobNamedProperty, setjobnamedproperty_req_data, NDR_IN, setjobnamedproperty_req_check); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncSetJobNamedProperty, setjobnamedproperty_req_data, NDR_IN, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, spoolss_SetPrinter, setprinter_level_3_xpsp3_req_data, NDR_IN, setprinter_level_3_xpsp3_req_check); + torture_suite_add_ndr_pull_fn_test(suite, winspool_AsyncSetPrinter, setprinter_level_3_xpsp3_req_data, NDR_IN, NULL); + + return suite; +} diff --git a/source4/torture/ndr/string.c b/source4/torture/ndr/string.c new file mode 100644 index 0000000..16d3fc3 --- /dev/null +++ b/source4/torture/ndr/string.c @@ -0,0 +1,223 @@ +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "torture/ndr/proto.h" +#include "../lib/util/dlinklist.h" +#include "param/param.h" + +static const char *ascii = "ascii"; +/* the following is equivalent to "kamelÃ¥sÃ¥ öäüÿéèóò" in latin1 */ +static const char latin1[] = { 0x6b, 0x61, 0x6d, 0x65, 0x6c, 0xe5, 0x73, + 0xe5, 0x20, 0xF6, 0xE4, 0xFC, 0xFF, 0xE9, + 0xE8, 0xF3, 0xF2, 0x00 }; +/* the following is equivalent to "kamelÃ¥sÃ¥ ☺☺☺ öäüÿéèóò" in utf8 */ +static const char utf8[] = { 0x6b, 0x61, 0x6d, 0x65, 0x6c, 0xc3, 0xa5, + 0x73, 0xc3, 0xa5, 0x20, 0xE2, 0x98, 0xBA, + 0xE2, 0x98, 0xBA, 0xE2, 0x98, 0xBA, 0x20, + 0xc3, 0xb6, 0xc3, 0xa4, 0xc3, 0xbc, 0xc3, + 0xbf, 0xc3, 0xa9, 0xc3, 0xa8, 0xc3, 0xb3, + 0xc3, 0xb2, 0x00 }; + +/* purely for convenience */ +static const libndr_flags fl_ascii_null = LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM; +static const libndr_flags fl_ascii_noterm = LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NOTERM|LIBNDR_FLAG_REMAINING; +static const libndr_flags fl_utf8_null = LIBNDR_FLAG_STR_UTF8|LIBNDR_FLAG_STR_NULLTERM; +static const libndr_flags fl_raw8_null = LIBNDR_FLAG_STR_RAW8|LIBNDR_FLAG_STR_NULLTERM; + +static bool +test_ndr_push_string (struct torture_context *tctx, const char *string, + libndr_flags flags, enum ndr_err_code exp_ndr_err, + bool strcmp_pass) +{ + TALLOC_CTX *mem_ctx; + struct ndr_push *ndr; + enum ndr_err_code err; + + torture_comment(tctx, + "test_ndr_push_string %s flags 0x%"PRI_LIBNDR_FLAGS" expecting " + "err 0x%x and strcmp %s\n", string, flags, exp_ndr_err, + strcmp_pass?"pass":"fail"); + if (exp_ndr_err != NDR_ERR_SUCCESS) { + torture_comment(tctx, "(ignore any Conversion error) "); + } + + mem_ctx = talloc_named (NULL, 0, "test_ndr_push_string"); + ndr = ndr_push_init_ctx(mem_ctx); + ndr_set_flags (&ndr->flags, flags); + + err = ndr_push_string (ndr, NDR_SCALARS, string); + torture_assert_ndr_err_equal(tctx, err, exp_ndr_err, + "ndr_push_string: unexpected return code"); + + if (exp_ndr_err == NDR_ERR_SUCCESS) { + uint32_t expected_offset = strlen(string); + + if (flags & LIBNDR_FLAG_STR_NULLTERM) { + expected_offset += 1; + } + + torture_assert_int_equal(tctx, + ndr->offset, expected_offset, + "ndr_push_string: invalid length"); + + torture_assert(tctx, ndr->data != NULL, + "ndr_push_string: succeeded but NULL data"); + + torture_assert(tctx, + strcmp_pass == !strcmp(string, (char *)ndr->data), + "ndr_push_string: post-push strcmp"); + + } + + talloc_free(mem_ctx); + return true; +} + +static bool +test_ndr_pull_string (struct torture_context *tctx, const char *string, + libndr_flags flags, enum ndr_err_code exp_ndr_err, + bool strcmp_pass) +{ + TALLOC_CTX *mem_ctx; + DATA_BLOB blob; + struct ndr_pull *ndr; + enum ndr_err_code err; + const char *result = NULL; + + torture_comment(tctx, + "test_ndr_pull_string '%s' flags 0x%"PRI_LIBNDR_FLAGS" expecting " + "err 0x%x and strcmp %s\n", string, flags, exp_ndr_err, + strcmp_pass?"pass":"fail"); + if (exp_ndr_err != NDR_ERR_SUCCESS) { + torture_comment(tctx, "(ignore any Conversion error) "); + } + + mem_ctx = talloc_named (NULL, 0, "test_ndr_pull_string"); + + blob = data_blob_string_const(string); + ndr = ndr_pull_init_blob(&blob, mem_ctx); + torture_assert(mem_ctx, ndr, "ndr init failed"); + ndr_set_flags (&ndr->flags, flags); + + err = ndr_pull_string (ndr, NDR_SCALARS, &result); + torture_assert_ndr_err_equal(tctx, err, exp_ndr_err, + "ndr_pull_string: unexpected return code"); + + if (exp_ndr_err == NDR_ERR_SUCCESS) { + torture_assert(tctx, result != NULL, + "ndr_pull_string: NULL data"); + torture_assert(tctx, strcmp_pass == !strcmp(string, result), + "ndr_pull_string: post-pull strcmp"); + torture_assert(tctx, result != NULL, + "ndr_pull_string succeeded but result NULL"); + } + + talloc_free(mem_ctx); + return true; +} + +static bool +torture_ndr_string(struct torture_context *torture) +{ + const char *saved_dos_cp = talloc_strdup(torture, lpcfg_dos_charset(torture->lp_ctx)); + + torture_assert(torture, + test_ndr_push_string (torture, ascii, fl_ascii_null, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string(ASCII, STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, ascii, fl_ascii_noterm, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string(ASCII, STR_ASCII|STR_NOTERM|REMAINING)"); + torture_assert(torture, + test_ndr_push_string (torture, "", fl_ascii_null, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string('', STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, "", fl_ascii_noterm, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string('', STR_ASCII|STR_NOTERM|REMAINING)"); + torture_assert(torture, + test_ndr_push_string (torture, utf8, fl_utf8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string(UTF8, STR_UTF8|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, utf8, fl_raw8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string(UTF8, STR_RAW8|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, latin1, fl_raw8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_push_string(LATIN1, STR_RAW8|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, utf8, fl_ascii_null, + NDR_ERR_CHARCNV, false), + "test_ndr_push_string(UTF8, STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_push_string (torture, latin1, fl_ascii_null, + NDR_ERR_CHARCNV, false), + "test_ndr_push_string(LATIN1, STR_ASCII|STR_NULL)"); + + + torture_assert(torture, + test_ndr_pull_string (torture, ascii, fl_ascii_null, + NDR_ERR_SUCCESS, true), + "test_ndr_pull_string(ASCII, STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_pull_string (torture, utf8, fl_utf8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_pull_string(UTF8, STR_UTF8|STR_NULL)"); + torture_assert(torture, + test_ndr_pull_string (torture, utf8, fl_raw8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_pull_string(UTF8, STR_RAW8|STR_NULL)"); + torture_assert(torture, + test_ndr_pull_string (torture, latin1, fl_raw8_null, + NDR_ERR_SUCCESS, true), + "test_ndr_pull_string(LATIN1, STR_RAW8|STR_NULL)"); + + /* Depending on runtime config, the behavior of ndr_pull_string on + * incorrect combinations of strings and flags (latin1 with ASCII + * flags, for example) may differ; it may return NDR_ERR_CHARCNV, or + * it may return NDR_ERR_SUCCESS but with a string that has been + * mutilated, depending on the value of "dos charset". We test for + * both cases here. */ + + lpcfg_do_global_parameter(torture->lp_ctx, "dos charset", "ASCII"); + reload_charcnv(torture->lp_ctx); + + torture_assert(torture, + test_ndr_pull_string (torture, latin1, fl_ascii_null, + NDR_ERR_CHARCNV, false), + "test_ndr_pull_string(LATIN1, STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_pull_string (torture, utf8, fl_ascii_null, + NDR_ERR_CHARCNV, false), + "test_ndr_pull_string(UTF8, STR_ASCII|STR_NULL)"); + + lpcfg_do_global_parameter(torture->lp_ctx, "dos charset", "CP850"); + reload_charcnv(torture->lp_ctx); + + torture_assert(torture, + test_ndr_pull_string (torture, latin1, fl_ascii_null, + NDR_ERR_SUCCESS, false), + "test_ndr_pull_string(LATIN1, STR_ASCII|STR_NULL)"); + torture_assert(torture, + test_ndr_pull_string (torture, utf8, fl_ascii_null, + NDR_ERR_SUCCESS, false), + "test_ndr_pull_string(UTF8, STR_ASCII|STR_NULL)"); + + lpcfg_do_global_parameter(torture->lp_ctx, "dos charset", saved_dos_cp); + reload_charcnv(torture->lp_ctx); + + return true; +} + +struct torture_suite *ndr_string_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ndr_string"); + + torture_suite_add_simple_test(suite, "ndr_string", torture_ndr_string); + suite->description = talloc_strdup(suite, "NDR - string-conversion focused push/pull tests"); + + return suite; +} diff --git a/source4/torture/ndr/svcctl.c b/source4/torture/ndr/svcctl.c new file mode 100644 index 0000000..6592bed --- /dev/null +++ b/source4/torture/ndr/svcctl.c @@ -0,0 +1,88 @@ +/* + Unix SMB/CIFS implementation. + test suite for svcctl ndr operations + + Copyright (C) Guenther Deschner 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_svcctl.h" +#include "torture/ndr/proto.h" +#include "param/param.h" + +static const uint8_t svcctl_ChangeServiceConfigW_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xcd, 0x94, 0x05, 0x40, 0x30, 0x28, 0x00, 0x49, + 0x8d, 0xe4, 0x8e, 0x85, 0xb7, 0x19, 0x5c, 0x83, 0x10, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool svcctl_ChangeServiceConfigW_req_check(struct torture_context *tctx, + struct svcctl_ChangeServiceConfigW *r) +{ + struct policy_handle handle = { 0 }; + GUID_from_string("400594cd-2830-4900-8de4-8e85b7195c83", &handle.uuid); + + torture_assert_guid_equal(tctx, r->in.handle->uuid, handle.uuid, "handle"); + torture_assert_u32_equal(tctx, r->in.type, 0x00000110, "type"); + torture_assert_u32_equal(tctx, r->in.start_type, SVCCTL_AUTO_START, "start_type"); + torture_assert_u32_equal(tctx, r->in.error_control, SVCCTL_SVC_ERROR_NORMAL, "error_control"); + torture_assert_str_equal(tctx, r->in.binary_path, NULL, "binary_path"); + torture_assert_str_equal(tctx, r->in.load_order_group, NULL, "load_order_group"); + torture_assert(tctx, r->in.tag_id == NULL, "tag_id"); + torture_assert_str_equal(tctx, r->in.dependencies, NULL, "dependencies"); + torture_assert_u32_equal(tctx, r->in.dwDependSize, 0, "dwDependSize"); + torture_assert_str_equal(tctx, r->in.service_start_name, NULL, "service_start_name"); + torture_assert_str_equal(tctx, r->in.password, NULL, "password"); + torture_assert_u32_equal(tctx, r->in.dwPwSize, 0, "dwPwSize"); + torture_assert_str_equal(tctx, r->in.display_name, NULL, "display_name"); + + return true; +} + +static const uint8_t svcctl_ChangeServiceConfigW_rep_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool svcctl_ChangeServiceConfigW_rep_check(struct torture_context *tctx, + struct svcctl_ChangeServiceConfigW *r) +{ + torture_assert(tctx, r->out.tag_id == NULL, "tag_id"); + torture_assert_werr_ok(tctx, r->out.result, "result"); + + return true; +} + +struct torture_suite *ndr_svcctl_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "svcctl"); + + torture_suite_add_ndr_pull_fn_test(suite, + svcctl_ChangeServiceConfigW, + svcctl_ChangeServiceConfigW_req_data, + NDR_IN, + svcctl_ChangeServiceConfigW_req_check); + + torture_suite_add_ndr_pull_fn_test(suite, + svcctl_ChangeServiceConfigW, + svcctl_ChangeServiceConfigW_rep_data, + NDR_OUT, + svcctl_ChangeServiceConfigW_rep_check); + return suite; +} diff --git a/source4/torture/ndr/winreg.c b/source4/torture/ndr/winreg.c new file mode 100644 index 0000000..4eaff8d --- /dev/null +++ b/source4/torture/ndr/winreg.c @@ -0,0 +1,620 @@ +/* + Unix SMB/CIFS implementation. + test suite for winreg ndr operations + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_winreg.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/ndr/proto.h" + +static const uint8_t closekey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x1d, 0xd8, 0xd7, 0xaa, 0x8d, 0x6c, 0x3f, 0x48, + 0xa7, 0x1e, 0x02, 0x6a, 0x47, 0xf6, 0x7b, 0xae +}; + +static bool closekey_in_check(struct torture_context *tctx, + struct winreg_CloseKey *ck) +{ + torture_assert(tctx, ck->in.handle != NULL, "handle invalid"); + torture_assert_int_equal(tctx, ck->in.handle->handle_type, 0, "handle type"); + return true; +} + +const static uint8_t closekey_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool closekey_out_check(struct torture_context *tctx, + struct winreg_CloseKey *ck) +{ + torture_assert_int_equal(tctx, ck->out.handle->handle_type, 0, "handle type"); + torture_assert_werr_ok(tctx, ck->out.result, "return code"); + return true; +} + +static const uint8_t OpenHKLM_In[] = { + 0x01, 0x00, 0x00, 0x00, 0xe0, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool openhklm_in_check(struct torture_context *tctx, + struct winreg_OpenHKLM *r) +{ + torture_assert(tctx, r->in.system_name != NULL, "system name pointer"); + torture_assert_int_equal(tctx, *r->in.system_name, 34016, "system name"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x02000000, "access mask"); + return true; +} + +static const uint8_t openhklm_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x00, 0x00, 0x00, 0x00 +}; + +static bool openhklm_out_check(struct torture_context *tctx, + struct winreg_OpenHKLM *r) +{ + torture_assert(tctx, r->out.handle != NULL, "handle pointer"); + torture_assert_int_equal(tctx, r->out.handle->handle_type, 0, "handle_type"); + torture_assert_werr_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t createkey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x16, 0x00, 0x16, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x73, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x79, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool createkey_in_check(struct torture_context *tctx, + struct winreg_CreateKey *r) +{ + torture_assert_str_equal(tctx, r->in.name.name, "spottyfoot", "name"); + torture_assert(tctx, r->in.keyclass.name == NULL, "keyclass"); + torture_assert_int_equal(tctx, r->in.options, 0, "option"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x2000000, "access mask"); + torture_assert(tctx, r->in.secdesc == NULL, "secdesc"); + torture_assert(tctx, r->in.action_taken == NULL, "action_taken"); + + return true; +} + +static const uint8_t createkey_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00 +}; + +static bool createkey_out_check(struct torture_context *tctx, + struct winreg_CreateKey *r) +{ + torture_assert(tctx, GUID_all_zero(&r->out.new_handle->uuid), "new_handle"); + torture_assert(tctx, r->out.action_taken == NULL, "action_taken pointer"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, + "return code"); + + return true; +} + +static const uint8_t enumvalue_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xae, 0x1a, 0xbd, 0xbe, 0xbb, 0x94, 0xce, 0x4e, + 0xba, 0xcf, 0x56, 0xeb, 0xe5, 0xb3, 0x6c, 0xa3, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool enumvalue_in_check(struct torture_context *tctx, + struct winreg_EnumValue *r) +{ + torture_assert_int_equal(tctx, r->in.enum_index, 5, "enum index"); + torture_assert(tctx, r->in.type != NULL, "type pointer"); + torture_assert_int_equal(tctx, *r->in.type, 0, "type"); + torture_assert_int_equal(tctx, *r->in.size, 65535, "size"); + torture_assert_int_equal(tctx, *r->in.length, 0, "length"); + torture_assert_int_equal(tctx, r->in.name->size, 512, "name size"); + torture_assert_int_equal(tctx, r->in.name->length, 0, "name length"); + + return true; +} + +static const uint8_t enumvalue_out_data[] = { + 0x12, 0x00, 0x00, 0x02, 0x28, 0x91, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4f, 0x00, + 0x4d, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, 0x54, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd8, 0x8c, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xe0, 0x00, 0x0c, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, + 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x73, 0x00, + 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x53, 0x00, + 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, + 0x73, 0x00, 0x5c, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0xf0, 0x8c, 0x07, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x8c, 0x07, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool enumvalue_out_check(struct torture_context *tctx, + struct winreg_EnumValue *r) +{ + torture_assert_int_equal(tctx, r->out.name->size, 512, "name size"); + torture_assert_int_equal(tctx, r->out.name->length, 18, "name length"); + torture_assert_str_equal(tctx, r->out.name->name, "HOMEPATH", "name"); + torture_assert_int_equal(tctx, *r->out.type, 1, "type"); + torture_assert_int_equal(tctx, *r->out.size, 76, "size"); + torture_assert_int_equal(tctx, *r->out.length, 76, "length"); + torture_assert_werr_ok(tctx, r->out.result, "return code"); + + return true; +} + +unsigned char enumvalue_in_data2[] = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x45, 0x9c, 0xed, 0xe2, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0xcc, 0xf9, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xf9, 0x06, 0x00, + 0x39, 0xa6, 0x07, 0x00, 0x00, 0xc4, 0x04, 0x01, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xf9, 0x06, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x94, 0xf9, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t queryvalue_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xae, 0x1a, 0xbd, 0xbe, 0xbb, 0x94, 0xce, 0x4e, + 0xba, 0xcf, 0x56, 0xeb, 0xe5, 0xb3, 0x6c, 0xa3, 0x12, 0x00, 0x12, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x45, 0x00, + 0x50, 0x00, 0x41, 0x00, 0x54, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool queryvalue_in_check(struct torture_context *tctx, + struct winreg_QueryValue *r) +{ + torture_assert_str_equal(tctx, r->in.value_name->name, "HOMEPATH", "name"); + torture_assert_int_equal(tctx, *r->in.type, 0, "type"); + torture_assert_int_equal(tctx, *r->in.data_size, 4095, "size"); + torture_assert_int_equal(tctx, *r->in.data_length, 0, "length"); + torture_assert(tctx, r->in.data == NULL, "data pointer"); + + return true; +} + +static const uint8_t queryvalue_out_data[] = { + 0xd8, 0xf5, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe4, 0xf5, 0x0b, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xec, 0xf5, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool queryvalue_out_check(struct torture_context *tctx, + struct winreg_QueryValue *r) +{ + torture_assert_werr_ok(tctx, r->out.result, "return code"); + torture_assert_int_equal(tctx, *r->out.type, 1, "type"); + torture_assert(tctx, r->out.data == NULL, "data pointer"); + torture_assert_int_equal(tctx, *r->out.data_size, 76, "size"); + torture_assert_int_equal(tctx, *r->out.data_length, 0, "length"); + + return true; +} + +static const uint8_t querymultiplevalues_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xae, 0x1a, 0xbd, 0xbe, 0xbb, 0x94, 0xce, 0x4e, + 0xba, 0xcf, 0x56, 0xeb, 0xe5, 0xb3, 0x6c, 0xa3, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4f, 0x00, + 0x4d, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, 0x54, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 +}; + +static bool querymultiplevalues_in_check(struct torture_context *tctx, + struct winreg_QueryMultipleValues *r) +{ + torture_assert_int_equal(tctx, r->in.num_values, 1, "num values"); + torture_assert_str_equal(tctx, r->in.values_in[0].ve_valuename->name, "HOMEPATH", "name"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuename->length, 18, "name len"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuename->size, 18, "name size"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuelen, 0, "length"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valueptr, 0, "ve_valueptr"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_type, 0, "type"); + torture_assert_int_equal(tctx, *r->in.buffer_size, 32, "buffer size"); + + return true; +} + +static const uint8_t querymultiplevalues_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd8, 0x8c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, 0x38, 0x87, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, + 0x54, 0x00, 0x48, 0x00, 0xc8, 0x95, 0x08, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4d, 0x45, 0x4d, 0xc8, 0x95, 0x08, 0x00, + 0x50, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x07, 0x00, + 0x00, 0x01, 0x0c, 0x00, 0x50, 0x95, 0x08, 0x00, 0x48, 0x96, 0x08, 0x00, + 0xdc, 0x00, 0x00, 0x00, 0xc0, 0x83, 0x00, 0x01, 0x0d, 0xf0, 0xff, 0xff, + 0x4c, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00 +}; + +static bool querymultiplevalues_out_check(struct torture_context *tctx, + struct winreg_QueryMultipleValues *r) +{ + torture_assert_str_equal(tctx, r->out.values_out[0].ve_valuename->name, "HOMEPATH", "name"); + torture_assert_int_equal(tctx, r->out.values_out[0].ve_type, 0, "type"); + torture_assert_int_equal(tctx, r->out.values_out[0].ve_valuelen, 0, "length"); + /* FIXME: r->out.buffer */ + torture_assert_int_equal(tctx, *r->out.buffer_size, 76, "buffer size"); + torture_assert_werr_equal(tctx, r->out.result, WERR_MORE_DATA, "return code"); + + return true; +} + +const uint8_t querymultiplevalues2_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x98, 0xe4, 0xdf, 0x3c, 0x70, 0xde, 0x69, 0x4a, + 0x90, 0xb4, 0x85, 0x36, 0x33, 0x79, 0x89, 0x32, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x54, 0x00, 0x45, 0x00, + 0x4d, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool querymultiplevalues2_in_check(struct torture_context *tctx, + struct winreg_QueryMultipleValues2 *r) +{ + torture_assert_int_equal(tctx, r->in.num_values, 1, "num values"); + torture_assert_str_equal(tctx, r->in.values_in[0].ve_valuename->name, "TEMP", "name"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuename->length, 10, "name len"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuename->size, 10, "name size"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valuelen, 0, "length"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_valueptr, 0, "ve_valueptr"); + torture_assert_int_equal(tctx, r->in.values_in[0].ve_type, 0, "type"); + torture_assert_int_equal(tctx, *r->in.offered, 0, "buffer size"); + + return true; +} + +const uint8_t querymultiplevalues2_out_data[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x45, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00 +}; + +static bool querymultiplevalues2_out_check(struct torture_context *tctx, + struct winreg_QueryMultipleValues2 *r) +{ + return true; +} + +static const uint8_t flushkey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3 +}; + +static bool flushkey_in_check(struct torture_context *tctx, + struct winreg_FlushKey *r) +{ + torture_assert_int_equal(tctx, r->in.handle->handle_type, 0, "handle type"); + return true; +} + +static const uint8_t flushkey_out_data[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static bool flushkey_out_check(struct torture_context *tctx, + struct winreg_FlushKey *r) +{ + torture_assert_werr_ok(tctx, r->out.result, "return code"); + return true; +} + + +static const uint8_t openkey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x16, 0x00, 0x16, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x73, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x79, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +}; + +static bool openkey_in_check(struct torture_context *tctx, struct winreg_OpenKey *r) +{ + torture_assert_int_equal(tctx, r->in.options, 0, "unknown"); + torture_assert_int_equal(tctx, r->in.access_mask, 0x02000000, "access mask"); + torture_assert_str_equal(tctx, r->in.keyname.name, "spottyfoot", "keyname"); + /* FIXME: parent handle */ + return true; +} + +static const uint8_t openkey_out_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 +}; + +static bool openkey_out_check(struct torture_context *tctx, struct winreg_OpenKey *r) +{ + torture_assert(tctx, GUID_all_zero(&r->out.handle->uuid), "handle"); + torture_assert_werr_equal(tctx, r->out.result, WERR_FILE_NOT_FOUND, "return code"); + return true; +} + +static const uint8_t deletekey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x16, 0x00, 0x16, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x73, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x74, 0x00, 0x79, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x00, 0x00 +}; + +static bool deletekey_in_check(struct torture_context *tctx, struct winreg_DeleteKey *r) +{ + /* FIXME: Handle */ + torture_assert_str_equal(tctx, r->in.key.name, "spottyfoot", "key name"); + return true; +} + +static const uint8_t deletekey_out_data[] = { + 0x02, 0x00, 0x00, 0x00 +}; + +static bool deletekey_out_check(struct torture_context *tctx, struct winreg_DeleteKey *r) +{ + torture_assert_werr_equal(tctx, r->out.result, WERR_FILE_NOT_FOUND, "return code"); + return true; +} + +static const uint8_t getversion_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3 +}; + +static bool getversion_in_check(struct torture_context *tctx, struct winreg_GetVersion *r) +{ + /* FIXME: Handle */ + return true; +} + +static const uint8_t getversion_out_data[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool getversion_out_check(struct torture_context *tctx, struct winreg_GetVersion *r) +{ + torture_assert_int_equal(tctx, *r->out.version, 5, "version"); + torture_assert_werr_ok(tctx, r->out.result, "return code"); + return true; +} + +static const uint8_t queryinfokey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool queryinfokey_in_check(struct torture_context *tctx, struct winreg_QueryInfoKey *r) +{ + /* FIXME: Handle */ + torture_assert(tctx, r->in.classname->name == NULL, "class in"); + return true; +} + +#if 0 +static const uint8_t queryinfokey_out_data[] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x10, 0x48, 0x02, 0x3a, 0xcf, 0xfd, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static bool queryinfokey_out_check(struct torture_context *tctx, struct winreg_QueryInfoKey *r) +{ + torture_assert(tctx, r->out.classname != NULL, "class out"); + torture_assert(tctx, r->out.classname->name != NULL, "class out name"); + torture_assert_str_equal(tctx, r->out.classname->name, "", "class out name"); + torture_assert_int_equal(tctx, *r->out.num_subkeys, 0, "num subkeys"); + torture_assert_int_equal(tctx, *r->out.max_subkeylen, 0, "subkey length"); + torture_assert_int_equal(tctx, *r->out.max_classlen, 140, "subkey size"); + torture_assert_werr_ok(tctx, r->out.result, "return code"); + return true; +} +#endif + +static const uint8_t notifychangekeyvalue_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x64, 0xbc, 0xb3, 0x7f, 0x90, 0x29, 0x4a, + 0xb4, 0xb3, 0x91, 0xe7, 0xe4, 0x4a, 0x58, 0xe3, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool notifychangekeyvalue_in_check(struct torture_context *tctx, struct winreg_NotifyChangeKeyValue *r) +{ + torture_assert_int_equal(tctx, r->in.watch_subtree, 1, "watch subtree"); + torture_assert_int_equal(tctx, r->in.notify_filter, 0, "notify filter"); + torture_assert_int_equal(tctx, r->in.unknown, 0, "unknown"); + torture_assert(tctx, r->in.string1.name == NULL, "string1"); + torture_assert(tctx, r->in.string2.name == NULL, "string2"); + torture_assert_int_equal(tctx, r->in.unknown2, 0, "unknown2"); + return true; +} + +static const uint8_t notifychangekeyvalue_out_data[] = { + 0x57, 0x00, 0x00, 0x00 +}; + +static bool notifychangekeyvalue_out_check(struct torture_context *tctx, struct winreg_NotifyChangeKeyValue *r) +{ + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "notify change key value"); + return true; +} + +#if 0 +static const uint8_t getkeysecurity_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0xbd, 0xaa, 0xf6, 0x59, 0xc1, 0x82, 0x1f, 0x4d, + 0x84, 0xa9, 0xdd, 0xae, 0x60, 0x77, 0x1e, 0x45, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool getkeysecurity_in_check(struct torture_context *tctx, + struct winreg_GetKeySecurity *r) +{ + /* FIXME: Handle */ + torture_assert_int_equal(tctx, r->in.sec_info, 2, "sec info"); + torture_assert_int_equal(tctx, r->in.sd->size, 65536, "sd size"); + torture_assert_int_equal(tctx, r->in.sd->len, 0, "sd len"); + torture_assert(tctx, r->in.sd->data == NULL, "sd data"); + return true; +} + +static const uint8_t getkeysecurity_out_data[] = { + 0x08, 0x91, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool getkeysecurity_out_check(struct torture_context *tctx, + struct winreg_GetKeySecurity *r) +{ + torture_assert_int_equal(tctx, r->in.sd->size, 20, "sd size"); + torture_assert_int_equal(tctx, r->in.sd->len, 20, "sd len"); + torture_assert_werr_ok(tctx, r->out.result, "return code"); + return true; +} +#endif + +static const uint8_t enumkey_in_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x85, 0xb8, 0x41, 0xb0, 0x17, 0xe4, 0x28, 0x45, + 0x8a, 0x69, 0xbf, 0x40, 0x79, 0x82, 0x8b, 0xcb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x04, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f +}; + +static bool enumkey_in_check(struct torture_context *tctx, struct winreg_EnumKey *r) +{ + torture_assert_int_equal(tctx, r->in.enum_index, 0, "enum index"); + torture_assert_int_equal(tctx, r->in.name->size, 1044, "name size"); + torture_assert_int_equal(tctx, r->in.name->length, 0, "name len"); + torture_assert(tctx, r->in.keyclass != NULL, "keyclass pointer"); + torture_assert(tctx, r->in.keyclass->name == NULL, "keyclass"); + torture_assert(tctx, r->in.last_changed_time != NULL, "last_changed_time != NULL"); + return true; +} + +static const uint8_t enumkey_out_data[] = { + 0x08, 0x00, 0x14, 0x04, 0x18, 0xe8, 0x07, 0x00, 0x0a, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x53, 0x00, 0x41, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0xd0, 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x62, 0x07, 0x00, 0x50, 0x67, 0xd0, 0x8b, + 0x16, 0x06, 0xc2, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static bool enumkey_out_check(struct torture_context *tctx, struct winreg_EnumKey *r) +{ + torture_assert_int_equal(tctx, r->out.name->size, 1044, "name size"); + torture_assert_int_equal(tctx, r->out.name->length, 8, "name len"); + torture_assert(tctx, r->out.keyclass != NULL, "keyclass pointer"); + torture_assert(tctx, r->out.keyclass->name == NULL, "keyclass"); + torture_assert(tctx, r->out.last_changed_time != NULL, "last_changed_time pointer"); + /* FIXME: *last_changed_time */ + return true; +} + +struct torture_suite *ndr_winreg_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "winreg"); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_CloseKey, closekey_in_data, NDR_IN, closekey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_CloseKey, closekey_out_data, NDR_OUT, closekey_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_OpenHKLM, OpenHKLM_In, NDR_IN, openhklm_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_OpenHKLM, openhklm_out_data, NDR_OUT, openhklm_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_CreateKey, createkey_in_data, NDR_IN, createkey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_CreateKey, createkey_out_data, NDR_OUT, createkey_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_EnumValue, enumvalue_in_data, NDR_IN, enumvalue_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_EnumValue, enumvalue_out_data, NDR_OUT, enumvalue_out_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_EnumValue, enumvalue_in_data2, NDR_IN, NULL); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryValue, queryvalue_in_data, NDR_IN, queryvalue_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryValue, queryvalue_out_data, NDR_OUT, queryvalue_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryMultipleValues, querymultiplevalues_in_data, NDR_IN, querymultiplevalues_in_check ); + torture_suite_add_ndr_pull_io_test(suite, winreg_QueryMultipleValues, querymultiplevalues_in_data, querymultiplevalues_out_data, querymultiplevalues_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryMultipleValues2, querymultiplevalues2_in_data, NDR_IN, querymultiplevalues2_in_check ); + torture_suite_add_ndr_pull_io_test(suite, winreg_QueryMultipleValues2, querymultiplevalues2_in_data, querymultiplevalues2_out_data, querymultiplevalues2_out_check); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_FlushKey, flushkey_in_data, NDR_IN, flushkey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_FlushKey, flushkey_out_data, NDR_OUT, flushkey_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_OpenKey, openkey_in_data, NDR_IN, openkey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_OpenKey, openkey_out_data, NDR_OUT, openkey_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_DeleteKey, deletekey_in_data, NDR_IN, deletekey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_DeleteKey, deletekey_out_data, NDR_OUT, deletekey_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_GetVersion, getversion_in_data, NDR_IN, getversion_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_GetVersion, getversion_out_data, NDR_OUT, getversion_out_check ); + + torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryInfoKey, queryinfokey_in_data, NDR_IN, queryinfokey_in_check ); + /*torture_suite_add_ndr_pull_fn_test(suite, winreg_QueryInfoKey, queryinfokey_out_data, NDR_OUT, queryinfokey_out_check );*/ + + torture_suite_add_ndr_pull_fn_test(suite, winreg_NotifyChangeKeyValue, notifychangekeyvalue_in_data, NDR_IN, notifychangekeyvalue_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_NotifyChangeKeyValue, notifychangekeyvalue_out_data, NDR_OUT, notifychangekeyvalue_out_check ); + + /*torture_suite_add_ndr_pull_fn_test(suite, winreg_GetKeySecurity, getkeysecurity_in_data, NDR_IN, getkeysecurity_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_GetKeySecurity, getkeysecurity_out_data, NDR_OUT, getkeysecurity_out_check );*/ + + torture_suite_add_ndr_pull_fn_test(suite, winreg_EnumKey, enumkey_in_data, NDR_IN, enumkey_in_check ); + torture_suite_add_ndr_pull_fn_test(suite, winreg_EnumKey, enumkey_out_data, NDR_OUT, enumkey_out_check ); + + return suite; +} + diff --git a/source4/torture/ndr/winspool.c b/source4/torture/ndr/winspool.c new file mode 100644 index 0000000..e5242b9 --- /dev/null +++ b/source4/torture/ndr/winspool.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss ndr operations + + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_winspool.h" +#include "torture/ndr/proto.h" + +static const uint8_t registerforremotenotifications_req_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x4c, 0x7b, 0xc1, 0x46, 0xde, 0x4b, + 0x81, 0x29, 0xcb, 0xa4, 0xd2, 0xf4, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x4e, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x79, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x6c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x46, 0x00, + 0x6c, 0x00, 0x61, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x4e, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x66, 0x00, 0x79, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x70, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x4e, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x79, 0x00, + 0x46, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x4e, 0x00, 0x6f, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x79, 0x00, 0x46, 0x00, 0x69, 0x00, + 0x6c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4e, 0x00, + 0x6f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x79, 0x00, 0x4f, 0x00, + 0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0b, 0x00, + 0x0d, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, + 0x16, 0x00, 0x17, 0x00 +}; + +static bool registerforremotenotifications_req_check(struct torture_context *tctx, + struct winspool_SyncRegisterForRemoteNotifications *r) +{ + struct winspool_PrintNamedProperty *p = r->in.pNotifyFilter->propertiesCollection; + struct spoolss_NotifyOption *o; + + /* r->in.hPrinter */ + torture_assert(tctx, r->in.pNotifyFilter, "pNotifyFilter NULL"); + + torture_assert_int_equal(tctx, r->in.pNotifyFilter->numberOfProperties, 4, "numberOfProperties"); + + torture_assert_str_equal(tctx, p[0].propertyName, "RemoteNotifyFilter Flags", "propertyName"); + torture_assert_int_equal(tctx, p[0].propertyValue.PropertyType, winspool_PropertyTypeInt32, "PropertyType"); + torture_assert_int_equal(tctx, p[0].propertyValue.value.propertyInt32, 255, "propertyInt32"); + + torture_assert_str_equal(tctx, p[1].propertyName, "RemoteNotifyFilter Options", "propertyName"); + torture_assert_int_equal(tctx, p[1].propertyValue.PropertyType, winspool_PropertyTypeInt32, "PropertyType"); + torture_assert_int_equal(tctx, p[1].propertyValue.value.propertyInt32, 0, "propertyInt32"); + + torture_assert_str_equal(tctx, p[2].propertyName, "RemoteNotifyFilter Color", "propertyName"); + torture_assert_int_equal(tctx, p[2].propertyValue.PropertyType, winspool_PropertyTypeInt32, "PropertyType"); + torture_assert_int_equal(tctx, p[2].propertyValue.value.propertyInt32, 0, "propertyInt32"); + + torture_assert_str_equal(tctx, p[3].propertyName, "RemoteNotifyFilter NotifyOptions", "propertyName"); + torture_assert_int_equal(tctx, p[3].propertyValue.PropertyType, winspool_PropertyTypeNotificationOptions, "PropertyType"); + + o = p[3].propertyValue.value.propertyOptionsContainer.pOptions; + + torture_assert_int_equal(tctx, o->version, 2, "version"); + torture_assert_int_equal(tctx, o->flags, 0, "flags"); + torture_assert_int_equal(tctx, o->count, 2, "count"); + + torture_assert_int_equal(tctx, o->types[0].type, PRINTER_NOTIFY_TYPE, "type"); + torture_assert_int_equal(tctx, o->types[0].u1, 0, "u1"); + torture_assert_int_equal(tctx, o->types[0].u2, 0, "u2"); + torture_assert_int_equal(tctx, o->types[0].u3, 0, "u3"); + torture_assert_int_equal(tctx, o->types[0].count, 10, "count"); + + torture_assert_int_equal(tctx, o->types[0].fields[0].field, PRINTER_NOTIFY_FIELD_SERVER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[1].field, PRINTER_NOTIFY_FIELD_PRINTER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[2].field, PRINTER_NOTIFY_FIELD_SHARE_NAME, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[3].field, PRINTER_NOTIFY_FIELD_PORT_NAME, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[4].field, PRINTER_NOTIFY_FIELD_DRIVER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[5].field, PRINTER_NOTIFY_FIELD_COMMENT, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[6].field, PRINTER_NOTIFY_FIELD_LOCATION, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[7].field, PRINTER_NOTIFY_FIELD_ATTRIBUTES, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[8].field, PRINTER_NOTIFY_FIELD_STATUS, "field"); + torture_assert_int_equal(tctx, o->types[0].fields[9].field, PRINTER_NOTIFY_FIELD_CJOBS, "field"); + + torture_assert_int_equal(tctx, o->types[1].type, JOB_NOTIFY_TYPE, "type"); + torture_assert_int_equal(tctx, o->types[1].u1, 0, "u1"); + torture_assert_int_equal(tctx, o->types[1].u2, 0, "u2"); + torture_assert_int_equal(tctx, o->types[1].u3, 0, "u3"); + torture_assert_int_equal(tctx, o->types[1].count, 16, "count"); + + torture_assert_int_equal(tctx, o->types[1].fields[0].field, JOB_NOTIFY_FIELD_PRINTER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[1].field, JOB_NOTIFY_FIELD_MACHINE_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[2].field, JOB_NOTIFY_FIELD_PORT_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[3].field, JOB_NOTIFY_FIELD_USER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[4].field, JOB_NOTIFY_FIELD_NOTIFY_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[5].field, JOB_NOTIFY_FIELD_DRIVER_NAME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[6].field, JOB_NOTIFY_FIELD_STATUS, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[7].field, JOB_NOTIFY_FIELD_STATUS_STRING, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[8].field, JOB_NOTIFY_FIELD_DOCUMENT, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[9].field, JOB_NOTIFY_FIELD_POSITION, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[10].field, JOB_NOTIFY_FIELD_SUBMITTED, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[11].field, JOB_NOTIFY_FIELD_TIME, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[12].field, JOB_NOTIFY_FIELD_TOTAL_PAGES, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[13].field, JOB_NOTIFY_FIELD_PAGES_PRINTED, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[14].field, JOB_NOTIFY_FIELD_TOTAL_BYTES, "field"); + torture_assert_int_equal(tctx, o->types[1].fields[15].field, JOB_NOTIFY_FIELD_BYTES_PRINTED, "field"); + + return true; +} + +struct torture_suite *ndr_winspool_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "winspool"); + + torture_suite_add_ndr_pull_fn_test_flags(suite, + winspool_SyncRegisterForRemoteNotifications, + registerforremotenotifications_req_data, + NDR_IN, + LIBNDR_FLAG_NDR64, + registerforremotenotifications_req_check); + + return suite; +} diff --git a/source4/torture/ndr/witness.c b/source4/torture/ndr/witness.c new file mode 100644 index 0000000..496d045 --- /dev/null +++ b/source4/torture/ndr/witness.c @@ -0,0 +1,411 @@ +/* + Unix SMB/CIFS implementation. + test suite for witness ndr operations + + Copyright (C) Guenther Deschner 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/ndr/ndr.h" +#include "librpc/gen_ndr/ndr_witness.h" +#include "torture/ndr/proto.h" +#include "param/param.h" + +static const uint8_t witness_GetInterfaceList_data[] = { + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x03, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static bool witness_GetInterfaceList_check(struct torture_context *tctx, + struct witness_GetInterfaceList *r) +{ + struct witness_interfaceList *l; + + torture_assert(tctx, r->out.interface_list, "r->out.interface_list"); + + l = *(r->out.interface_list); + + torture_assert_int_equal(tctx, l->num_interfaces, 2, "l->num_interfaces"); + torture_assert(tctx, l->interfaces, "l->interfaces"); + + torture_assert_str_equal(tctx, l->interfaces[0].group_name, "NODE2", "l->interfaces[0].group_name"); + torture_assert_int_equal(tctx, l->interfaces[0].version, -1, "l->interfaces[0].version"); + torture_assert_int_equal(tctx, l->interfaces[0].state, 1, "l->interfaces[0].state"); + torture_assert_str_equal(tctx, l->interfaces[0].ipv4, "192.168.3.44", "l->interfaces[0].state"); + torture_assert_int_equal(tctx, l->interfaces[0].flags, 5, "l->interfaces[0].flags"); + + torture_assert_str_equal(tctx, l->interfaces[1].group_name, "NODE1", "l->interfaces[0].group_name"); + torture_assert_int_equal(tctx, l->interfaces[1].version, -1, "l->interfaces[0].version"); + torture_assert_int_equal(tctx, l->interfaces[1].state, 1, "l->interfaces[0].state"); + torture_assert_str_equal(tctx, l->interfaces[1].ipv4, "192.168.3.45", "l->interfaces[0].state"); + torture_assert_int_equal(tctx, l->interfaces[1].flags, 1, "l->interfaces[0].flags"); + torture_assert_werr_ok(tctx, r->out.result, "r->out.result"); + + return true; +} + +static const uint8_t witness_Register_data_IN[] = { + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x73, 0x00, 0x6f, 0x00, + 0x66, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x35, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x54, 0x00, + 0x48, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x41, 0x00, + 0x00, 0x00 +}; + +static bool witness_Register_check_IN(struct torture_context *tctx, + struct witness_Register *r) +{ + torture_assert_int_equal(tctx, r->in.version, 65537, "r->in.version"); + torture_assert_str_equal(tctx, r->in.net_name, "sofs", "r->in.net_name"); + torture_assert_str_equal(tctx, r->in.ip_address, "192.168.3.45", "r->in.ip_address"); + torture_assert_str_equal(tctx, r->in.client_computer_name, "MTHELENA", "r->in.client_computer_name"); + + return true; +} + +static const uint8_t witness_Register_data_OUT[] = { + 0x00, 0x00, 0x00, 0x00, 0x33, 0x86, 0xb8, 0x3a, 0x57, 0x1e, 0x1a, 0x4c, + 0x85, 0x6c, 0xd1, 0xbc, 0x4b, 0x15, 0xbb, 0xb1, 0x00, 0x00, 0x00, 0x00 +}; + +static bool witness_Register_check_OUT(struct torture_context *tctx, + struct witness_Register *r) +{ + struct GUID guid; + + torture_assert(tctx, r->out.context_handle, "r->out.context_handle"); + torture_assert_int_equal(tctx, r->out.context_handle->handle_type, 0, "r->out.context_handle->handle_type"); + torture_assert_ntstatus_ok(tctx, GUID_from_string("3ab88633-1e57-4c1a-856c-d1bc4b15bbb1", &guid), ""); + torture_assert_mem_equal(tctx, &r->out.context_handle->uuid, &guid, sizeof(guid), "r->out.context_handle->uuid"); + torture_assert_werr_ok(tctx, r->out.result, "r->out.result"); + + return true; +} + +static const uint8_t witness_UnRegister_data_IN[] = { + 0x00, 0x00, 0x00, 0x00, 0x33, 0x86, 0xb8, 0x3a, 0x57, 0x1e, 0x1a, 0x4c, + 0x85, 0x6c, 0xd1, 0xbc, 0x4b, 0x15, 0xbb, 0xb1 +}; + +static bool witness_UnRegister_check_IN(struct torture_context *tctx, + struct witness_UnRegister *r) +{ + struct GUID guid; + + torture_assert_int_equal(tctx, r->in.context_handle.handle_type, 0, "r->in.context_handle.handle_type"); + torture_assert_ntstatus_ok(tctx, GUID_from_string("3ab88633-1e57-4c1a-856c-d1bc4b15bbb1", &guid), ""); + torture_assert_mem_equal(tctx, &r->in.context_handle.uuid, &guid, sizeof(guid), "r->in.context_handle.uuid"); + + return true; +} + +static const uint8_t witness_UnRegister_data_OUT[] = { + 0x00, 0x00, 0x00, 0x00 +}; + +static bool witness_UnRegister_check_OUT(struct torture_context *tctx, + struct witness_UnRegister *r) +{ + torture_assert_werr_ok(tctx, r->out.result, "r->out.result"); + + return true; +} + +static const uint8_t witness_AsyncNotify_data_IN[] = { + 0x00, 0x00, 0x00, 0x00, 0xee, 0xf2, 0xb9, 0x1f, 0x4d, 0x2a, 0xf8, 0x4b, + 0xaf, 0x8b, 0xcb, 0x9d, 0x45, 0x29, 0xa9, 0xab +}; + +static bool witness_AsyncNotify_check_IN(struct torture_context *tctx, + struct witness_AsyncNotify *r) +{ + struct GUID guid; + + torture_assert_int_equal(tctx, r->in.context_handle.handle_type, 0, "r->in.context_handle.handle_type"); + torture_assert_ntstatus_ok(tctx, GUID_from_string("1fb9f2ee-2a4d-4bf8-af8b-cb9d4529a9ab", &guid), ""); + torture_assert_mem_equal(tctx, &r->in.context_handle.uuid, &guid, sizeof(guid), "r->in.context_handle.uuid"); + + return true; +} + +static const uint8_t witness_AsyncNotify_data_OUT[] = { + 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x53, 0x00, 0x4f, 0x00, + 0x46, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static bool witness_AsyncNotify_check_OUT(struct torture_context *tctx, + struct witness_AsyncNotify *r) +{ + struct witness_notifyResponse *n; + struct witness_ResourceChange *c; + + torture_assert(tctx, r->out.response, "r->out.response"); + + n = *(r->out.response); + + torture_assert_int_equal(tctx, n->type, WITNESS_NOTIFY_RESOURCE_CHANGE, "type"); + torture_assert_int_equal(tctx, n->length, 18, "length"); + torture_assert_int_equal(tctx, n->num, 1, "num"); + + c = &n->messages[0].resource_change; + + torture_assert_int_equal(tctx, c->length, 18, "c->length"); + torture_assert_int_equal(tctx, c->type, WITNESS_RESOURCE_STATE_UNAVAILABLE, "c->type"); + torture_assert_str_equal(tctx, c->name, "SOFS", "c->name"); + + return true; +} + +static const uint8_t witness_AsyncNotify_data_move_OUT[] = { + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x00, + 0x38, 0xe8, 0xeb, 0x26, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x60, 0x26, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool witness_AsyncNotify_check_move_OUT(struct torture_context *tctx, + struct witness_AsyncNotify *r) +{ + struct witness_notifyResponse *n; + struct witness_IPaddrInfoList *i; + + torture_assert(tctx, r->out.response, "r->out.response"); + + n = *(r->out.response); + + torture_assert_int_equal(tctx, n->type, WITNESS_NOTIFY_CLIENT_MOVE, "type"); + torture_assert_int_equal(tctx, n->length, 36, "length"); + torture_assert_int_equal(tctx, n->num, 1, "num"); + + i = &n->messages[0].client_move; + + torture_assert_int_equal(tctx, i->length, 36, "i->length"); + torture_assert_int_equal(tctx, i->reserved, 0, "i->reserved"); + torture_assert_int_equal(tctx, i->num, 1, "i->num"); + + torture_assert_int_equal(tctx, i->addr[0].flags, WITNESS_IPADDR_V4, "i->addr[0].flags"); + torture_assert_str_equal(tctx, i->addr[0].ipv4, "192.168.3.45", "i->addr[0].ipv4"); + torture_assert_str_equal(tctx, i->addr[0].ipv6, "0000:0000:38e8:eb26:8e00:0000:009e:6026", "i->addr[0].ipv6"); + + return true; +} + +static const uint8_t witness_AsyncNotify_data_fuzz1_OUT[] = { + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static bool witness_AsyncNotify_check_fuzz1_OUT(struct torture_context *tctx, + struct witness_AsyncNotify *r) +{ + struct witness_notifyResponse *n; + struct witness_IPaddrInfoList *i; + + torture_assert(tctx, r->out.response, "r->out.response"); + + n = *(r->out.response); + + torture_assert_int_equal(tctx, n->type, WITNESS_NOTIFY_CLIENT_MOVE, "type"); + torture_assert_int_equal(tctx, n->length, 12, "length"); + torture_assert_int_equal(tctx, n->num, 1, "num"); + + i = &n->messages[0].client_move; + + torture_assert_int_equal(tctx, i->length, 12, "i->length"); + torture_assert_int_equal(tctx, i->reserved, 0, "i->reserved"); + torture_assert_int_equal(tctx, i->num, 0, "i->num"); + + return true; +} + +struct torture_suite *ndr_witness_suite(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "witness"); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_GetInterfaceList, + witness_GetInterfaceList_data, + NDR_OUT, + witness_GetInterfaceList_check); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_Register, + witness_Register_data_IN, + NDR_IN, + witness_Register_check_IN); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_Register, + witness_Register_data_OUT, + NDR_OUT, + witness_Register_check_OUT); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_UnRegister, + witness_UnRegister_data_IN, + NDR_IN, + witness_UnRegister_check_IN); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_UnRegister, + witness_UnRegister_data_OUT, + NDR_OUT, + witness_UnRegister_check_OUT); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_IN, + NDR_IN, + witness_AsyncNotify_check_IN); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_OUT, + NDR_OUT, + witness_AsyncNotify_check_OUT); + + torture_suite_add_ndr_pullpush_fn_test_flags(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_OUT, + NDR_OUT, + 0, + witness_AsyncNotify_check_OUT); + + torture_suite_add_ndr_pullpush_fn_test_flags(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_move_OUT, + NDR_OUT, + 0, + witness_AsyncNotify_check_move_OUT); + + torture_suite_add_ndr_pull_fn_test(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_fuzz1_OUT, + NDR_OUT, + witness_AsyncNotify_check_fuzz1_OUT); + + torture_suite_add_ndr_pullpush_fn_test_flags(suite, + witness_AsyncNotify, + witness_AsyncNotify_data_fuzz1_OUT, + NDR_OUT, + 0, + witness_AsyncNotify_check_fuzz1_OUT); + + return suite; +} diff --git a/source4/torture/ntp/ntp_signd.c b/source4/torture/ntp/ntp_signd.c new file mode 100644 index 0000000..fd7da67 --- /dev/null +++ b/source4/torture/ntp/ntp_signd.c @@ -0,0 +1,306 @@ +/* + Unix SMB/CIFS implementation. + + Test NTP authentication support + + Copyright (C) Andrew Bartlet 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include +#include "lib/stream/packet.h" +#include "lib/tsocket/tsocket.h" +#include "libcli/util/tstream.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_ntp_signd.h" +#include "param/param.h" +#include "system/network.h" +#include "torture/ntp/proto.h" + +#include +#include + +#define TEST_MACHINE_NAME "ntpsigndtest" + +struct signd_client_state { + struct tsocket_address *local_address; + struct tsocket_address *remote_address; + + struct tstream_context *tstream; + struct tevent_queue *send_queue; + + uint8_t request_hdr[4]; + struct iovec request_iov[2]; + + DATA_BLOB reply; + + NTSTATUS status; +}; + +/* + * A torture test to show that the unix domain socket protocol is + * operating correctly, and the signatures are as expected + */ +static bool test_ntp_signd(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + uint32_t rid; + const char *machine_name; + const struct samr_Password *pwhash = cli_credentials_get_nt_hash(credentials, mem_ctx); + uint32_t negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + + struct sign_request sign_req; + struct signed_reply signed_reply; + DATA_BLOB sign_req_blob; + + struct signd_client_state *signd_client; + struct tevent_req *req; + char *unix_address; + int sys_errno; + + gnutls_hash_hd_t hash_hnd; + uint8_t sig[16]; + enum ndr_err_code ndr_err; + bool ok; + int rc; + + machine_name = cli_credentials_get_workstation(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_ServerReqChallenge_r(p->binding_handle, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "ServerReqChallenge failed"); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + pwhash, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_ServerAuthenticate3_r(p->binding_handle, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, + "ServerAuthenticate3 failed"); + torture_assert(tctx, + netlogon_creds_client_check(creds, &credentials3), + "Credential chaining failed"); + + sign_req.op = SIGN_TO_CLIENT; + sign_req.packet_id = 1; + sign_req.key_id = rid; + sign_req.packet_to_sign = data_blob_string_const("I am a tea pot"); + + ndr_err = ndr_push_struct_blob(&sign_req_blob, + mem_ctx, + &sign_req, + (ndr_push_flags_fn_t)ndr_push_sign_request); + torture_assert(tctx, + NDR_ERR_CODE_IS_SUCCESS(ndr_err), + "Failed to push sign_req"); + + signd_client = talloc(mem_ctx, struct signd_client_state); + + /* Create socket addresses */ + torture_comment(tctx, "Creating the socket addresses\n"); + rc = tsocket_address_unix_from_path(signd_client, "", + &signd_client->local_address); + torture_assert(tctx, rc == 0, + "Failed to create local address from unix path."); + + unix_address = talloc_asprintf(signd_client, + "%s/socket", + lpcfg_ntp_signd_socket_directory(tctx->lp_ctx)); + rc = tsocket_address_unix_from_path(mem_ctx, + unix_address, + &signd_client->remote_address); + torture_assert(tctx, rc == 0, + "Failed to create remote address from unix path."); + + /* Connect to the unix socket */ + torture_comment(tctx, "Connecting to the unix socket\n"); + req = tstream_unix_connect_send(signd_client, + tctx->ev, + signd_client->local_address, + signd_client->remote_address); + torture_assert(tctx, req != NULL, + "Failed to create a tstream unix connect request."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_unix_connect_send."); + + rc = tstream_unix_connect_recv(req, + &sys_errno, + signd_client, + &signd_client->tstream); + TALLOC_FREE(req); + torture_assert(tctx, rc == 0, "Failed to connect to signd!"); + + /* Allocate the send queue */ + signd_client->send_queue = tevent_queue_create(signd_client, + "signd_client_queue"); + torture_assert(tctx, signd_client->send_queue != NULL, + "Failed to create send queue!"); + + /* + * Create the request buffer. + * First add the length of the request buffer + */ + RSIVAL(signd_client->request_hdr, 0, sign_req_blob.length); + signd_client->request_iov[0].iov_base = (char *) signd_client->request_hdr; + signd_client->request_iov[0].iov_len = 4; + + signd_client->request_iov[1].iov_base = (char *) sign_req_blob.data; + signd_client->request_iov[1].iov_len = sign_req_blob.length; + + /* Fire the request buffer */ + torture_comment(tctx, "Sending the request\n"); + req = tstream_writev_queue_send(signd_client, + tctx->ev, + signd_client->tstream, + signd_client->send_queue, + signd_client->request_iov, 2); + torture_assert(tctx, req != NULL, + "Failed to send the signd request."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_writev_queue_send."); + + rc = tstream_writev_queue_recv(req, &sys_errno); + TALLOC_FREE(req); + torture_assert(tctx, rc > 0, "Failed to send data"); + + /* Wait for a reply */ + torture_comment(tctx, "Waiting for the reply\n"); + req = tstream_read_pdu_blob_send(signd_client, + tctx->ev, + signd_client->tstream, + 4, /*initial_read_size */ + tstream_full_request_u32, + NULL); + torture_assert(tctx, req != NULL, + "Failed to setup a read for pdu_blob."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_read_pdu_blob_send."); + + signd_client->status = tstream_read_pdu_blob_recv(req, + signd_client, + &signd_client->reply); + torture_assert_ntstatus_ok(tctx, signd_client->status, + "Error reading signd_client reply packet"); + + /* Skip length header */ + signd_client->reply.data += 4; + signd_client->reply.length -= 4; + + /* Check if the reply buffer is valid */ + torture_comment(tctx, "Validating the reply buffer\n"); + ndr_err = ndr_pull_struct_blob_all(&signd_client->reply, + mem_ctx, + &signed_reply, + (ndr_pull_flags_fn_t)ndr_pull_signed_reply); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), + ndr_map_error2string(ndr_err)); + + torture_assert_u64_equal(tctx, signed_reply.version, + NTP_SIGND_PROTOCOL_VERSION_0, + "Invalid Version"); + torture_assert_u64_equal(tctx, signed_reply.packet_id, + sign_req.packet_id, "Invalid Packet ID"); + torture_assert_u64_equal(tctx, signed_reply.op, + SIGNING_SUCCESS, + "Should have replied with signing success"); + torture_assert_u64_equal(tctx, signed_reply.signed_packet.length, + sign_req.packet_to_sign.length + 20, + "Invalid reply length from signd"); + torture_assert_u64_equal(tctx, rid, + IVAL(signed_reply.signed_packet.data, + sign_req.packet_to_sign.length), + "Incorrect RID in reply"); + + /* Check computed signature */ + gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5); + gnutls_hash(hash_hnd, pwhash->hash, sizeof(pwhash->hash)); + gnutls_hash(hash_hnd, + sign_req.packet_to_sign.data, + sign_req.packet_to_sign.length); + gnutls_hash_deinit(hash_hnd, sig); + + torture_assert_mem_equal(tctx, + &signed_reply.signed_packet.data[sign_req.packet_to_sign.length + 4], + sig, 16, "Signature on reply was incorrect!"); + + talloc_free(mem_ctx); + + return true; +} + +NTSTATUS torture_ntp_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ntp"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, + "signd", &ndr_table_netlogon, TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds(tcase, "ntp_signd", test_ntp_signd); + + suite->description = talloc_strdup(suite, "NTP tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} + diff --git a/source4/torture/rap/printing.c b/source4/torture/rap/printing.c new file mode 100644 index 0000000..af76b26 --- /dev/null +++ b/source4/torture/rap/printing.c @@ -0,0 +1,711 @@ +/* + Unix SMB/CIFS implementation. + test suite for SMB printing operations + + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "system/filesys.h" + +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "libcli/rap/rap.h" +#include "torture/rap/proto.h" +#include "param/param.h" + +/* TODO: + + printing half the file, + finding job + delete job + try writing 2nd half + + SMBsplretq + +*/ + +#define TORTURE_PRINT_FILE "torture_print_file" + +static bool print_printjob(struct torture_context *tctx, + struct smbcli_tree *tree) +{ + int fnum; + DATA_BLOB data; + ssize_t size_written; + const char *str; + + torture_comment(tctx, "creating printjob %s\n", TORTURE_PRINT_FILE); + + fnum = smbcli_open(tree, TORTURE_PRINT_FILE, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE); + if (fnum == -1) { + torture_fail(tctx, "failed to open file"); + } + + str = talloc_asprintf(tctx, "TortureTestPage: %d\nData\n",0); + + data = data_blob_string_const(str); + + size_written = smbcli_write(tree, fnum, 0, data.data, 0, data.length); + if (size_written != data.length) { + torture_fail(tctx, "failed to write file"); + } + + torture_assert_ntstatus_ok(tctx, + smbcli_close(tree, fnum), + "failed to close file"); + + return true; +} + +static bool test_raw_print(struct torture_context *tctx, + struct smbcli_state *cli) +{ + return print_printjob(tctx, cli->tree); +} + +static bool test_netprintqenum(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i, q; + uint16_t levels[] = { 0, 1, 2, 3, 4, 5 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetPrintQEnum level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "smbcli_rap_netprintqenum failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (q=0; qtree, tctx, &r), + "smbcli_rap_netprintqgetinfo failed"); + torture_assert_werr_equal(tctx, + W_ERROR(r.out.status), + WERR_INVALID_PARAMETER, + "smbcli_rap_netprintqgetinfo failed"); + + r_enum.in.level = 5; + r_enum.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r_enum), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r_enum.out.status), + "failed to enum printq"); + + for (p=0; p < r_enum.out.count; p++) { + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + r.in.bufsize = 8192; + r.in.PrintQueueName = r_enum.out.info[p].info5.PrintQueueName; + + torture_comment(tctx, "Testing rap_NetPrintQGetInfo(%s) level %d\n", + r.in.PrintQueueName, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqgetinfo(cli->tree, tctx, &r), + "smbcli_rap_netprintqgetinfo failed"); + torture_assert_werr_ok(tctx, + W_ERROR(r.out.status), + "smbcli_rap_netprintqgetinfo failed"); + + switch (r.in.level) { + case 0: + printf("%s\n", r.out.info.info0.PrintQName); + break; + } + } + } + + return true; +} + +static bool test_netprintjob_pause(struct torture_context *tctx, + struct smbcli_state *cli, + uint16_t job_id) +{ + struct rap_NetPrintJobPause r; + + r.in.JobID = job_id; + + torture_comment(tctx, "Testing rap_NetPrintJobPause(%d)\n", r.in.JobID); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobpause(cli->tree, tctx, &r), + "smbcli_rap_netprintjobpause failed"); + + return true; +} + +static bool test_netprintjob_continue(struct torture_context *tctx, + struct smbcli_state *cli, + uint16_t job_id) +{ + struct rap_NetPrintJobContinue r; + + r.in.JobID = job_id; + + torture_comment(tctx, "Testing rap_NetPrintJobContinue(%d)\n", r.in.JobID); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobcontinue(cli->tree, tctx, &r), + "smbcli_rap_netprintjobcontinue failed"); + + return true; +} + +static bool test_netprintjob_delete(struct torture_context *tctx, + struct smbcli_state *cli, + uint16_t job_id) +{ + struct rap_NetPrintJobDelete r; + + r.in.JobID = job_id; + + torture_comment(tctx, "Testing rap_NetPrintJobDelete(%d)\n", r.in.JobID); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobdelete(cli->tree, tctx, &r), + "smbcli_rap_netprintjobdelete failed"); + + return true; +} + +static bool test_netprintjob(struct torture_context *tctx, + struct smbcli_state *cli) +{ + uint16_t job_id = 400; + + torture_assert(tctx, + test_netprintjob_pause(tctx, cli, job_id), + "failed to pause job"); + torture_assert(tctx, + test_netprintjob_continue(tctx, cli, job_id), + "failed to continue job"); + torture_assert(tctx, + test_netprintjob_delete(tctx, cli, job_id), + "failed to delete job"); + + return true; +} + +static bool test_netprintq_pause(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName) +{ + struct rap_NetPrintQueuePause r; + + r.in.PrintQueueName = PrintQueueName; + + torture_comment(tctx, "Testing rap_NetPrintQueuePause(%s)\n", r.in.PrintQueueName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqueuepause(cli->tree, tctx, &r), + "smbcli_rap_netprintqueuepause failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netprintqueuepause failed"); + + return true; +} + +static bool test_netprintq_resume(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName) +{ + struct rap_NetPrintQueueResume r; + + r.in.PrintQueueName = PrintQueueName; + + torture_comment(tctx, "Testing rap_NetPrintQueueResume(%s)\n", r.in.PrintQueueName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqueueresume(cli->tree, tctx, &r), + "smbcli_rap_netprintqueueresume failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netprintqueueresume failed"); + + return true; +} + +static bool test_netprintq(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i; + + r.in.level = 5; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (i=0; i < r.out.count; i++) { + + const char *printqname = r.out.info[i].info5.PrintQueueName; + + torture_assert(tctx, + test_netprintq_pause(tctx, cli, printqname), + "failed to pause print queue"); + + torture_assert(tctx, + test_netprintq_resume(tctx, cli, printqname), + "failed to resume print queue"); + } + + return true; +} + +static bool test_netprintjobenum_args(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName, + uint16_t level, + uint16_t *count_p, + union rap_printj_info **info_p) +{ + struct rap_NetPrintJobEnum r; + + r.in.PrintQueueName = PrintQueueName; + r.in.bufsize = 8192; + r.in.level = level; + + torture_comment(tctx, + "Testing rap_NetPrintJobEnum(%s) level %d\n", r.in.PrintQueueName, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobenum(cli->tree, tctx, &r), + "smbcli_rap_netprintjobenum failed"); + + if (count_p) { + *count_p = r.out.count; + } + if (info_p) { + *info_p = r.out.info; + } + + return true; +} + +static bool test_netprintjobenum_one(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName) +{ + struct rap_NetPrintJobEnum r; + int i; + uint16_t levels[] = { 0, 1, 2 }; + + r.in.PrintQueueName = PrintQueueName; + r.in.bufsize = 8192; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_comment(tctx, + "Testing rap_NetPrintJobEnum(%s) level %d\n", r.in.PrintQueueName, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobenum(cli->tree, tctx, &r), + "smbcli_rap_netprintjobenum failed"); + } + + return true; +} + +static bool test_netprintjobgetinfo_byid(struct torture_context *tctx, + struct smbcli_state *cli, + uint16_t JobID) +{ + struct rap_NetPrintJobGetInfo r; + uint16_t levels[] = { 0, 1, 2 }; + int i; + + r.in.JobID = JobID; + r.in.bufsize = 8192; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_comment(tctx, "Testing rap_NetPrintJobGetInfo(%d) level %d\n", r.in.JobID, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobgetinfo(cli->tree, tctx, &r), + "smbcli_rap_netprintjobgetinfo failed"); + } + + return true; +} + +static bool test_netprintjobsetinfo_byid(struct torture_context *tctx, + struct smbcli_state *cli, + uint16_t JobID) +{ + struct rap_NetPrintJobSetInfo r; + uint16_t levels[] = { 0, 1, 2 }; + int i; + const char *comment = "tortured by samba"; + + r.in.JobID = JobID; + r.in.bufsize = strlen(comment); + r.in.ParamNum = RAP_PARAM_JOBCOMMENT; + r.in.Param.string = comment; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_comment(tctx, "Testing rap_NetPrintJobSetInfo(%d) level %d\n", r.in.JobID, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobsetinfo(cli->tree, tctx, &r), + "smbcli_rap_netprintjobsetinfo failed"); + } + + return true; +} + + +static bool test_netprintjobgetinfo_byqueue(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName) +{ + struct rap_NetPrintJobEnum r; + int i; + + r.in.PrintQueueName = PrintQueueName; + r.in.bufsize = 8192; + r.in.level = 0; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobenum(cli->tree, tctx, &r), + "failed to enumerate jobs"); + + for (i=0; i < r.out.count; i++) { + + torture_assert(tctx, + test_netprintjobgetinfo_byid(tctx, cli, r.out.info[i].info0.JobID), + "failed to get job info"); + } + + return true; +} + +static bool test_netprintjobsetinfo_byqueue(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintQueueName) +{ + struct rap_NetPrintJobEnum r; + int i; + + r.in.PrintQueueName = PrintQueueName; + r.in.bufsize = 8192; + r.in.level = 0; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintjobenum(cli->tree, tctx, &r), + "failed to enumerate jobs"); + + for (i=0; i < r.out.count; i++) { + + torture_assert(tctx, + test_netprintjobsetinfo_byid(tctx, cli, r.out.info[i].info0.JobID), + "failed to set job info"); + } + + return true; +} + +static bool test_netprintjobenum(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i; + + r.in.level = 5; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (i=0; i < r.out.count; i++) { + + const char *printqname = r.out.info[i].info5.PrintQueueName; + + torture_assert(tctx, + test_netprintjobenum_one(tctx, cli, printqname), + "failed to enumerate printjobs on print queue"); + } + + return true; +} + +static bool test_netprintjobgetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i; + + r.in.level = 5; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (i=0; i < r.out.count; i++) { + + const char *printqname = r.out.info[i].info5.PrintQueueName; + + torture_assert(tctx, + test_netprintjobgetinfo_byqueue(tctx, cli, printqname), + "failed to enumerate printjobs on print queue"); + } + + return true; +} + +static bool test_netprintjobsetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i; + + r.in.level = 5; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (i=0; i < r.out.count; i++) { + + const char *printqname = r.out.info[i].info5.PrintQueueName; + + torture_assert(tctx, + test_netprintjobsetinfo_byqueue(tctx, cli, printqname), + "failed to set printjobs on print queue"); + } + + return true; +} + +static bool test_netprintdestenum(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintDestEnum r; + int i; + uint16_t levels[] = { 0, 1, 2, 3 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetPrintDestEnum level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintdestenum(cli->tree, tctx, &r), + "smbcli_rap_netprintdestenum failed"); + } + + return true; +} + +static bool test_netprintdestgetinfo_bydest(struct torture_context *tctx, + struct smbcli_state *cli, + const char *PrintDestName) +{ + struct rap_NetPrintDestGetInfo r; + int i; + uint16_t levels[] = { 0, 1, 2, 3 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.PrintDestName = PrintDestName; + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetPrintDestGetInfo(%s) level %d\n", r.in.PrintDestName, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintdestgetinfo(cli->tree, tctx, &r), + "smbcli_rap_netprintdestgetinfo failed"); + } + + return true; +} + + +static bool test_netprintdestgetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintDestEnum r; + int i; + + r.in.level = 2; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetPrintDestEnum level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintdestenum(cli->tree, tctx, &r), + "smbcli_rap_netprintdestenum failed"); + + for (i=0; i < r.out.count; i++) { + + torture_assert(tctx, + test_netprintdestgetinfo_bydest(tctx, cli, r.out.info[i].info2.PrinterName), + "failed to get printdest info"); + + } + + return true; +} + +static bool test_rap_print(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetPrintQEnum r; + int i; + + r.in.level = 5; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netprintqenum(cli->tree, tctx, &r), + "failed to enum printq"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "failed to enum printq"); + + for (i=0; i < r.out.count; i++) { + + const char *printqname = r.out.info[i].info5.PrintQueueName; + struct smbcli_tree *res_queue = NULL; + uint16_t num_jobs; + union rap_printj_info *job_info; + int j; + + torture_assert(tctx, + test_netprintq_pause(tctx, cli, printqname), + "failed to set printjobs on print queue"); + + torture_assert_ntstatus_ok(tctx, + torture_second_tcon(tctx, cli->session, printqname, &res_queue), + "failed to open 2nd connection"); + + torture_assert(tctx, + print_printjob(tctx, res_queue), + "failed to print job on 2nd connection"); + + talloc_free(res_queue); + + torture_assert(tctx, + test_netprintjobenum_args(tctx, cli, printqname, 1, + &num_jobs, &job_info), + "failed to enum printjobs on print queue"); + + for (j=0; j < num_jobs; j++) { + + uint16_t job_id = job_info[j].info1.JobID; + + torture_assert(tctx, + test_netprintjobgetinfo_byid(tctx, cli, job_id), + "failed to getinfo on new printjob"); + + torture_assert(tctx, + test_netprintjob_delete(tctx, cli, job_id), + "failed to delete job"); + } + + torture_assert(tctx, + test_netprintq_resume(tctx, cli, printqname), + "failed to resume print queue"); + + } + + return true; +} + +struct torture_suite *torture_rap_printing(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "printing"); + + torture_suite_add_1smb_test(suite, "raw_print", test_raw_print); + torture_suite_add_1smb_test(suite, "rap_print", test_rap_print); + torture_suite_add_1smb_test(suite, "rap_printq_enum", test_netprintqenum); + torture_suite_add_1smb_test(suite, "rap_printq_getinfo", test_netprintqgetinfo); + torture_suite_add_1smb_test(suite, "rap_printq", test_netprintq); + torture_suite_add_1smb_test(suite, "rap_printjob_enum", test_netprintjobenum); + torture_suite_add_1smb_test(suite, "rap_printjob_getinfo", test_netprintjobgetinfo); + torture_suite_add_1smb_test(suite, "rap_printjob_setinfo", test_netprintjobsetinfo); + torture_suite_add_1smb_test(suite, "rap_printjob", test_netprintjob); + torture_suite_add_1smb_test(suite, "rap_printdest_enum", test_netprintdestenum); + torture_suite_add_1smb_test(suite, "rap_printdest_getinfo", test_netprintdestgetinfo); + + return suite; +} diff --git a/source4/torture/rap/rap.c b/source4/torture/rap/rap.c new file mode 100644 index 0000000..054e011 --- /dev/null +++ b/source4/torture/rap/rap.c @@ -0,0 +1,275 @@ +/* + Unix SMB/CIFS implementation. + test suite for various RAP operations + Copyright (C) Volker Lendecke 2004 + Copyright (C) Tim Potter 2005 + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "param/param.h" +#include "libcli/rap/rap.h" +#include "torture/rap/proto.h" + +static bool test_netshareenum(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetShareEnum r; + int i; + + r.in.level = 1; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netshareenum(cli->tree, tctx, &r), ""); + + for (i=0; itree, tctx, &r), ""); + + for (i=0; itree, tctx, &r), + "rap_netservergetinfo level 0 failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "rap_netservergetinfo level 0 failed"); + + r.in.level = 1; + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netservergetinfo(cli->tree, tctx, &r), + "rap_netservergetinfo level 1 failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "rap_netservergetinfo level 1 failed"); + + return res; +} + +static bool test_netsessionenum(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetSessionEnum r; + int i,n; + uint16_t levels[] = { 2 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetSessionEnum level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netsessionenum(cli->tree, tctx, &r), + "smbcli_rap_netsessionenum failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netsessionenum failed"); + + for (n=0; n < r.out.count; n++) { + switch (r.in.level) { + case 2: + torture_comment(tctx, "ComputerName: %s\n", + r.out.info[n].info2.ComputerName); + + torture_comment(tctx, "UserName: %s\n", + r.out.info[n].info2.UserName); + + torture_assert(tctx, r.out.info[n].info2.ComputerName, + "ComputerName empty"); + torture_assert(tctx, r.out.info[n].info2.UserName, + "UserName empty"); + break; + default: + break; + } + } + } + + return true; +} + +static bool test_netsessiongetinfo_bysession(struct torture_context *tctx, + struct smbcli_state *cli, + const char *session) +{ + struct rap_NetSessionGetInfo r; + int i; + uint16_t levels[] = { 2 }; + + if (session && session[0] == '\\' && session[1] == '\\') { + r.in.SessionName = session; + } else { + r.in.SessionName = talloc_asprintf(tctx, "\\\\%s", session); + } + r.in.bufsize = 0xffff; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netsessiongetinfo(cli->tree, tctx, &r), + "rap_netsessiongetinfo failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "rap_netsessiongetinfo failed"); + } + + return true; +} + +static bool test_netsessiongetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetSessionEnum r; + int i,n; + uint16_t levels[] = { 2 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netsessionenum(cli->tree, tctx, &r), + "smbcli_rap_netsessionenum failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netsessionenum failed"); + + for (n=0; n < r.out.count; n++) { + torture_assert(tctx, + test_netsessiongetinfo_bysession(tctx, cli, r.out.info[n].info2.ComputerName), + "failed to query sessioninfo"); + } + } + + return true; +} + +static bool test_netremotetod(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_NetRemoteTOD r; + + r.in.bufsize = 8192; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netremotetod(cli->tree, tctx, &r), + "smbcli_rap_netremotetod failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netremotetod failed"); + + return true; +} + +bool torture_rap_scan(struct torture_context *torture, struct smbcli_state *cli) +{ + int callno; + + for (callno = 0; callno < 0xffff; callno++) { + struct rap_call *call = new_rap_cli_call(torture, callno); + NTSTATUS result; + + result = rap_cli_do_call(cli->tree, call); + + if (!NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER)) + continue; + + printf("callno %d is RAP call\n", callno); + } + + return true; +} + +NTSTATUS torture_rap_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "rap"); + struct torture_suite *suite_basic = torture_suite_create(suite, "basic"); + + torture_suite_add_suite(suite, suite_basic); + torture_suite_add_suite(suite, torture_rap_rpc(suite)); + torture_suite_add_suite(suite, torture_rap_printing(suite)); + torture_suite_add_suite(suite, torture_rap_sam(suite)); + + torture_suite_add_1smb_test(suite_basic, "netserverenum", + test_netserverenum); + torture_suite_add_1smb_test(suite_basic, "netshareenum", + test_netshareenum); + torture_suite_add_1smb_test(suite_basic, "netservergetinfo", + test_netservergetinfo); + torture_suite_add_1smb_test(suite_basic, "netsessionenum", + test_netsessionenum); + torture_suite_add_1smb_test(suite_basic, "netsessiongetinfo", + test_netsessiongetinfo); + torture_suite_add_1smb_test(suite_basic, "netremotetod", + test_netremotetod); + + torture_suite_add_1smb_test(suite, "scan", torture_rap_scan); + + suite->description = talloc_strdup(suite, + "Remote Administration Protocol tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/rap/rpc.c b/source4/torture/rap/rpc.c new file mode 100644 index 0000000..6c8c86c --- /dev/null +++ b/source4/torture/rap/rpc.c @@ -0,0 +1,100 @@ +/* + Unix SMB/CIFS implementation. + test suite for RAP / DCERPC consistency + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "libcli/rap/rap.h" +#include "torture/rap/proto.h" +#include "param/param.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" + +static bool test_rpc_netservergetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct rap_WserverGetInfo r; + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + struct srvsvc_NetSrvGetInfo s; + union srvsvc_NetSrvInfo info; + + const char *server_name; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p, &ndr_table_srvsvc), + "failed to open srvsvc"); + + b = p->binding_handle; + + s.in.server_unc = NULL; + s.in.level = 101; + s.out.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_srvsvc_NetSrvGetInfo_r(b, tctx, &s), + "srvsvc_NetSrvGetInfo level 101 failed"); + torture_assert_werr_ok(tctx, s.out.result, + "srvsvc_NetSrvGetInfo level 101 failed"); + + r.in.bufsize = 0xffff; + r.in.level = 0; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netservergetinfo(cli->tree, tctx, &r), + "rap_netservergetinfo level 0 failed"); + torture_assert_int_equal(tctx, r.out.status, 0, + "rap_netservergetinfo level 0 failed"); + + server_name = talloc_strndup(tctx, info.info101->server_name, 16); + + torture_assert_str_equal(tctx, (const char *)r.out.info.info0.name, server_name, "server name"); + + r.in.level = 1; + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netservergetinfo(cli->tree, tctx, &r), + "rap_netservergetinfo level 1 failed"); + torture_assert_int_equal(tctx, r.out.status, 0, + "rap_netservergetinfo level 1 failed"); + + torture_assert_str_equal(tctx, (const char *)r.out.info.info1.name, server_name, "server name"); + torture_assert_int_equal(tctx, r.out.info.info1.version_major, info.info101->version_major, "version major"); + torture_assert_int_equal(tctx, r.out.info.info1.version_minor, info.info101->version_minor, "version minor"); + torture_assert_int_equal(tctx, r.out.info.info1.servertype, info.info101->server_type, "server_type"); + torture_assert_str_equal(tctx, r.out.info.info1.comment, info.info101->comment, "comment"); + + talloc_free(p); + + return true; +} + +struct torture_suite *torture_rap_rpc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "rpc"); + + torture_suite_add_1smb_test(suite, "netservergetinfo", + test_rpc_netservergetinfo); + + suite->description = talloc_strdup(suite, + "RAP / DCERPC consistency tests"); + + return suite; +} diff --git a/source4/torture/rap/sam.c b/source4/torture/rap/sam.c new file mode 100644 index 0000000..3c13849 --- /dev/null +++ b/source4/torture/rap/sam.c @@ -0,0 +1,376 @@ +/* + Unix SMB/CIFS implementation. + test suite for RAP sam operations + + Copyright (C) Guenther Deschner 2010-2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "libcli/rap/rap.h" +#include "torture/rap/proto.h" +#include "../libcli/auth/libcli_auth.h" +#include "torture/rpc/torture_rpc.h" + +#include +#include + +#define TEST_RAP_USER "torture_rap_user" + +static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) +{ + size_t len = MAX(8, min_len); + char *s = generate_random_password(mem_ctx, len, len+6); + printf("Generated password '%s'\n", s); + return s; +} + +static bool test_userpasswordset2_args(struct torture_context *tctx, + struct smbcli_state *cli, + const char *username, + const char **password) +{ + struct rap_NetUserPasswordSet2 r; + char *newpass = samr_rand_pass(tctx, 8); + + ZERO_STRUCT(r); + + r.in.UserName = username; + + memcpy(r.in.OldPassword, *password, MIN(strlen(*password), 16)); + memcpy(r.in.NewPassword, newpass, MIN(strlen(newpass), 16)); + r.in.EncryptedPassword = 0; + r.in.RealPasswordLength = strlen(newpass); + + torture_comment(tctx, "Testing rap_NetUserPasswordSet2(%s)\n", r.in.UserName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuserpasswordset2(cli->tree, tctx, &r), + "smbcli_rap_netuserpasswordset2 failed"); + if (!W_ERROR_IS_OK(W_ERROR(r.out.status))) { + torture_warning(tctx, "RAP NetUserPasswordSet2 gave: %s\n", + win_errstr(W_ERROR(r.out.status))); + } else { + *password = newpass; + } + + return true; +} + +static bool test_userpasswordset2_crypt_args(struct torture_context *tctx, + struct smbcli_state *cli, + const char *username, + const char **password) +{ + struct rap_NetUserPasswordSet2 r; + char *newpass = samr_rand_pass(tctx, 8); + + r.in.UserName = username; + + E_deshash(*password, r.in.OldPassword); + E_deshash(newpass, r.in.NewPassword); + + r.in.RealPasswordLength = strlen(newpass); + r.in.EncryptedPassword = 1; + + torture_comment(tctx, "Testing rap_NetUserPasswordSet2(%s)\n", r.in.UserName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuserpasswordset2(cli->tree, tctx, &r), + "smbcli_rap_netuserpasswordset2 failed"); + if (!W_ERROR_IS_OK(W_ERROR(r.out.status))) { + torture_warning(tctx, "RAP NetUserPasswordSet2 gave: %s\n", + win_errstr(W_ERROR(r.out.status))); + } else { + *password = newpass; + } + + return true; +} + +static bool test_userpasswordset2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct test_join *join_ctx; + const char *password; + bool ret = true; + + join_ctx = torture_create_testuser_max_pwlen(tctx, TEST_RAP_USER, + torture_setting_string(tctx, "workgroup", NULL), + ACB_NORMAL, + &password, 14); + if (join_ctx == NULL) { + torture_fail(tctx, "failed to create user\n"); + } + + ret &= test_userpasswordset2_args(tctx, cli, TEST_RAP_USER, &password); + ret &= test_userpasswordset2_crypt_args(tctx, cli, TEST_RAP_USER, &password); + + torture_leave_domain(tctx, join_ctx); + + return ret; +} + +static bool test_oemchangepassword_args(struct torture_context *tctx, + struct smbcli_state *cli, + const char *username, + const char **password) +{ + struct rap_NetOEMChangePassword r; + + const char *oldpass = *password; + char *newpass = samr_rand_pass(tctx, 9); + uint8_t old_pw_hash[16]; + uint8_t new_pw_hash[16]; + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t pw_key = { + .data = old_pw_hash, + .size = sizeof(old_pw_hash), + }; + + r.in.UserName = username; + + E_deshash(oldpass, old_pw_hash); + E_deshash(newpass, new_pw_hash); + + encode_pw_buffer(r.in.crypt_password, newpass, STR_ASCII); + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &pw_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, + r.in.crypt_password, + 516); + gnutls_cipher_deinit(cipher_hnd); + E_old_pw_hash(new_pw_hash, old_pw_hash, r.in.password_hash); + + torture_comment(tctx, "Testing rap_NetOEMChangePassword(%s)\n", r.in.UserName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netoemchangepassword(cli->tree, tctx, &r), + "smbcli_rap_netoemchangepassword failed"); + if (!W_ERROR_IS_OK(W_ERROR(r.out.status))) { + torture_warning(tctx, "RAP NetOEMChangePassword gave: %s\n", + win_errstr(W_ERROR(r.out.status))); + } else { + *password = newpass; + } + + return true; +} + +static bool test_oemchangepassword(struct torture_context *tctx, + struct smbcli_state *cli) +{ + + struct test_join *join_ctx; + const char *password; + bool ret; + + join_ctx = torture_create_testuser_max_pwlen(tctx, TEST_RAP_USER, + torture_setting_string(tctx, "workgroup", NULL), + ACB_NORMAL, + &password, 14); + if (join_ctx == NULL) { + torture_fail(tctx, "failed to create user\n"); + } + + ret = test_oemchangepassword_args(tctx, cli, TEST_RAP_USER, &password); + + torture_leave_domain(tctx, join_ctx); + + return ret; +} + +static bool test_usergetinfo_byname(struct torture_context *tctx, + struct smbcli_state *cli, + const char *UserName) +{ + struct rap_NetUserGetInfo r; + int i; + uint16_t levels[] = { 0, 1, 2, 10, 11 }; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.UserName = UserName; + r.in.level = levels[i]; + r.in.bufsize = 8192; + + torture_comment(tctx, + "Testing rap_NetUserGetInfo(%s) level %d\n", r.in.UserName, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netusergetinfo(cli->tree, tctx, &r), + "smbcli_rap_netusergetinfo failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netusergetinfo failed"); + } + + return true; +} + +static bool test_usergetinfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ + + struct test_join *join_ctx; + const char *password; + bool ret; + + join_ctx = torture_create_testuser_max_pwlen(tctx, TEST_RAP_USER, + torture_setting_string(tctx, "workgroup", NULL), + ACB_NORMAL, + &password, 14); + if (join_ctx == NULL) { + torture_fail(tctx, "failed to create user\n"); + } + + ret = test_usergetinfo_byname(tctx, cli, TEST_RAP_USER); + + torture_leave_domain(tctx, join_ctx); + + return ret; +} + +static bool test_useradd(struct torture_context *tctx, + struct smbcli_state *cli) +{ + + struct rap_NetUserAdd r; + struct rap_NetUserInfo1 info1; + int i; + uint16_t levels[] = { 1 }; + const char *username = TEST_RAP_USER; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + const char *pwd; + + pwd = generate_random_password(tctx, 9, 16); + + r.in.level = levels[i]; + r.in.bufsize = 0xffff; + r.in.pwdlength = strlen(pwd); + r.in.unknown = 0; + + switch (r.in.level) { + case 1: + ZERO_STRUCT(info1); + + info1.Name = username; + memcpy(info1.Password, pwd, MIN(strlen(pwd), 16)); + info1.Priv = USER_PRIV_USER; + info1.Flags = 0x21; + info1.HomeDir = "home_dir"; + info1.Comment = "comment"; + info1.ScriptPath = "logon_script"; + + r.in.info.info1 = info1; + break; + } + + torture_comment(tctx, + "Testing rap_NetUserAdd(%s) level %d\n", username, r.in.level); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuseradd(cli->tree, tctx, &r), + "smbcli_rap_netuseradd failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netuseradd failed"); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuseradd(cli->tree, tctx, &r), + "2nd smbcli_rap_netuseradd failed"); + torture_assert_werr_equal(tctx, W_ERROR(r.out.status), WERR_NERR_USEREXISTS, + "2nd smbcli_rap_netuseradd failed"); + + { + struct rap_NetUserDelete d; + + d.in.UserName = username; + + smbcli_rap_netuserdelete(cli->tree, tctx, &d); + } + } + + return true; +} + +static bool test_userdelete(struct torture_context *tctx, + struct smbcli_state *cli) +{ + + struct rap_NetUserDelete r; + + { + struct rap_NetUserAdd a; + const char *pwd; + + ZERO_STRUCT(a.in.info.info1); + + pwd = generate_random_password(tctx, 9, 16); + + a.in.level = 1; + a.in.bufsize = 0xffff; + a.in.pwdlength = strlen(pwd); + a.in.unknown = 0; + a.in.info.info1.Name = TEST_RAP_USER; + a.in.info.info1.Priv = USER_PRIV_USER; + + memcpy(a.in.info.info1.Password, pwd, MIN(strlen(pwd), 16)); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuseradd(cli->tree, tctx, &a), + "smbcli_rap_netuseradd failed"); + } + + r.in.UserName = TEST_RAP_USER; + + torture_comment(tctx, + "Testing rap_NetUserDelete(%s)\n", r.in.UserName); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuserdelete(cli->tree, tctx, &r), + "smbcli_rap_netuserdelete failed"); + torture_assert_werr_ok(tctx, W_ERROR(r.out.status), + "smbcli_rap_netuserdelete failed"); + + torture_assert_ntstatus_ok(tctx, + smbcli_rap_netuserdelete(cli->tree, tctx, &r), + "2nd smbcli_rap_netuserdelete failed"); + torture_assert_werr_equal(tctx, W_ERROR(r.out.status), WERR_NERR_USERNOTFOUND, + "2nd smbcli_rap_netuserdelete failed"); + + return true; +} + +struct torture_suite *torture_rap_sam(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "sam"); + + torture_suite_add_1smb_test(suite, "userpasswordset2", test_userpasswordset2); + torture_suite_add_1smb_test(suite, "oemchangepassword", test_oemchangepassword); + torture_suite_add_1smb_test(suite, "usergetinfo", test_usergetinfo); + torture_suite_add_1smb_test(suite, "useradd", test_useradd); + torture_suite_add_1smb_test(suite, "userdelete", test_userdelete); + + return suite; +} diff --git a/source4/torture/raw/acls.c b/source4/torture/raw/acls.c new file mode 100644 index 0000000..6a56514 --- /dev/null +++ b/source4/torture/raw/acls.c @@ -0,0 +1,2556 @@ +/* + Unix SMB/CIFS implementation. + + test security descriptor operations + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "librpc/gen_ndr/lsa.h" +#include "libcli/smb2/smb2.h" +#include "libcli/util/clilsa.h" +#include "libcli/security/security.h" +#include "torture/util.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\testsd" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + goto done; \ + }} while (0) + +#define FAIL_UNLESS(__cond) \ + do { \ + if (__cond) {} else { \ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "%s) condition violated: %s\n", \ + __location__, #__cond); \ + goto done; \ + } \ + } while(0) + +#define CHECK_SECURITY_DESCRIPTOR(_sd1, _sd2) do { \ + if (!security_descriptor_equal(_sd1, _sd2)) { \ + torture_warning(tctx, "%s: security descriptors don't match!\n", __location__); \ + torture_warning(tctx, "got:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd1); \ + torture_warning(tctx, "expected:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd2); \ + ret = false; \ + } \ +} while (0) + +/* + * Helper function to verify a security descriptor, by querying + * and comparing against the passed in sd. + * Copied to smb2_util_verify_sd() for SMB2. + */ +static bool verify_sd(TALLOC_CTX *tctx, struct smbcli_state *cli, + int fnum, struct security_descriptor *sd) +{ + NTSTATUS status; + bool ret = true; + union smb_fileinfo q = {}; + + if (sd) { + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* More work is needed if we're going to check this bit. */ + sd->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + } + + done: + return ret; +} + +/* + * Helper function to verify attributes, by querying + * and comparing against the passed attrib. + * Copied to smb2_util_verify_attrib() for SMB2. + */ +static bool verify_attrib(TALLOC_CTX *tctx, struct smbcli_state *cli, + int fnum, uint32_t attrib) +{ + NTSTATUS status; + bool ret = true; + union smb_fileinfo q2 = {}; + + if (attrib) { + q2.standard.level = RAW_FILEINFO_STANDARD; + q2.standard.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &q2); + CHECK_STATUS(status, NT_STATUS_OK); + + q2.standard.out.attrib &= ~FILE_ATTRIBUTE_ARCHIVE; + + if (q2.standard.out.attrib != attrib) { + torture_warning(tctx, "%s: attributes don't match! " + "got %x, expected %x\n", __location__, + (uint32_t)q2.standard.out.attrib, + (uint32_t)attrib); + ret = false; + } + } + + done: + return ret; +} + +/** + * Test setting and removing a DACL. + * Test copied to torture_smb2_setinfo() for SMB2. + */ +static bool test_sd(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\sd.txt"; + bool ret = true; + int fnum = -1; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_ace ace = {}; + struct security_descriptor *sd; + const struct dom_sid *test_sid; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING SETFILEINFO EA_SET\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd = q.query_secdesc.out.sd; + + torture_comment(tctx, "add a new ACE to the DACL\n"); + + test_sid = &global_sid_Authenticated_Users; + + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL; + ace.trustee = *test_sid; + + status = security_descriptor_dacl_add(sd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = q.query_secdesc.in.secinfo_flags; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + FAIL_UNLESS(verify_sd(tctx, cli, fnum, sd)); + + torture_comment(tctx, "remove it again\n"); + + status = security_descriptor_dacl_del(sd, test_sid); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + FAIL_UNLESS(verify_sd(tctx, cli, fnum, sd)); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test using nttrans create to create a file with an initial acl set + Test copied to test_create_acl() for SMB2. +*/ +static bool test_nttrans_create_ext(struct torture_context *tctx, + struct smbcli_state *cli, bool test_dir) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\acl2.txt"; + bool ret = true; + int fnum = -1; + union smb_fileinfo q = {}; + struct security_ace ace; + struct security_descriptor *sd; + const struct dom_sid *test_sid; + uint32_t attrib = + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + (test_dir ? FILE_ATTRIBUTE_DIRECTORY : 0); + NTSTATUS (*delete_func)(struct smbcli_tree *, const char *) = + test_dir ? smbcli_rmdir : smbcli_unlink; + + ZERO_STRUCT(ace); + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = + test_dir ? NTCREATEX_OPTIONS_DIRECTORY : 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.sec_desc = NULL; + io.ntcreatex.in.ea_list = NULL; + + torture_comment(tctx, "basic create\n"); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "querying ACL\n"); + + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd = q.query_secdesc.out.sd; + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + status = delete_func(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "adding a new ACE\n"); + test_sid = &global_sid_Authenticated_Users; + + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL; + ace.trustee = *test_sid; + + status = security_descriptor_dacl_add(sd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "creating with an initial ACL\n"); + + io.ntcreatex.in.sec_desc = sd; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + FAIL_UNLESS(verify_sd(tctx, cli, fnum, sd)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + status = delete_func(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "creating with attributes\n"); + + io.ntcreatex.in.sec_desc = NULL; + io.ntcreatex.in.file_attr = attrib; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + FAIL_UNLESS(verify_attrib(tctx, cli, fnum, attrib)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + status = delete_func(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "creating with attributes and ACL\n"); + + io.ntcreatex.in.sec_desc = sd; + io.ntcreatex.in.file_attr = attrib; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + FAIL_UNLESS(verify_sd(tctx, cli, fnum, sd)); + FAIL_UNLESS(verify_attrib(tctx, cli, fnum, attrib)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + status = delete_func(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test using nttrans create to create a file and directory with an initial acl + and owner. +*/ +static bool test_nttrans_create_ext_owner( + struct torture_context *tctx, + struct smbcli_state *cli, bool test_dir) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\foo.txt"; + bool ret = true; + int fnum = -1; + struct security_ace ace; + struct security_descriptor *sd; + uint32_t attrib = + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + (test_dir ? FILE_ATTRIBUTE_DIRECTORY : 0); + NTSTATUS (*delete_func)(struct smbcli_tree *, const char *) = + test_dir ? smbcli_rmdir : smbcli_unlink; + + ZERO_STRUCT(ace); + + smbcli_deltree(cli->tree, BASEDIR); + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = + test_dir ? NTCREATEX_OPTIONS_DIRECTORY : 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.sec_desc = NULL; + io.ntcreatex.in.ea_list = NULL; + + torture_comment(tctx, "creating with attributes, ACL and owner\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, SID_WORLD, SID_BUILTIN_USERS, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + io.ntcreatex.in.sec_desc = sd; + io.ntcreatex.in.file_attr = attrib; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + FAIL_UNLESS(verify_sd(tctx, cli, fnum, sd)); + FAIL_UNLESS(verify_attrib(tctx, cli, fnum, attrib)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + status = delete_func(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +static bool test_nttrans_create_file(struct torture_context *tctx, + struct smbcli_state *cli) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc on files\n"); + + return test_nttrans_create_ext(tctx, cli, false); +} + +static bool test_nttrans_create_dir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc on directories\n"); + + return test_nttrans_create_ext(tctx, cli, true); +} + +static bool test_nttrans_create_owner_file(struct torture_context *tctx, + struct smbcli_state *cli) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc with owner on file\n"); + + return test_nttrans_create_ext_owner(tctx, cli, false); +} + +static bool test_nttrans_create_owner_dir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc with owner on directory\n"); + + return test_nttrans_create_ext_owner(tctx, cli, true); +} + +#define CHECK_ACCESS_FLAGS(_fnum, flags) do { \ + union smb_fileinfo _q; \ + _q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; \ + _q.access_information.in.file.fnum = (_fnum); \ + status = smb_raw_fileinfo(cli->tree, tctx, &_q); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if (_q.access_information.out.access_flags != (flags)) { \ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect access_flags 0x%08x - should be 0x%08x\n", \ + __location__, _q.access_information.out.access_flags, (flags)); \ + goto done; \ + } \ +} while (0) + +/* + test using NTTRANS CREATE to create a file with a null ACL set + Test copied to test_create_null_dacl() for SMB2. +*/ +static bool test_nttrans_create_null_dacl(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\nulldacl.txt"; + bool ret = true; + int fnum = -1; + union smb_fileinfo q; + union smb_setfileinfo s; + struct security_descriptor *sd = security_descriptor_initialise(tctx); + struct security_acl dacl; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING SEC_DESC WITH A NULL DACL\n"); + + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC + | SEC_STD_WRITE_OWNER; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.sec_desc = sd; + io.ntcreatex.in.ea_list = NULL; + + torture_comment(tctx, "creating a file with a empty sd\n"); + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Testing the created DACL, + * the server should add the inherited DACL + * when SEC_DESC_DACL_PRESENT isn't specified + */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL_PRESENT flag not set by the server!\n"); + goto done; + } + if (q.query_secdesc.out.sd->dacl == NULL) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "no DACL has been created on the server!\n"); + goto done; + } + + torture_comment(tctx, "set NULL DACL\n"); + sd->type |= SEC_DESC_DACL_PRESENT; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.fnum = fnum; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL_PRESENT flag not set by the server!\n"); + goto done; + } + if (q.query_secdesc.out.sd->dacl != NULL) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL has been created on the server!\n"); + goto done; + } + + torture_comment(tctx, "try open for read control\n"); + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_STD_READ_CONTROL | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for write\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for read\n"); + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for generic write\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_RIGHTS_FILE_WRITE | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for generic read\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_RIGHTS_FILE_READ | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "set DACL with 0 aces\n"); + ZERO_STRUCT(dacl); + dacl.revision = SECURITY_ACL_REVISION_NT4; + dacl.num_aces = 0; + sd->dacl = &dacl; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.fnum = fnum; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL_PRESENT flag not set by the server!\n"); + goto done; + } + if (q.query_secdesc.out.sd->dacl == NULL) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "no DACL has been created on the server!\n"); + goto done; + } + if (q.query_secdesc.out.sd->dacl->num_aces != 0) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL has %u aces!\n", + q.query_secdesc.out.sd->dacl->num_aces); + goto done; + } + + torture_comment(tctx, "try open for read control\n"); + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_STD_READ_CONTROL | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for write => access_denied\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read => access_denied\n"); + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic write => access_denied\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read => access_denied\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "set empty sd\n"); + sd->type &= ~SEC_DESC_DACL_PRESENT; + sd->dacl = NULL; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.fnum = fnum; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL_PRESENT flag not set by the server!\n"); + goto done; + } + if (q.query_secdesc.out.sd->dacl != NULL) { + ret = false; + torture_result(tctx, TORTURE_FAIL, "DACL has been created on the server!\n"); + goto done; + } +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test the behaviour of the well known SID_CREATOR_OWNER sid, and some generic + mapping bits + Test copied to smb2/acls.c for SMB2. +*/ +static bool test_creator_sid(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\creator.txt"; + bool ret = true; + int fnum = -1; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig, *sd2; + const char *owner_sid; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING SID_CREATOR_OWNER\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "try open for write\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic write\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "set a sec desc allowing no write by owner\n"); + sd = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "check that sd has been mapped correctly\n"); + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + torture_comment(tctx, "try open for write\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_FILE_READ_DATA| + SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for generic write\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_RIGHTS_FILE_READ); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "set a sec desc allowing generic read by owner\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "check that generic read has been mapped correctly\n"); + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + torture_comment(tctx, "try open for write\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + SEC_FILE_READ_DATA | + SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + torture_comment(tctx, "try open for generic write\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.ntcreatex.in.access_mask = SEC_GENERIC_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, SEC_RIGHTS_FILE_READ); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test the mapping of the SEC_GENERIC_xx bits to SEC_STD_xx and + SEC_FILE_xx bits + Test copied to smb2/acls.c for SMB2. +*/ +static bool test_generic_bits(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\generic.txt"; + bool ret = true; + int fnum = -1, i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig, *sd2; + const char *owner_sid; + const struct { + uint32_t gen_bits; + uint32_t specific_bits; + } file_mappings[] = { + { 0, 0 }, + { SEC_GENERIC_READ, SEC_RIGHTS_FILE_READ }, + { SEC_GENERIC_WRITE, SEC_RIGHTS_FILE_WRITE }, + { SEC_GENERIC_EXECUTE, SEC_RIGHTS_FILE_EXECUTE }, + { SEC_GENERIC_ALL, SEC_RIGHTS_FILE_ALL }, + { SEC_FILE_READ_DATA, SEC_FILE_READ_DATA }, + { SEC_FILE_READ_ATTRIBUTE, SEC_FILE_READ_ATTRIBUTE } + }; + const struct { + uint32_t gen_bits; + uint32_t specific_bits; + } dir_mappings[] = { + { 0, 0 }, + { SEC_GENERIC_READ, SEC_RIGHTS_DIR_READ }, + { SEC_GENERIC_WRITE, SEC_RIGHTS_DIR_WRITE }, + { SEC_GENERIC_EXECUTE, SEC_RIGHTS_DIR_EXECUTE }, + { SEC_GENERIC_ALL, SEC_RIGHTS_DIR_ALL } + }; + bool has_restore_privilege; + bool has_take_ownership_privilege; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING FILE GENERIC BITS\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", + nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", + nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); + + for (i=0;itree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + file_mappings[i].specific_bits, + 0, + NULL); + + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + expected_mask | file_mappings[i].specific_bits); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + if (!has_take_ownership_privilege) { + continue; + } + + torture_comment(tctx, "Testing generic bits 0x%08x (anonymous)\n", + file_mappings[i].gen_bits); + sd = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + file_mappings[i].gen_bits, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + sd2 = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + file_mappings[i].specific_bits, + 0, + NULL); + + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + expected_mask_anon | file_mappings[i].specific_bits); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + } + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + + torture_comment(tctx, "TESTING DIR GENERIC BITS\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", + nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", + nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); + + for (i=0;itree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + dir_mappings[i].specific_bits, + 0, + NULL); + + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + expected_mask | dir_mappings[i].specific_bits); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + if (!has_take_ownership_privilege) { + continue; + } + + torture_comment(tctx, "Testing generic bits 0x%08x (anonymous)\n", + file_mappings[i].gen_bits); + sd = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + file_mappings[i].gen_bits, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + sd2 = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + file_mappings[i].specific_bits, + 0, + NULL); + + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, + expected_mask_anon | dir_mappings[i].specific_bits); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + } + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + see what access bits the owner of a file always gets + Test copied to smb2/acls.c for SMB2. +*/ +static bool test_owner_bits(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\test_owner_bits.txt"; + bool ret = true; + int fnum = -1, i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig; + const char *owner_sid; + bool has_restore_privilege; + bool has_take_ownership_privilege; + uint32_t expected_bits; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING FILE OWNER BITS\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = torture_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "torture_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + expected_bits = SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE; + + for (i=0;i<16;i++) { + uint32_t bit = (1<tree, tctx, &io); + if (expected_bits & bit) { + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "failed with access mask 0x%08x of expected 0x%08x\n", + bit, expected_bits); + } + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, bit | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + } else { + if (NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "open succeeded with access mask 0x%08x of " + "expected 0x%08x - should fail\n", + bit, expected_bits); + } + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + } + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + + +/* + test the inheritance of ACL flags onto new files and directories + Test copied to smb2/acls.c for SMB2. +*/ +static bool test_inheritance(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + const char *fname2 = BASEDIR "\\inheritance\\testdir"; + bool ret = true; + int fnum=0, fnum2, i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd2, *sd_orig=NULL, *sd_def1, *sd_def2; + const char *owner_sid, *group_sid; + const struct dom_sid *creator_owner; + const struct { + uint32_t parent_flags; + uint32_t file_flags; + uint32_t dir_flags; + } test_flags[] = { + { + 0, + 0, + 0 + }, + { + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY, + }, + { + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + } + }; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACL INHERITANCE\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + group_sid = dom_sid_string(tctx, sd_orig->group_sid); + + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + torture_comment(tctx, "group_sid is %s\n", group_sid); + + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + + if (torture_setting_bool(tctx, "samba4", false)) { + /* the default ACL in Samba4 includes the group and + other permissions */ + sd_def1 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + group_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE, + 0, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE, + 0, + SID_NT_SYSTEM, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + NULL); + } else { + /* + * The Windows Default ACL for a new file, when there is no ACL to be + * inherited: FullControl for the owner and SYSTEM. + */ + sd_def1 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + SID_NT_SYSTEM, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + NULL); + } + + /* + * Use this in the case the system being tested does not add an ACE for + * the SYSTEM SID. + */ + sd_def2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + NULL); + + creator_owner = dom_sid_parse_talloc(tctx, SID_CREATOR_OWNER); + + for (i=0;itree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + io.ntcreatex.in.fname = fname1; + io.ntcreatex.in.create_options = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + q.query_secdesc.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum2); + smbcli_unlink(cli->tree, fname1); + + if (!(test_flags[i].parent_flags & SEC_ACE_FLAG_OBJECT_INHERIT)) { + if (!security_descriptor_equal(q.query_secdesc.out.sd, sd_def1) && + !security_descriptor_equal(q.query_secdesc.out.sd, sd_def2)) { + torture_warning(tctx, "Expected default sd " + "for i=%d:\n", i); + NDR_PRINT_DEBUG(security_descriptor, sd_def1); + torture_warning(tctx, "at %d - got:\n", i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + } + goto check_dir; + } + + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid)) { + ret = false; + torture_warning(tctx, "Bad sd in child file at %d\n", i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + goto check_dir; + } + + if (q.query_secdesc.out.sd->dacl->aces[0].flags != + test_flags[i].file_flags) { + torture_warning(tctx, "incorrect file_flags 0x%x - expected 0x%x for parent 0x%x with (i=%d)\n", + q.query_secdesc.out.sd->dacl->aces[0].flags, + test_flags[i].file_flags, + test_flags[i].parent_flags, + i); + ret = false; + } + + check_dir: + io.ntcreatex.in.fname = fname2; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + q.query_secdesc.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum2); + smbcli_rmdir(cli->tree, fname2); + + if (!(test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) && + (!(test_flags[i].parent_flags & SEC_ACE_FLAG_OBJECT_INHERIT) || + (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT))) { + if (!security_descriptor_equal(q.query_secdesc.out.sd, sd_def1) && + !security_descriptor_equal(q.query_secdesc.out.sd, sd_def2)) { + torture_warning(tctx, "Expected default sd for dir at %d:\n", i); + NDR_PRINT_DEBUG(security_descriptor, sd_def1); + torture_warning(tctx, "got:\n"); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + } + continue; + } + + if ((test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) && + (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid) || + q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) { + torture_warning(tctx, "(CI & NP) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_comment(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } else if (test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 2 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid) || + q.query_secdesc.out.sd->dacl->aces[1].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[1].trustee, + creator_owner) || + q.query_secdesc.out.sd->dacl->aces[0].flags != 0 || + q.query_secdesc.out.sd->dacl->aces[1].flags != + (test_flags[i].dir_flags | SEC_ACE_FLAG_INHERIT_ONLY)) { + torture_warning(tctx, "(CI) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_comment(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } else { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + creator_owner) || + q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) { + torture_warning(tctx, "(0) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_comment(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } + } + + torture_comment(tctx, "Testing access checks on inherited create with %s\n", fname1); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Check DACL we just set. */ + torture_comment(tctx, "checking new sd\n"); + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + io.ntcreatex.in.fname = fname1; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_ACCESS_FLAGS(fnum2, SEC_RIGHTS_FILE_ALL); + + q.query_secdesc.in.file.fnum = fnum2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, fnum2); + + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + 0, + NULL); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + status = smb_raw_open(cli->tree, tctx, &io); + if (NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "failed: w2k3 ACL bug (allowed open when ACL should deny)\n"); + ret = false; + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_ACCESS_FLAGS(fnum2, SEC_RIGHTS_FILE_ALL); + smbcli_close(cli->tree, fnum2); + } else { + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + } + + torture_comment(tctx, "trying without execute\n"); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL & ~SEC_FILE_EXECUTE; + status = smb_raw_open(cli->tree, tctx, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "and with full permissions again\n"); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + status = smb_raw_open(cli->tree, tctx, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_ACCESS_FLAGS(fnum2, SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, fnum2); + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + status = smb_raw_open(cli->tree, tctx, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_ACCESS_FLAGS(fnum2, SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE); + smbcli_close(cli->tree, fnum2); + +done: + if (sd_orig != NULL) { + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + } + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname1); + smbcli_rmdir(cli->tree, dname); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + if (!ret) { + torture_result(tctx, + TORTURE_FAIL, "(%s) test_inheritance\n", + __location__); + } + + return ret; +} + +static bool test_inheritance_flags(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + bool ret = true; + int fnum=0, fnum2, i, j; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd2, *sd_orig=NULL; + const char *owner_sid; + struct { + uint32_t parent_set_sd_type; /* 3 options */ + uint32_t parent_set_ace_inherit; /* 1 option */ + uint32_t parent_get_sd_type; + uint32_t parent_get_ace_inherit; + uint32_t child_get_sd_type; + uint32_t child_get_ace_inherit; + } tflags[16] = {{0}}; /* 2^4 */ + + for (i = 0; i < 15; i++) { + torture_comment(tctx, "i=%d:", i); + + ZERO_STRUCT(tflags[i]); + + if (i & 1) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + torture_comment(tctx, "AUTO_INHERITED, "); + } + if (i & 2) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERIT_REQ; + torture_comment(tctx, "AUTO_INHERIT_REQ, "); + } + if (i & 4) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_PROTECTED; + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_PROTECTED; + torture_comment(tctx, "PROTECTED, "); + } + if (i & 8) { + tflags[i].parent_set_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + tflags[i].parent_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, "INHERITED, "); + } + + if ((tflags[i].parent_set_sd_type & + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) == + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) { + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent is AUTO INHERITED"); + } + + if (tflags[i].parent_set_ace_inherit & + SEC_ACE_FLAG_INHERITED_ACE) { + tflags[i].parent_get_ace_inherit = + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent ACE is INHERITED"); + } + + torture_comment(tctx, "\n"); + } + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACL INHERITANCE FLAGS\n"); + + ZERO_STRUCT(io); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + torture_comment(tctx, "creating initial directory %s\n", dname); + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "getting original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + for (i=0; i < ARRAY_SIZE(tflags); i++) { + torture_comment(tctx, "setting a new sd on directory, pass #%d\n", i); + + sd = security_descriptor_dacl_create(tctx, + tflags[i].parent_set_sd_type, + NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + tflags[i].parent_set_ace_inherit, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check DACL we just set, except change the bits to what they + * should be. + */ + torture_comment(tctx, " checking new sd\n"); + + /* REQ bit should always be false. */ + sd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + + if ((tflags[i].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + /* Create file. */ + torture_comment(tctx, " creating file %s\n", fname1); + io.ntcreatex.in.fname = fname1; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_ACCESS_FLAGS(fnum2, SEC_RIGHTS_FILE_ALL); + + q.query_secdesc.in.file.fnum = fnum2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " checking sd on file %s\n", fname1); + sd2 = security_descriptor_dacl_create(tctx, + tflags[i].child_get_sd_type, + owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + tflags[i].child_get_ace_inherit, + NULL); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + /* + * Set new sd on file ... prove that the bits have nothing to + * do with the parents bits when manually setting an ACL. The + * _AUTO_INHERITED bit comes directly from the ACL set. + */ + for (j = 0; j < ARRAY_SIZE(tflags); j++) { + torture_comment(tctx, " setting new file sd, pass #%d\n", j); + + /* Change sd type. */ + sd2->type &= ~(SEC_DESC_DACL_AUTO_INHERITED | + SEC_DESC_DACL_AUTO_INHERIT_REQ | + SEC_DESC_DACL_PROTECTED); + sd2->type |= tflags[j].parent_set_sd_type; + + sd2->dacl->aces[0].flags &= + ~SEC_ACE_FLAG_INHERITED_ACE; + sd2->dacl->aces[0].flags |= + tflags[j].parent_set_ace_inherit; + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum2; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd2; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Check DACL we just set. */ + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + if ((tflags[j].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.fnum = fnum2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + } + + smbcli_close(cli->tree, fnum2); + smbcli_unlink(cli->tree, fname1); + } + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + if (!ret) { + torture_result(tctx, + TORTURE_FAIL, "(%s) test_inheritance_flags\n", + __location__); + } + + return ret; +} + +/* + test dynamic acl inheritance + Test copied to smb2/acls.c for SMB2. +*/ +static bool test_inheritance_dynamic(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *dname = BASEDIR "\\inheritance2"; + const char *fname1 = BASEDIR "\\inheritance2\\testfile"; + bool ret = true; + int fnum=0, fnum2; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig=NULL; + const char *owner_sid; + + torture_comment(tctx, "TESTING DYNAMIC ACL INHERITANCE\n"); + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ; + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "create a file with an inherited acl\n"); + io.ntcreatex.in.fname = fname1; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + smbcli_close(cli->tree, fnum2); + + torture_comment(tctx, "try and access file with base rights - should be OK\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + smbcli_close(cli->tree, fnum2); + + torture_comment(tctx, "try and access file with extra rights - should be denied\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "update parent sd\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ; + + set.set_secdesc.in.sd = sd; + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "try and access file with base rights - should be OK\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + smbcli_close(cli->tree, fnum2); + + + torture_comment(tctx, "try and access now - should be OK if dynamic inheritance works\n"); + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE; + status = smb_raw_open(cli->tree, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "Server does not have dynamic inheritance\n"); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Server does have dynamic inheritance\n"); + } + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + smbcli_unlink(cli->tree, fname1); + +done: + if (sd_orig != NULL) { + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb_raw_setfileinfo(cli->tree, &set); + } + smbcli_close(cli->tree, fnum); + smbcli_rmdir(cli->tree, dname); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +#define CHECK_STATUS_FOR_BIT_ACTION(status, bits, action) do { \ + if (!(bits & desired_64)) {\ + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); \ + action; \ + } else { \ + CHECK_STATUS(status, NT_STATUS_OK); \ + } \ +} while (0) + +#define CHECK_STATUS_FOR_BIT(status, bits, access) do { \ + if (NT_STATUS_IS_OK(status)) { \ + if (!(granted & access)) {\ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s but flags 0x%08X are not granted! granted[0x%08X] desired[0x%08X]\n", \ + __location__, nt_errstr(status), access, granted, desired); \ + goto done; \ + } \ + } else { \ + if (granted & access) {\ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s but flags 0x%08X are granted! granted[0x%08X] desired[0x%08X]\n", \ + __location__, nt_errstr(status), access, granted, desired); \ + goto done; \ + } \ + } \ + CHECK_STATUS_FOR_BIT_ACTION(status, bits, do {} while (0)); \ +} while (0) + +#if 0 + +/* test what access mask is needed for getting and setting security_descriptors + Test copied to smb2/acls.c for SMB2. */ +static bool test_sd_get_set(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo fi; + union smb_setfileinfo si; + struct security_descriptor *sd; + struct security_descriptor *sd_owner = NULL; + struct security_descriptor *sd_group = NULL; + struct security_descriptor *sd_dacl = NULL; + struct security_descriptor *sd_sacl = NULL; + int fnum=0; + const char *fname = BASEDIR "\\sd_get_set.txt"; + uint64_t desired_64; + uint32_t desired = 0, granted; + int i = 0; +#define NO_BITS_HACK (((uint64_t)1)<<32) + uint64_t open_bits = + SEC_MASK_GENERIC | + SEC_FLAG_SYSTEM_SECURITY | + SEC_FLAG_MAXIMUM_ALLOWED | + SEC_STD_ALL | + SEC_FILE_ALL | + NO_BITS_HACK; + uint64_t get_owner_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_owner_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER; + uint64_t get_group_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_group_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER; + uint64_t get_dacl_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_dacl_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_DAC; + uint64_t get_sacl_bits = SEC_FLAG_SYSTEM_SECURITY; + uint64_t set_sacl_bits = SEC_FLAG_SYSTEM_SECURITY; + + if (!torture_setup_dir(cli, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACCESS MASKS FOR SD GET/SET\n"); + + /* first create a file with full access for everyone */ + sd = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, SID_BUILTIN_USERS, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + NULL); + sd->type |= SEC_DESC_SACL_PRESENT; + sd->sacl = NULL; + io.ntcreatex.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_GENERIC_ALL; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.sec_desc = sd; + io.ntcreatex.in.ea_list = NULL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * now try each access_mask bit and no bit at all in a loop + * and see what's allowed + * NOTE: if i == 32 it means access_mask = 0 (see NO_BITS_HACK above) + */ + for (i=0; i <= 32; i++) { + desired_64 = ((uint64_t)1) << i; + desired = (uint32_t)desired_64; + + /* first open the file with the desired access */ + io.ntcreatex.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.access_mask = desired; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS_FOR_BIT_ACTION(status, open_bits, goto next); + fnum = io.ntcreatex.out.file.fnum; + + /* then check what access was granted */ + fi.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + fi.access_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + granted = fi.access_information.out.access_flags; + + /* test the owner */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.fnum = fnum; + fi.query_secdesc.in.secinfo_flags = SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_owner_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_owner = fi.query_secdesc.out.sd; + } else if (!sd_owner) { + sd_owner = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.fnum = fnum; + si.set_secdesc.in.secinfo_flags = SECINFO_OWNER; + si.set_secdesc.in.sd = sd_owner; + status = smb_raw_setfileinfo(cli->tree, &si); + CHECK_STATUS_FOR_BIT(status, set_owner_bits, SEC_STD_WRITE_OWNER); + + /* test the group */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.fnum = fnum; + fi.query_secdesc.in.secinfo_flags = SECINFO_GROUP; + status = smb_raw_fileinfo(cli->tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_group_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_group = fi.query_secdesc.out.sd; + } else if (!sd_group) { + sd_group = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.fnum = fnum; + si.set_secdesc.in.secinfo_flags = SECINFO_GROUP; + si.set_secdesc.in.sd = sd_group; + status = smb_raw_setfileinfo(cli->tree, &si); + CHECK_STATUS_FOR_BIT(status, set_group_bits, SEC_STD_WRITE_OWNER); + + /* test the DACL */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.fnum = fnum; + fi.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_dacl_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_dacl = fi.query_secdesc.out.sd; + } else if (!sd_dacl) { + sd_dacl = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.fnum = fnum; + si.set_secdesc.in.secinfo_flags = SECINFO_DACL; + si.set_secdesc.in.sd = sd_dacl; + status = smb_raw_setfileinfo(cli->tree, &si); + CHECK_STATUS_FOR_BIT(status, set_dacl_bits, SEC_STD_WRITE_DAC); + + /* test the SACL */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.fnum = fnum; + fi.query_secdesc.in.secinfo_flags = SECINFO_SACL; + status = smb_raw_fileinfo(cli->tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_sacl_bits, SEC_FLAG_SYSTEM_SECURITY); + if (fi.query_secdesc.out.sd) { + sd_sacl = fi.query_secdesc.out.sd; + } else if (!sd_sacl) { + sd_sacl = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.fnum = fnum; + si.set_secdesc.in.secinfo_flags = SECINFO_SACL; + si.set_secdesc.in.sd = sd_sacl; + status = smb_raw_setfileinfo(cli->tree, &si); + CHECK_STATUS_FOR_BIT(status, set_sacl_bits, SEC_FLAG_SYSTEM_SECURITY); + + /* close the handle */ + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); +next: + continue; + } + +done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +#endif + +/* + basic testing of security descriptor calls +*/ +struct torture_suite *torture_raw_acls(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "acls"); + + torture_suite_add_1smb_test(suite, "sd", test_sd); + torture_suite_add_1smb_test(suite, "create_file", test_nttrans_create_file); + torture_suite_add_1smb_test(suite, "create_dir", test_nttrans_create_dir); + torture_suite_add_1smb_test(suite, "create_owner_file", test_nttrans_create_owner_file); + torture_suite_add_1smb_test(suite, "create_owner_dir", test_nttrans_create_owner_dir); + torture_suite_add_1smb_test(suite, "nulldacl", test_nttrans_create_null_dacl); + torture_suite_add_1smb_test(suite, "creator", test_creator_sid); + torture_suite_add_1smb_test(suite, "generic", test_generic_bits); + torture_suite_add_1smb_test(suite, "owner", test_owner_bits); + torture_suite_add_1smb_test(suite, "inheritance", test_inheritance); + + torture_suite_add_1smb_test(suite, "INHERITFLAGS", test_inheritance_flags); + torture_suite_add_1smb_test(suite, "dynamic", test_inheritance_dynamic); +#if 0 + /* XXX This test does not work against XP or Vista. */ + torture_suite_add_1smb_test(suite, "GETSET", test_sd_get_set); +#endif + + return suite; +} diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c new file mode 100644 index 0000000..2afd7ea --- /dev/null +++ b/source4/torture/raw/chkpath.c @@ -0,0 +1,390 @@ +/* + Unix SMB/CIFS implementation. + chkpath individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/locale.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\rawchkpath" + +#define CHECK_STATUS(status, correct, dos_correct) do { \ + if (!NT_STATUS_EQUAL(status, correct) && !NT_STATUS_EQUAL(status, dos_correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + +static NTSTATUS single_search(struct smbcli_state *cli, + TALLOC_CTX *mem_ctx, const char *pattern) +{ + union smb_search_first io; + NTSTATUS status; + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = RAW_SEARCH_DATA_STANDARD; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 1; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + + status = smb_raw_search_first(cli->tree, mem_ctx, + &io, NULL, NULL); + + return status; +} + +static bool test_path_ex(struct smbcli_state *cli, struct torture_context *tctx, + const char *path, const char *path_expected, + NTSTATUS expected, NTSTATUS dos_expected) +{ + union smb_chkpath io; + union smb_fileinfo finfo; + NTSTATUS status; + + io.chkpath.in.path = path; + status = smb_raw_chkpath(cli->tree, &io); + if (!NT_STATUS_EQUAL(status, expected) && !NT_STATUS_EQUAL(status, dos_expected)) { + printf("FAILED %-30s chkpath %s should be %s or %s\n", + path, nt_errstr(status), nt_errstr(expected), nt_errstr(dos_expected)); + return false; + } else { + printf("%-30s chkpath correct (%s)\n", path, nt_errstr(status)); + } + + if (NT_STATUS_EQUAL(expected, NT_STATUS_NOT_A_DIRECTORY)) { + expected = NT_STATUS_OK; + } + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_NAME_INFO; + finfo.generic.in.file.path = path; + status = smb_raw_pathinfo(cli->tree, cli, &finfo); + if (!NT_STATUS_EQUAL(status, expected) && !NT_STATUS_EQUAL(status, dos_expected)) { + printf("FAILED: %-30s pathinfo %s should be %s or %s\n", + path, nt_errstr(status), nt_errstr(expected), nt_errstr(dos_expected)); + return false; + } + + if (!NT_STATUS_IS_OK(status)) { + printf("%-30s chkpath correct (%s)\n", path, nt_errstr(status)); + return true; + } + + if (path_expected && + (!finfo.name_info.out.fname.s || + strcmp(finfo.name_info.out.fname.s, path_expected) != 0)) { + if (tctx && torture_setting_bool(tctx, "samba4", false)) { + printf("IGNORE: %-30s => %-20s should be %s\n", + path, finfo.name_info.out.fname.s, path_expected); + return true; + } + printf("FAILED: %-30s => %-20s should be %s\n", + path, finfo.name_info.out.fname.s, path_expected); + return false; + } + printf("%-30s => %-20s correct\n", + path, finfo.name_info.out.fname.s); + + return true; +} + +static bool test_path(struct smbcli_state *cli, const char *path, + NTSTATUS expected, NTSTATUS dos_expected) +{ + return test_path_ex(cli, NULL, path, path, expected, dos_expected); +} + +static bool test_chkpath(struct smbcli_state *cli, struct torture_context *tctx) +{ + union smb_chkpath io; + NTSTATUS status; + bool ret = true; + int fnum = -1; + + io.chkpath.in.path = BASEDIR; + + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK, NT_STATUS_OK); + + ret &= test_path(cli, BASEDIR "\\nodir", NT_STATUS_OBJECT_NAME_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + fnum = create_complex_file(cli, tctx, BASEDIR "\\test.txt.."); + if (fnum == -1) { + printf("failed to open test.txt - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + ret &= test_path(cli, BASEDIR "\\test.txt..", NT_STATUS_NOT_A_DIRECTORY, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + if (!torture_set_file_attribute(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN)) { + printf("failed to set basedir hidden\n"); + ret = false; + goto done; + } + + ret &= test_path_ex(cli, tctx, BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, ((const char *)BASEDIR) + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, ((const char *)BASEDIR"\\\\") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, ((const char *)BASEDIR"\\foo\\..") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, ((const char *)BASEDIR"\\f\\o\\o\\..\\..\\..") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, ((const char *)BASEDIR"\\foo\\\\..\\\\") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, BASEDIR"\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, BASEDIR"\\\\..\\"BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, BASEDIR"\\\\\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, "\\\\\\\\"BASEDIR"\\\\\\\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, "\\\\\\\\"BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path_ex(cli, tctx, BASEDIR "\\foo\\..\\test.txt..", BASEDIR "\\test.txt..", + NT_STATUS_NOT_A_DIRECTORY, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path_ex(cli, tctx, "", "\\", NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path(cli, ".", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\\\\\.\\", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "." BASEDIR, NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\.", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\.\\test.txt..", NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.\\.aaaaa", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\.\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\.\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\.\\\\\\\\\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + /* Note that the two following paths are identical but + give different NT status returns for chkpth and findfirst. */ + + printf("Testing findfirst on %s\n", "\\.\\\\\\\\\\\\."); + status = single_search(cli, tctx, "\\.\\\\\\\\\\\\."); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRinvalidname)); + + ret &= test_path(cli, "\\.\\\\\\\\\\\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + /* We expect this open to fail with the same error code as the chkpath below. */ + printf("Testing Open on %s\n", "\\.\\\\\\\\\\\\."); + /* findfirst seems to fail with a different error. */ + (void)smbcli_nt_create_full(cli->tree, "\\.\\\\\\\\\\\\.", + 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + status = smbcli_nt_error(cli->tree); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + + ret &= test_path(cli, "\\.\\\\xxx", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "..\\..\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath)); + ret &= test_path(cli, "\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath)); + ret &= test_path(cli, "\\.\\\\\\\\\\\\xxx", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"\\.\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"\\.\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"\\.\\nt", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"\\.\\.\\nt", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"\\nt", NT_STATUS_OK, NT_STATUS_OK); + ret &= test_path(cli, BASEDIR".\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR"xx\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, ".\\.\\.\\.\\foo\\.\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR".\\.\\.\\.\\foo\\.\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR".\\.\\.\\.\\foo\\..\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR".", NT_STATUS_OBJECT_NAME_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\", NT_STATUS_OK,NT_STATUS_OK); + ret &= test_path(cli, "\\.", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, "\\..\\", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath)); + ret &= test_path(cli, "\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath)); + ret &= test_path(cli, BASEDIR "\\.", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path_ex(cli, tctx, BASEDIR "\\..", "\\", NT_STATUS_OK,NT_STATUS_OK); + ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb600", NT_STATUS_OBJECT_NAME_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe", NT_STATUS_NOT_A_DIRECTORY,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + /* We expect this open to fail with the same error code as the chkpath below. */ + printf("Testing Open on %s\n", BASEDIR".\\.\\.\\.\\foo\\..\\.\\"); + /* findfirst seems to fail with a different error. */ + (void)smbcli_nt_create_full(cli->tree, BASEDIR".\\.\\.\\.\\foo\\..\\.\\", + 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + status = smbcli_nt_error(cli->tree); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + printf("Testing findfirst on %s\n", BASEDIR".\\.\\.\\.\\foo\\..\\.\\"); + status = single_search(cli, tctx, BASEDIR".\\.\\.\\.\\foo\\..\\.\\"); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + /* We expect this open to fail with the same error code as the chkpath below. */ + /* findfirst seems to fail with a different error. */ + printf("Testing Open on %s\n", BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3"); + (void)smbcli_nt_create_full(cli->tree, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3", + 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + status = smbcli_nt_error(cli->tree); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + + ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\nt\\3\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\nt\\V S\\*\\vb6.exe\\3", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + ret &= test_path(cli, BASEDIR "\\nt\\V S\\*\\*\\vb6.exe\\3", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath)); + +done: + smbcli_close(cli->tree, fnum); + return ret; +} + +static bool test_chkpath_names(struct smbcli_state *cli, struct torture_context *tctx) +{ + union smb_chkpath io; + union smb_fileinfo finfo; + NTSTATUS status; + bool ret = true; + uint8_t i; + + /* + * we don't test characters >= 0x80 yet, + * as somehow our client libraries can't do that + */ + for (i=0x01; i <= 0x7F; i++) { + /* + * it's important that we test the last character + * because of the error code with ':' 0x3A + * and servers without stream support + */ + char *path = talloc_asprintf(tctx, "%s\\File0x%02X%c", + BASEDIR, i, i); + NTSTATUS expected; + NTSTATUS expected_dos1; + NTSTATUS expected_dos2; + + expected = NT_STATUS_OBJECT_NAME_NOT_FOUND; + expected_dos1 = NT_STATUS_DOS(ERRDOS,ERRbadpath); + expected_dos2 = NT_STATUS_DOS(ERRDOS,ERRbadfile); + + switch (i) { + case '"':/*0x22*/ + case '*':/*0x2A*/ + case '/':/*0x2F*/ + case ':':/*0x3A*/ + case '<':/*0x3C*/ + case '>':/*0x3E*/ + case '?':/*0x3F*/ + case '|':/*0x7C*/ + if (i == '/' && + torture_setting_bool(tctx, "samba3", false)) { + /* samba 3 handles '/' as '\\' */ + break; + } + expected = NT_STATUS_OBJECT_NAME_INVALID; + expected_dos1 = NT_STATUS_DOS(ERRDOS,ERRbadpath); + expected_dos2 = NT_STATUS_DOS(ERRDOS,ERRinvalidname); + break; + default: + if (i <= 0x1F) { + expected = NT_STATUS_OBJECT_NAME_INVALID; + expected_dos1 = NT_STATUS_DOS(ERRDOS,ERRbadpath); + expected_dos2 = NT_STATUS_DOS(ERRDOS,ERRinvalidname); + } + break; + } + + printf("Checking File0x%02X%c%s expected[%s|%s|%s]\n", + i, isprint(i)?(char)i:' ', + isprint(i)?"":"(not printable)", + nt_errstr(expected), + nt_errstr(expected_dos1), + nt_errstr(expected_dos2)); + + io.chkpath.in.path = path; + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, expected, expected_dos1); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_NAME_INFO; + finfo.generic.in.file.path = path; + status = smb_raw_pathinfo(cli->tree, cli, &finfo); + CHECK_STATUS(status, expected, expected_dos2); + + talloc_free(path); + } + +done: + return ret; +} + +/* + basic testing of chkpath calls +*/ +bool torture_raw_chkpath(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + int fnum; + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt"))) { + printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt\\V S"))) { + printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree)); + return false; + } + + if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt\\V S\\VB98"))) { + printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree)); + return false; + } + + fnum = create_complex_file(cli, torture, BASEDIR "\\nt\\V S\\VB98\\vb6.exe"); + if (fnum == -1) { + printf("failed to open \\nt\\V S\\VB98\\vb6.exe - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + ret &= test_chkpath(cli, torture); + ret &= test_chkpath_names(cli, torture); + + done: + + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} diff --git a/source4/torture/raw/close.c b/source4/torture/raw/close.c new file mode 100644 index 0000000..56b63c6 --- /dev/null +++ b/source4/torture/raw/close.c @@ -0,0 +1,178 @@ +/* + Unix SMB/CIFS implementation. + RAW_CLOSE_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "system/time.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +/** + * basic testing of all RAW_CLOSE_* calls +*/ +bool torture_raw_close(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + union smb_close io; + union smb_flush io_flush; + int fnum; + const char *fname = "\\torture_close.txt"; + time_t basetime = (time(NULL) + 3*86400) & ~1; + union smb_fileinfo finfo, finfo2; + NTSTATUS status; + +#define REOPEN do { \ + fnum = create_complex_file(cli, torture, fname); \ + if (fnum == -1) { \ + printf("(%d) Failed to create %s\n", __LINE__, fname); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + REOPEN; + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.file.fnum = fnum; + io.close.in.write_time = basetime; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Testing close.in.write_time\n"); + + /* the file should have the write time set */ + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, torture, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (basetime != nt_time_to_unix(finfo.all_info.out.write_time)) { + printf("Incorrect write time on file - %s - %s\n", + timestring(torture, basetime), + nt_time_string(torture, finfo.all_info.out.write_time)); + dump_all_info(torture, &finfo); + ret = false; + } + + printf("Testing other times\n"); + + /* none of the other times should be set to that time */ + if (nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.access_time) || + nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.create_time) || + nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.change_time)) { + printf("Incorrect times after close - only write time should be set\n"); + dump_all_info(torture, &finfo); + + if (!torture_setting_bool(torture, "samba3", false)) { + /* + * In Samba3 as of 3.0.23d we don't yet support all + * file times, so don't mark this as a critical + * failure + */ + ret = false; + } + } + + + smbcli_unlink(cli->tree, fname); + REOPEN; + + finfo2.generic.level = RAW_FILEINFO_ALL_INFO; + finfo2.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, torture, &finfo2); + CHECK_STATUS(status, NT_STATUS_OK); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.file.fnum = fnum; + io.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* the file should have the write time set equal to access time */ + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, torture, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!nt_time_equal(&finfo.all_info.out.write_time, + &finfo2.all_info.out.write_time)) { + printf("Incorrect write time on file - 0 time should be ignored\n"); + dump_all_info(torture, &finfo); + ret = false; + } + + printf("Testing splclose\n"); + + /* check splclose on a file */ + REOPEN; + io.splclose.level = RAW_CLOSE_SPLCLOSE; + io.splclose.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror)); + + printf("Testing flush\n"); + smbcli_close(cli->tree, fnum); + + io_flush.flush.level = RAW_FLUSH_FLUSH; + io_flush.flush.in.file.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + io_flush.flush_all.level = RAW_FLUSH_ALL; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_OK); + + REOPEN; + + io_flush.flush.level = RAW_FLUSH_FLUSH; + io_flush.flush.in.file.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing SMBexit\n"); + smb_raw_exit(cli->session); + + io_flush.flush.level = RAW_FLUSH_FLUSH; + io_flush.flush.in.file.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + +done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + return ret; +} diff --git a/source4/torture/raw/composite.c b/source4/torture/raw/composite.c new file mode 100644 index 0000000..7eb682c --- /dev/null +++ b/source4/torture/raw/composite.c @@ -0,0 +1,417 @@ +/* + Unix SMB/CIFS implementation. + + libcli composite function testing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/events/events.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "libcli/security/security.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "lib/cmdline/cmdline.h" +#include "torture/util.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\composite" + +static void loadfile_complete(struct composite_context *c) +{ + int *count = talloc_get_type(c->async.private_data, int); + (*count)++; +} + +/* + test a simple savefile/loadfile combination +*/ +static bool test_loadfile(struct torture_context *tctx, struct smbcli_state *cli) +{ + const char *fname = BASEDIR "\\test.txt"; + NTSTATUS status; + struct smb_composite_savefile io1; + struct smb_composite_loadfile *io2; + struct composite_context **c; + uint8_t *data; + size_t len = random() % 100000; + const int num_ops = 50; + int i; + int *count = talloc_zero(tctx, int); + + data = talloc_array(tctx, uint8_t, len); + + generate_random_buffer(data, len); + + io1.in.fname = fname; + io1.in.data = data; + io1.in.size = len; + + torture_comment(tctx, "Testing savefile\n"); + + status = smb_composite_savefile(cli->tree, &io1); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "savefile failed"); + + torture_comment(tctx, "Testing parallel loadfile with %d ops\n", num_ops); + + c = talloc_array(tctx, struct composite_context *, num_ops); + io2 = talloc_zero_array(tctx, struct smb_composite_loadfile, num_ops); + + for (i=0;itree, &io2[i]); + c[i]->async.fn = loadfile_complete; + c[i]->async.private_data = count; + } + + torture_comment(tctx, "waiting for completion\n"); + while (*count != num_ops) { + tevent_loop_once(tctx->ev); + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "(%s) count=%d\r", __location__, *count); + fflush(stdout); + } + } + torture_comment(tctx, "count=%d\n", *count); + + for (i=0;isession); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test a simple savefile/loadfile combination +*/ +static bool test_fetchfile(struct torture_context *tctx, struct smbcli_state *cli) +{ + const char *fname = BASEDIR "\\test.txt"; + NTSTATUS status; + struct smb_composite_savefile io1; + struct smb_composite_fetchfile io2; + struct composite_context **c; + uint8_t *data; + int i; + size_t len = random() % 10000; + extern int torture_numops; + struct tevent_context *event_ctx; + int *count = talloc_zero(tctx, int); + bool ret = true; + + data = talloc_array(tctx, uint8_t, len); + + generate_random_buffer(data, len); + + ZERO_STRUCT(io1); + io1.in.fname = fname; + io1.in.data = data; + io1.in.size = len; + + torture_comment(tctx, "Testing savefile\n"); + + status = smb_composite_savefile(cli->tree, &io1); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "savefile failed"); + + ZERO_STRUCT(io2); + + io2.in.dest_host = torture_setting_string(tctx, "host", NULL); + io2.in.ports = lpcfg_smb_ports(tctx->lp_ctx); + io2.in.called_name = torture_setting_string(tctx, "host", NULL); + io2.in.service = torture_setting_string(tctx, "share", NULL); + io2.in.service_type = "A:"; + io2.in.socket_options = lpcfg_socket_options(tctx->lp_ctx); + + io2.in.credentials = samba_cmdline_get_creds(); + io2.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io2.in.filename = fname; + lpcfg_smbcli_options(tctx->lp_ctx, &io2.in.options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &io2.in.session_options); + io2.in.resolve_ctx = lpcfg_resolve_context(tctx->lp_ctx); + io2.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + + torture_comment(tctx, "Testing parallel fetchfile with %d ops\n", torture_numops); + + event_ctx = tctx->ev; + c = talloc_array(tctx, struct composite_context *, torture_numops); + + for (i=0; iasync.fn = loadfile_complete; + c[i]->async.private_data = count; + } + + torture_comment(tctx, "waiting for completion\n"); + + while (*count != torture_numops) { + tevent_loop_once(event_ctx); + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "(%s) count=%d\r", __location__, *count); + fflush(stdout); + } + } + torture_comment(tctx, "count=%d\n", *count); + + for (i=0;isession); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test setfileacl +*/ +static bool test_appendacl(struct torture_context *tctx, struct smbcli_state *cli) +{ + struct smb_composite_appendacl **io; + struct smb_composite_appendacl **io_orig; + struct composite_context **c; + struct tevent_context *event_ctx; + + struct security_descriptor *test_sd; + struct security_ace *ace; + struct dom_sid *test_sid; + + const int num_ops = 50; + int *count = talloc_zero(tctx, int); + struct smb_composite_savefile io1; + + NTSTATUS status; + int i; + + io_orig = talloc_array(tctx, struct smb_composite_appendacl *, num_ops); + + printf ("creating %d empty files and getting their acls with appendacl\n", num_ops); + + for (i = 0; i < num_ops; i++) { + io1.in.fname = talloc_asprintf(io_orig, BASEDIR "\\test%d.txt", i); + io1.in.data = NULL; + io1.in.size = 0; + + status = smb_composite_savefile(cli->tree, &io1); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "savefile failed"); + + io_orig[i] = talloc (io_orig, struct smb_composite_appendacl); + io_orig[i]->in.fname = talloc_steal(io_orig[i], io1.in.fname); + io_orig[i]->in.sd = security_descriptor_initialise(io_orig[i]); + status = smb_composite_appendacl(cli->tree, io_orig[i], io_orig[i]); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "appendacl failed"); + } + + + /* fill Security Descriptor with aces to be added */ + + test_sd = security_descriptor_initialise(tctx); + test_sid = dom_sid_parse_talloc (tctx, "S-1-5-32-1234-5432"); + + ace = talloc_zero(tctx, struct security_ace); + + ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace->flags = 0; + ace->access_mask = SEC_STD_ALL; + ace->trustee = *test_sid; + + status = security_descriptor_dacl_add(test_sd, ace); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "appendacl failed"); + + /* set parameters for appendacl async call */ + + torture_comment(tctx, "Testing parallel appendacl with %d ops\n", num_ops); + + c = talloc_array(tctx, struct composite_context *, num_ops); + io = talloc_array(tctx, struct smb_composite_appendacl *, num_ops); + + for (i=0; i < num_ops; i++) { + io[i] = talloc (io, struct smb_composite_appendacl); + io[i]->in.sd = test_sd; + io[i]->in.fname = talloc_asprintf(io[i], BASEDIR "\\test%d.txt", i); + + c[i] = smb_composite_appendacl_send(cli->tree, io[i]); + c[i]->async.fn = loadfile_complete; + c[i]->async.private_data = count; + } + + event_ctx = tctx->ev; + torture_comment(tctx, "waiting for completion\n"); + while (*count != num_ops) { + tevent_loop_once(event_ctx); + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "(%s) count=%d\r", __location__, *count); + fflush(stdout); + } + } + torture_comment(tctx, "count=%d\n", *count); + + for (i=0; i < num_ops; i++) { + status = smb_composite_appendacl_recv(c[i], io[i]); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) appendacl[%d] failed - %s\n", __location__, i, nt_errstr(status)); + return false; + } + + security_descriptor_dacl_add(io_orig[i]->out.sd, ace); + torture_assert(tctx, + security_acl_equal(io_orig[i]->out.sd->dacl, + io[i]->out.sd->dacl), + "appendacl failed - needed acl isn't set"); + } + + + talloc_free (ace); + talloc_free (test_sid); + talloc_free (test_sd); + + return true; +} + +static bool test_appendacl_t(struct torture_context *tctx, struct smbcli_state *cli) +{ + int ret; + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "failed to setup " BASEDIR); + ret = test_appendacl(tctx, cli); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* test a query FS info by asking for share's GUID */ +static bool test_fsinfo(struct torture_context *tctx, struct smbcli_state *cli) +{ + char *guid = NULL; + NTSTATUS status; + struct smb_composite_fsinfo io1; + struct composite_context **c; + + int i; + extern int torture_numops; + struct tevent_context *event_ctx; + int *count = talloc_zero(tctx, int); + bool ret = true; + + io1.in.dest_host = torture_setting_string(tctx, "host", NULL); + io1.in.dest_ports = lpcfg_smb_ports(tctx->lp_ctx); + io1.in.socket_options = lpcfg_socket_options(tctx->lp_ctx); + io1.in.called_name = torture_setting_string(tctx, "host", NULL); + io1.in.service = torture_setting_string(tctx, "share", NULL); + io1.in.service_type = "A:"; + io1.in.credentials = samba_cmdline_get_creds(); + io1.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io1.in.level = RAW_QFS_OBJECTID_INFORMATION; + io1.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + + torture_comment(tctx, "Testing parallel queryfsinfo [Object ID] with %d ops\n", + torture_numops); + + event_ctx = tctx->ev; + c = talloc_array(tctx, struct composite_context *, torture_numops); + + for (i=0; itree, &io1, lpcfg_resolve_context(tctx->lp_ctx), event_ctx); + torture_assert(tctx, c[i], "smb_composite_fsinfo_send failed!"); + c[i]->async.fn = loadfile_complete; + c[i]->async.private_data = count; + } + + torture_comment(tctx, "waiting for completion\n"); + + while (*count < torture_numops) { + tevent_loop_once(event_ctx); + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "(%s) count=%d\r", __location__, *count); + fflush(stdout); + } + } + torture_comment(tctx, "count=%d\n", *count); + + for (i=0;igeneric.level, RAW_QFS_OBJECTID_INFORMATION, "wrong level in returned info"); + + guid=GUID_string(tctx, &io1.out.fsinfo->objectid_information.out.guid); + torture_comment(tctx, "[%d] GUID: %s\n", i, guid); + } + + return ret; +} + +static bool test_fsinfo_t(struct torture_context *tctx, struct smbcli_state *cli) +{ + int ret; + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "failed to setup " BASEDIR); + ret = test_fsinfo(tctx, cli); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + basic testing of all RAW_SEARCH_* calls using a single file +*/ +struct torture_suite *torture_raw_composite(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "composite"); + + torture_suite_add_1smb_test(suite, "fetchfile", test_fetchfile_t); + torture_suite_add_1smb_test(suite, "loadfile", test_loadfile_t); + torture_suite_add_1smb_test(suite, "appendacl", test_appendacl_t); + torture_suite_add_1smb_test(suite, "fsinfo", test_fsinfo_t); + + return suite; +} diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c new file mode 100644 index 0000000..b984ffe --- /dev/null +++ b/source4/torture/raw/context.c @@ -0,0 +1,893 @@ +/* + Unix SMB/CIFS implementation. + test suite for session setup operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb_composite/smb_composite.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\rawcontext" + +#define CHECK_STATUS(status, correct) \ + torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__) + +#define CHECK_VALUE(v, correct) \ + torture_assert_int_equal_goto(tctx, v, correct, ret, done, __location__) + +#define CHECK_NOT_VALUE(v, correct) \ + torture_assert_goto(tctx, ((v) != (correct)), ret, done, \ + talloc_asprintf(tctx, "(%s) Incorrect value %s=%d - should not be %d\n", \ + __location__, #v, v, correct)); + + +/* + test session ops +*/ +static bool test_session(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + struct smbcli_session *session; + struct smbcli_session *session2; + uint16_t vuid3; + struct smbcli_session *session3; + struct smbcli_session *session4; + struct cli_credentials *anon_creds; + struct smbcli_session *sessions[15]; + struct composite_context *composite_contexts[15]; + struct smbcli_tree *tree; + struct smb_composite_sesssetup setup; + struct smb_composite_sesssetup setups[15]; + struct gensec_settings *gensec_settings; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c = 1; + int i; + struct smbcli_session_options options; + + torture_comment(tctx, "TESTING SESSION HANDLING\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "create a second security context on the same transport\n"); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + + session = smbcli_session_init(cli->transport, tctx, false, options); + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */ + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + + setup.in.credentials = samba_cmdline_get_creds(); + setup.in.gensec_settings = gensec_settings; + + status = smb_composite_sesssetup(session, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + + session->vuid = setup.out.vuid; + + torture_comment(tctx, "create a third security context on the same transport, with given vuid\n"); + session2 = smbcli_session_init(cli->transport, tctx, false, options); + + if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) { + vuid3 = session->vuid+1; + if (vuid3 == cli->session->vuid) { + vuid3 += 1; + } + if (vuid3 == UINT16_MAX) { + vuid3 += 2; + } + } else { + vuid3 = session->vuid; + } + session2->vuid = vuid3; + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */ + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + + setup.in.credentials = samba_cmdline_get_creds(); + + torture_comment(tctx, "vuid1=%d vuid2=%d vuid3=%d\n", cli->session->vuid, session->vuid, vuid3); + + status = smb_composite_sesssetup(session2, &setup); + if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) { + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRbaduid)); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + session2->vuid = setup.out.vuid; + CHECK_NOT_VALUE(session2->vuid, vuid3); + } + + torture_comment(tctx, "vuid1=%d vuid2=%d vuid3=%d=>%d (%s)\n", + cli->session->vuid, session->vuid, + vuid3, session2->vuid, nt_errstr(status)); + + talloc_free(session2); + + if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) { + torture_comment(tctx, "create a fourth security context on the same transport, without extended security\n"); + session3 = smbcli_session_init(cli->transport, tctx, false, options); + + session3->vuid = vuid3; + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */ + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + + setup.in.credentials = samba_cmdline_get_creds(); + + status = smb_composite_sesssetup(session3, &setup); + if (!NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)) { + /* + * Windows 2008 R2 returns INVALID_PARAMETER + * while Windows 2000 sp4 returns LOGON_FAILURE... + */ + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } + + torture_comment(tctx, "create a fourth anonymous security context on the same transport, without extended security\n"); + session4 = smbcli_session_init(cli->transport, tctx, false, options); + + session4->vuid = vuid3; + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */ + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + + anon_creds = cli_credentials_init(tctx); + cli_credentials_set_conf(anon_creds, tctx->lp_ctx); + cli_credentials_set_anonymous(anon_creds); + + setup.in.credentials = anon_creds; + + status = smb_composite_sesssetup(session3, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + + talloc_free(session4); + } + + torture_comment(tctx, "use the same tree as the existing connection\n"); + tree = smbcli_tree_init(session, tctx, false); + tree->tid = cli->tree->tid; + + torture_comment(tctx, "create a file using the new vuid\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using the old vuid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "write with the new vuid\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "logoff the new vuid\n"); + status = smb_raw_ulogoff(session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the new vuid should not now be accessible\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "second logoff for the new vuid should fail\n"); + status = smb_raw_ulogoff(session); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRbaduid)); + talloc_free(tree); + talloc_free(session); + + torture_comment(tctx, "the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "create %d secondary security contexts on the same transport\n", + (int)ARRAY_SIZE(sessions)); + for (i=0; i transport->negotiate.sesskey; + setups[i].in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */ + setups[i].in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + + setups[i].in.credentials = samba_cmdline_get_creds(); + setups[i].in.gensec_settings = gensec_settings; + + sessions[i] = smbcli_session_init(cli->transport, tctx, false, options); + composite_contexts[i] = smb_composite_sesssetup_send(sessions[i], &setups[i]); + + } + + + torture_comment(tctx, "finishing %d secondary security contexts on the same transport\n", + (int)ARRAY_SIZE(sessions)); + for (i=0; i< ARRAY_SIZE(sessions); i++) { + status = smb_composite_sesssetup_recv(composite_contexts[i]); + CHECK_STATUS(status, NT_STATUS_OK); + sessions[i]->vuid = setups[i].out.vuid; + torture_comment(tctx, "VUID: %d\n", sessions[i]->vuid); + status = smb_raw_ulogoff(sessions[i]); + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + return ret; +} + + +/* + test tree ops +*/ +static bool test_tree(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + const char *share, *host; + struct smbcli_tree *tree; + union smb_tcon tcon; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c = 1; + + torture_comment(tctx, "TESTING TREE HANDLING\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + share = torture_setting_string(tctx, "share", NULL); + host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "create a second tree context on the same session\n"); + tree = smbcli_tree_init(cli->session, tctx, false); + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + tcon.tconx.in.device = "A:"; + status = smb_raw_tcon(tree, tctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + + + tree->tid = tcon.tconx.out.tid; + torture_comment(tctx, "tid1=%d tid2=%d\n", cli->tree->tid, tree->tid); + + torture_comment(tctx, "try a tconx with a bad device type\n"); + tcon.tconx.in.device = "FOO"; + status = smb_raw_tcon(tree, tctx, &tcon); + CHECK_STATUS(status, NT_STATUS_BAD_DEVICE_TYPE); + + + torture_comment(tctx, "create a file using the new tid\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using the old tid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "write with the new tid\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "disconnect the new tid\n"); + status = smb_tree_disconnect(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the new tid should not now be accessible\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + /* close down the new tree */ + talloc_free(tree); + +done: + return ret; +} + +/* + test tree with ulogoff + this demonstrates that a tcon isn't autoclosed by a ulogoff + the tcon can be reused using any other valid session later +*/ +static bool test_tree_ulogoff(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + const char *share, *host; + struct smbcli_session *session1; + struct smbcli_session *session2; + struct smb_composite_sesssetup setup; + struct smbcli_tree *tree; + union smb_tcon tcon; + union smb_open io; + union smb_write wr; + int fnum1, fnum2; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + uint8_t c = 1; + struct smbcli_session_options options; + + torture_comment(tctx, "TESTING TREE with ulogoff\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + share = torture_setting_string(tctx, "share", NULL); + host = torture_setting_string(tctx, "host", NULL); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + torture_comment(tctx, "create the first new sessions\n"); + session1 = smbcli_session_init(cli->transport, tctx, false, options); + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + setup.in.credentials = samba_cmdline_get_creds(); + setup.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(session1, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + session1->vuid = setup.out.vuid; + torture_comment(tctx, "vuid1=%d\n", session1->vuid); + + torture_comment(tctx, "create a tree context on the with vuid1\n"); + tree = smbcli_tree_init(session1, tctx, false); + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + tcon.tconx.in.device = "A:"; + status = smb_raw_tcon(tree, tctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + tree->tid = tcon.tconx.out.tid; + torture_comment(tctx, "tid=%d\n", tree->tid); + + torture_comment(tctx, "create a file using vuid1\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + status = smb_raw_open(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using vuid1\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum1; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "ulogoff the vuid1\n"); + status = smb_raw_ulogoff(session1); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "create the second new sessions\n"); + session2 = smbcli_session_init(cli->transport, tctx, false, options); + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + setup.in.credentials = samba_cmdline_get_creds(); + setup.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(session2, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + session2->vuid = setup.out.vuid; + torture_comment(tctx, "vuid2=%d\n", session2->vuid); + + torture_comment(tctx, "use the existing tree with vuid2\n"); + tree->session = session2; + + torture_comment(tctx, "create a file using vuid2\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname2; + status = smb_raw_open(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using vuid2\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum2; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "ulogoff the vuid2\n"); + status = smb_raw_ulogoff(session2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* this also demonstrates that SMBtdis doesn't need a valid vuid */ + torture_comment(tctx, "disconnect the existing tree connection\n"); + status = smb_tree_disconnect(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "disconnect the existing tree connection\n"); + status = smb_tree_disconnect(tree); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV,ERRinvnid)); + + /* close down the new tree */ + talloc_free(tree); + +done: + return ret; +} + +/* + test pid ops + this test demonstrates that exit() only sees the PID + used for the open() calls +*/ +static bool test_pid_exit_only_sees_open(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = tctx; + bool ret = true; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c = 1; + uint16_t pid1, pid2; + + torture_comment(tctx, "TESTING PID HANDLING exit() only cares about open() PID\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + pid1 = cli->session->pid; + pid2 = pid1 + 1; + + torture_comment(tctx, "pid1=%d pid2=%d\n", pid1, pid2); + + torture_comment(tctx, "create a file using pid1\n"); + cli->session->pid = pid1; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using pid2\n"); + cli->session->pid = pid2; + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit pid2\n"); + cli->session->pid = pid2; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum should still be accessible via pid2\n"); + cli->session->pid = pid2; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit pid2\n"); + cli->session->pid = pid2; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum should still be accessible via pid1 and pid2\n"); + cli->session->pid = pid1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + cli->session->pid = pid2; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit pid1\n"); + cli->session->pid = pid1; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum should not now be accessible via pid1 or pid2\n"); + cli->session->pid = pid1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + cli->session->pid = pid2; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum should have been auto-closed\n"); + cli->session->pid = pid1; + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +done: + return ret; +} + +/* + test pid ops with 2 sessions +*/ +static bool test_pid_2sess(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + struct smbcli_session *session; + struct smb_composite_sesssetup setup; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c = 1; + uint16_t vuid1, vuid2; + struct smbcli_session_options options; + + torture_comment(tctx, "TESTING PID HANDLING WITH 2 SESSIONS\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + torture_comment(tctx, "create a second security context on the same transport\n"); + session = smbcli_session_init(cli->transport, tctx, false, options); + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */ + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + setup.in.credentials = samba_cmdline_get_creds(); + setup.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + + status = smb_composite_sesssetup(session, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + session->vuid = setup.out.vuid; + + vuid1 = cli->session->vuid; + vuid2 = session->vuid; + + torture_comment(tctx, "vuid1=%d vuid2=%d\n", vuid1, vuid2); + + torture_comment(tctx, "create a file using the vuid1\n"); + cli->session->vuid = vuid1; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using the vuid1 (fnum=%d)\n", fnum); + cli->session->vuid = vuid1; + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit the pid with vuid2\n"); + cli->session->vuid = vuid2; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum should still be accessible\n"); + cli->session->vuid = vuid1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit the pid with vuid1\n"); + cli->session->vuid = vuid1; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum should not now be accessible\n"); + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +done: + return ret; +} + +/* + test pid ops with 2 tcons +*/ +static bool test_pid_2tcon(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + const char *share, *host; + struct smbcli_tree *tree; + union smb_tcon tcon; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum1, fnum2; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + uint8_t c = 1; + uint16_t tid1, tid2; + + torture_comment(tctx, "TESTING PID HANDLING WITH 2 TCONS\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + share = torture_setting_string(tctx, "share", NULL); + host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "create a second tree context on the same session\n"); + tree = smbcli_tree_init(cli->session, tctx, false); + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + tcon.tconx.in.device = "A:"; + status = smb_raw_tcon(tree, tctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + + tree->tid = tcon.tconx.out.tid; + + tid1 = cli->tree->tid; + tid2 = tree->tid; + torture_comment(tctx, "tid1=%d tid2=%d\n", tid1, tid2); + + torture_comment(tctx, "create a file using the tid1\n"); + cli->tree->tid = tid1; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using the tid1\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum1; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "create a file using the tid2\n"); + cli->tree->tid = tid2; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "write using the tid2\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum2; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + torture_comment(tctx, "exit the pid\n"); + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "the fnum1 on tid1 should not be accessible\n"); + cli->tree->tid = tid1; + wr.writex.in.file.fnum = fnum1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum1 on tid1 should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum1; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum2 on tid2 should not be accessible\n"); + cli->tree->tid = tid2; + wr.writex.in.file.fnum = fnum2; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "the fnum2 on tid2 should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum2; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +done: + return ret; +} + +struct torture_suite *torture_raw_context(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "context"); + + torture_suite_add_1smb_test(suite, "session1", test_session); + /* + * TODO: add test_session with 'use spnego = false' + * torture_suite_add_1smb_test(suite, "session1", test_session); + */ + torture_suite_add_1smb_test(suite, "tree", test_tree); + torture_suite_add_1smb_test(suite, "tree_ulogoff", test_tree_ulogoff); + torture_suite_add_1smb_test(suite, "pid_only_sess", test_pid_exit_only_sees_open); + torture_suite_add_1smb_test(suite, "pid_2sess", test_pid_2sess); + torture_suite_add_1smb_test(suite, "pid_2tcon", test_pid_2tcon); + + return suite; +} diff --git a/source4/torture/raw/eas.c b/source4/torture/raw/eas.c new file mode 100644 index 0000000..59baae5 --- /dev/null +++ b/source4/torture/raw/eas.c @@ -0,0 +1,594 @@ +/* + Unix SMB/CIFS implementation. + + test DOS extended attributes + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Guenter Kukkukk 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\testeas" + +#define CHECK_STATUS(status, correct) do { \ + torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, "Incorrect status"); \ + } while (0) + +static bool maxeadebug; /* need that here, to allow no file delete in debug case */ + +static bool check_ea(struct smbcli_state *cli, + const char *fname, const char *eaname, const char *value) +{ + NTSTATUS status = torture_check_ea(cli, fname, eaname, value); + return NT_STATUS_IS_OK(status); +} + +static char bad_ea_chars[] = "\"*+,/:;<=>?[\\]|"; + +static bool test_eas(struct smbcli_state *cli, struct torture_context *tctx) +{ + NTSTATUS status; + union smb_setfileinfo setfile; + union smb_open io; + const char *fname = BASEDIR "\\ea.txt"; + bool ret = true; + char bad_ea_name[7]; + int i; + int fnum = -1; + + torture_comment(tctx, "TESTING SETFILEINFO EA_SET\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ret &= check_ea(cli, fname, "EAONE", NULL); + + torture_comment(tctx, "Adding first two EAs\n"); + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE1"); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo"); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_ea(cli, fname, "EAONE", "VALUE1"); + ret &= check_ea(cli, fname, "SECONDEA", "ValueTwo"); + + torture_comment(tctx, "Modifying 2nd EA\n"); + setfile.ea_set.in.num_eas = 1; + setfile.ea_set.in.eas[0].name.s = "SECONDEA"; + setfile.ea_set.in.eas[0].value = data_blob_string_const(" Changed Value"); + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_ea(cli, fname, "EAONE", "VALUE1"); + ret &= check_ea(cli, fname, "SECONDEA", " Changed Value"); + + torture_comment(tctx, "Setting a NULL EA\n"); + setfile.ea_set.in.eas[0].value = data_blob(NULL, 0); + setfile.ea_set.in.eas[0].name.s = "NULLEA"; + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_ea(cli, fname, "EAONE", "VALUE1"); + ret &= check_ea(cli, fname, "SECONDEA", " Changed Value"); + ret &= check_ea(cli, fname, "NULLEA", NULL); + + torture_comment(tctx, "Deleting first EA\n"); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob(NULL, 0); + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_ea(cli, fname, "EAONE", NULL); + ret &= check_ea(cli, fname, "SECONDEA", " Changed Value"); + + torture_comment(tctx, "Deleting second EA\n"); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "SECONDEA"; + setfile.ea_set.in.eas[0].value = data_blob(NULL, 0); + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_ea(cli, fname, "EAONE", NULL); + ret &= check_ea(cli, fname, "SECONDEA", NULL); + + /* Check EA name containing colon. All EA's set + must be ignored, not just the one with the bad + name. */ + + torture_comment(tctx, "Adding bad EA name\n"); + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 3; + setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 3); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE1"); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECOND:EA"; + setfile.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo"); + setfile.ea_set.in.eas[2].flags = 0; + setfile.ea_set.in.eas[2].name.s = "THIRDEA"; + setfile.ea_set.in.eas[2].value = data_blob_string_const("ValueThree"); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, STATUS_INVALID_EA_NAME); + + ret &= check_ea(cli, fname, "EAONE", NULL); + ret &= check_ea(cli, fname, "THIRDEA", NULL); + + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 1; + setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 1); + setfile.ea_set.in.eas[0].flags = 0; + strlcpy(bad_ea_name, "TEST_X", sizeof(bad_ea_name)); + setfile.ea_set.in.eas[0].name.s = bad_ea_name; + + torture_comment(tctx, "Testing bad EA name range.\n"); + + for (i = 1; i < 256; i++) { + setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE1"); + bad_ea_name[5] = (char)i; + torture_comment(tctx, "Testing bad EA name %d.\n", i); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (i < 32 || strchr(bad_ea_chars, i)) { + CHECK_STATUS(status, STATUS_INVALID_EA_NAME); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now delete the EA we just set to make + sure we don't run out of room. */ + setfile.ea_set.in.eas[0].value = data_blob(NULL, 0); + status = smb_raw_setfileinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + } + } + +done: + smbcli_close(cli->tree, fnum); + return ret; +} + + +/* + * Helper function to retrieve the max. ea size for one ea name + */ +static int test_one_eamax(struct torture_context *tctx, + struct smbcli_state *cli, const int fnum, + const char *eaname, DATA_BLOB eablob, + const int eastart, const int eadebug) +{ + NTSTATUS status; + struct ea_struct eastruct; + union smb_setfileinfo setfile; + int i, high, low, maxeasize; + + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 1; + setfile.ea_set.in.eas = &eastruct; + setfile.ea_set.in.eas->flags = 0; + setfile.ea_set.in.eas->name.s = eaname; + setfile.ea_set.in.eas->value = eablob; + + maxeasize = eablob.length; + i = eastart; + low = 0; + high = maxeasize; + + do { + if (eadebug) { + torture_comment(tctx, "Testing EA size: %d\n", i); + } + setfile.ea_set.in.eas->value.length = i; + + status = smb_raw_setfileinfo(cli->tree, &setfile); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + if (eadebug) { + torture_comment(tctx, "[%s] EA size %d succeeded! " + "(high=%d low=%d)\n", + eaname, i, high, low); + } + low = i; + if (low == maxeasize) { + torture_comment(tctx, "Max. EA size for \"%s\"=%d " + "[but could be possibly larger]\n", + eaname, low); + break; + } + if (high - low == 1 && high != maxeasize) { + torture_comment(tctx, "Max. EA size for \"%s\"=%d\n", + eaname, low); + break; + } + i += (high - low + 1) / 2; + } else { + if (eadebug) { + torture_comment(tctx, "[%s] EA size %d failed! " + "(high=%d low=%d) [%s]\n", + eaname, i, high, low, + nt_errstr(status)); + } + high = i; + if (high - low <= 1) { + torture_comment(tctx, "Max. EA size for \"%s\"=%d\n", + eaname, low); + break; + } + i -= (high - low + 1) / 2; + } + } while (true); + + return low; +} + +/* + * Test for maximum ea size - more than one ea name is checked. + * + * Additional parameters can be passed, to allow further testing: + * + * default + * maxeasize 65536 limit the max. size for a single EA name + * maxeanames 101 limit of the number of tested names + * maxeastart 1 this EA size is used to test for the 1st EA (atm) + * maxeadebug 0 if set true, further debug output is done - in addition + * the testfile is not deleted for further inspection! + * + * Set some/all of these options on the cmdline with: + * --option torture:maxeasize=1024 --option torture:maxeadebug=1 ... + * + */ +static bool test_max_eas(struct smbcli_state *cli, struct torture_context *tctx) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\ea_max.txt"; + int fnum = -1; + bool ret = true; + bool err = false; + + int i, j, k, last; + size_t total; + DATA_BLOB eablob; + char *eaname = NULL; + int maxeasize; + int maxeanames; + int maxeastart; + + torture_comment(tctx, "TESTING SETFILEINFO MAX. EA_SET\n"); + + maxeasize = torture_setting_int(tctx, "maxeasize", 65536); + maxeanames = torture_setting_int(tctx, "maxeanames", 101); + maxeastart = torture_setting_int(tctx, "maxeastart", 1); + maxeadebug = torture_setting_bool(tctx, "maxeadebug", false); + + /* Do some sanity check on possibly passed parms */ + if (maxeasize <= 0) { + torture_comment(tctx, "Invalid parameter 'maxeasize=%d'",maxeasize); + err = true; + } + if (maxeanames <= 0) { + torture_comment(tctx, "Invalid parameter 'maxeanames=%d'",maxeanames); + err = true; + } + if (maxeastart <= 0) { + torture_comment(tctx, "Invalid parameter 'maxeastart=%d'",maxeastart); + err = true; + } + if (err) { + torture_comment(tctx, "\n\n"); + goto done; + } + if (maxeastart > maxeasize) { + maxeastart = maxeasize; + torture_comment(tctx, "'maxeastart' outside range - corrected to %d\n", + maxeastart); + } + torture_comment(tctx, "MAXEA parms: maxeasize=%d maxeanames=%d maxeastart=%d" + " maxeadebug=%d\n", maxeasize, maxeanames, maxeastart, + maxeadebug); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + eablob = data_blob_talloc(tctx, NULL, maxeasize); + if (eablob.data == NULL) { + goto done; + } + /* + * Fill in some EA data - the offset could be easily checked + * during a hexdump. + */ + for (i = 0, k = 0; i < eablob.length / 4; i++, k+=4) { + eablob.data[k] = k & 0xff; + eablob.data[k+1] = (k >> 8) & 0xff; + eablob.data[k+2] = (k >> 16) & 0xff; + eablob.data[k+3] = (k >> 24) & 0xff; + } + + i = eablob.length % 4; + if (i-- > 0) { + eablob.data[k] = k & 0xff; + if (i-- > 0) { + eablob.data[k+1] = (k >> 8) & 0xff; + if (i-- > 0) { + eablob.data[k+2] = (k >> 16) & 0xff; + } + } + } + /* + * Filesystems might allow max. EAs data for different EA names. + * So more than one EA name should be checked. + */ + total = 0; + last = maxeastart; + + for (i = 0; i < maxeanames; i++) { + if (eaname != NULL) { + talloc_free(eaname); + } + eaname = talloc_asprintf(tctx, "MAX%d", i); + if(eaname == NULL) { + goto done; + } + j = test_one_eamax(tctx, cli, fnum, eaname, eablob, last, maxeadebug); + if (j <= 0) { + break; + } + total += j; + last = j; + } + + torture_comment(tctx, "Total EA size:%zu\n", total); + if (i == maxeanames) { + torture_comment(tctx, "NOTE: More EAs could be available!\n"); + } + if (total == 0) { + ret = false; + } +done: + smbcli_close(cli->tree, fnum); + return ret; +} + +/* + test using NTTRANS CREATE to create a file with an initial EA set +*/ +static bool test_nttrans_create(struct smbcli_state *cli, struct torture_context *tctx) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\ea2.txt"; + const char *fname_bad = BASEDIR "\\ea2_bad.txt"; + bool ret = true; + int fnum = -1; + struct ea_struct eas[3]; + struct smb_ea_list ea_list; + + torture_comment(tctx, "TESTING NTTRANS CREATE WITH EAS\n"); + + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + ea_list.num_eas = 3; + ea_list.eas = eas; + + eas[0].flags = 0; + eas[0].name.s = "1st EA"; + eas[0].value = data_blob_string_const("Value One"); + + eas[1].flags = 0; + eas[1].name.s = "2nd EA"; + eas[1].value = data_blob_string_const("Second Value"); + + eas[2].flags = 0; + eas[2].name.s = "and 3rd"; + eas[2].value = data_blob_string_const("final value"); + + io.ntcreatex.in.ea_list = &ea_list; + io.ntcreatex.in.sec_desc = NULL; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ret &= check_ea(cli, fname, "EAONE", NULL); + ret &= check_ea(cli, fname, "1st EA", "Value One"); + ret &= check_ea(cli, fname, "2nd EA", "Second Value"); + ret &= check_ea(cli, fname, "and 3rd", "final value"); + + smbcli_close(cli->tree, fnum); + + torture_comment(tctx, "Trying to add EAs on non-create\n"); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = fname; + + ea_list.num_eas = 1; + eas[0].flags = 0; + eas[0].name.s = "Fourth EA"; + eas[0].value = data_blob_string_const("Value Four"); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ret &= check_ea(cli, fname, "1st EA", "Value One"); + ret &= check_ea(cli, fname, "2nd EA", "Second Value"); + ret &= check_ea(cli, fname, "and 3rd", "final value"); + ret &= check_ea(cli, fname, "Fourth EA", NULL); + + torture_comment(tctx, "TESTING NTTRANS CREATE WITH BAD EA NAMES\n"); + + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_bad; + + ea_list.num_eas = 3; + ea_list.eas = eas; + + eas[0].flags = 0; + eas[0].name.s = "1st EA"; + eas[0].value = data_blob_string_const("Value One"); + + eas[1].flags = 0; + eas[1].name.s = "2nd:BAD:EA"; + eas[1].value = data_blob_string_const("Second Value"); + + eas[2].flags = 0; + eas[2].name.s = "and 3rd"; + eas[2].value = data_blob_string_const("final value"); + + io.ntcreatex.in.ea_list = &ea_list; + io.ntcreatex.in.sec_desc = NULL; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, STATUS_INVALID_EA_NAME); + + /* File must not exist. */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_bad; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + smbcli_close(cli->tree, fnum); + return ret; +} + +/* + basic testing of EA calls +*/ +bool torture_raw_eas(struct torture_context *torture, struct smbcli_state *cli) +{ + bool ret = true; + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + ret &= test_eas(cli, torture); + ret &= test_nttrans_create(cli, torture); + + smb_raw_exit(cli->session); + + return ret; +} + +/* + test max EA size +*/ +bool torture_max_eas(struct torture_context *torture) +{ + struct smbcli_state *cli; + bool ret = true; + + if (!torture_open_connection(&cli, torture, 0)) { + return false; + } + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + ret &= test_max_eas(cli, torture); + + smb_raw_exit(cli->session); + if (!maxeadebug) { + /* in no ea debug case, all files are gone now */ + smbcli_deltree(cli->tree, BASEDIR); + } + + torture_close_connection(cli); + return ret; +} diff --git a/source4/torture/raw/ioctl.c b/source4/torture/raw/ioctl.c new file mode 100644 index 0000000..0dc0ac9 --- /dev/null +++ b/source4/torture/raw/ioctl.c @@ -0,0 +1,191 @@ +/* + Unix SMB/CIFS implementation. + ioctl individual test suite + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "../libcli/smb/smb_constants.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\rawioctl" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + +/* test some ioctls */ +static bool test_ioctl(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_ioctl ctl; + int fnum; + NTSTATUS status; + bool ret = true; + const char *fname = BASEDIR "\\test.dat"; + + printf("TESTING IOCTL FUNCTIONS\n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to create test.dat - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying 0xFFFF\n"); + ctl.ioctl.level = RAW_IOCTL_IOCTL; + ctl.ioctl.in.file.fnum = fnum; + ctl.ioctl.in.request = 0xFFFF; + + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror)); + + printf("Trying QUERY_JOB_INFO\n"); + ctl.ioctl.level = RAW_IOCTL_IOCTL; + ctl.ioctl.in.file.fnum = fnum; + ctl.ioctl.in.request = IOCTL_QUERY_JOB_INFO; + + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror)); + + printf("Trying bad handle\n"); + ctl.ioctl.in.file.fnum = fnum+1; + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror)); + +done: + smbcli_close(cli->tree, fnum); + return ret; +} + +/* test some filesystem control functions */ +static bool test_fsctl(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + int fnum; + NTSTATUS status; + bool ret = true; + const char *fname = BASEDIR "\\test.dat"; + union smb_ioctl nt; + + printf("\nTESTING FSCTL FUNCTIONS\n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to create test.dat - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying FSCTL_FIND_FILES_BY_SID\n"); + nt.ioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_FIND_FILES_BY_SID; + nt.ntioctl.in.file.fnum = fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = 0; + nt.ntioctl.in.blob = data_blob(NULL, 1024); + /* definitely not a sid... */ + generate_random_buffer(nt.ntioctl.in.blob.data, + nt.ntioctl.in.blob.length); + nt.ntioctl.in.blob.data[1] = 15+1; + status = smb_raw_ioctl(cli->tree, mem_ctx, &nt); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("Got unexpected error code: %s\n", + nt_errstr(status)); + ret = false; + goto done; + } + + printf("trying sparse file\n"); + nt.ioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_SET_SPARSE; + nt.ntioctl.in.file.fnum = fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = 0; + nt.ntioctl.in.blob = data_blob(NULL, 0); + + status = smb_raw_ioctl(cli->tree, mem_ctx, &nt); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("trying batch oplock\n"); + nt.ioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_REQUEST_BATCH_OPLOCK; + nt.ntioctl.in.file.fnum = fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = 0; + nt.ntioctl.in.blob = data_blob(NULL, 0); + + status = smb_raw_ioctl(cli->tree, mem_ctx, &nt); + if (NT_STATUS_IS_OK(status)) { + printf("Server supports batch oplock upgrades on open files\n"); + } else { + printf("Server does not support batch oplock upgrades on open files\n"); + } + + printf("Trying bad handle\n"); + nt.ntioctl.in.file.fnum = fnum+1; + status = smb_raw_ioctl(cli->tree, mem_ctx, &nt); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +#if 0 + nt.ntioctl.in.file.fnum = fnum; + for (i=0;i<100;i++) { + nt.ntioctl.in.function = FSCTL_FILESYSTEM + (i<<2); + status = smb_raw_ioctl(cli->tree, mem_ctx, &nt); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("filesystem fsctl 0x%x - %s\n", + i, nt_errstr(status)); + } + } +#endif + +done: + smbcli_close(cli->tree, fnum); + return ret; +} + +/* + basic testing of some ioctl calls +*/ +bool torture_raw_ioctl(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + ret &= test_ioctl(cli, torture); + ret &= test_fsctl(cli, torture); + + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c new file mode 100644 index 0000000..6dbc9e9 --- /dev/null +++ b/source4/torture/raw/lock.c @@ -0,0 +1,3586 @@ +/* + Unix SMB/CIFS implementation. + test suite for various lock operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" +#include "torture/raw/proto.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS_CONT(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS_OR(status, correct1, correct2) do { \ + if ((!NT_STATUS_EQUAL(status, correct1)) && \ + (!NT_STATUS_EQUAL(status, correct2))) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s or %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct1), \ + nt_errstr(correct2)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS_OR_CONT(status, correct1, correct2) do { \ + if ((!NT_STATUS_EQUAL(status, correct1)) && \ + (!NT_STATUS_EQUAL(status, correct2))) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s or %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct1), \ + nt_errstr(correct2)); \ + ret = false; \ + }} while (0) +#define BASEDIR "\\testlock" + +#define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false)) +#define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false)) +#define TARGET_IS_WINDOWS(_tctx) \ + ((torture_setting_bool(_tctx, "w2k3", false)) || \ + (torture_setting_bool(_tctx, "w2k8", false)) || \ + (torture_setting_bool(_tctx, "win7", false))) +#define TARGET_IS_SAMBA3(_tctx) (torture_setting_bool(_tctx, "samba3", false)) +#define TARGET_IS_SAMBA4(_tctx) (torture_setting_bool(_tctx, "samba4", false)) + +#define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \ + (torture_setting_bool(_tctx, "invalid_lock_range_support", true)) +#define TARGET_SUPPORTS_SMBEXIT(_tctx) \ + (torture_setting_bool(_tctx, "smbexit_pdu_support", true)) +#define TARGET_SUPPORTS_SMBLOCK(_tctx) \ + (torture_setting_bool(_tctx, "smblock_pdu_support", true)) +#define TARGET_SUPPORTS_OPENX_DENY_DOS(_tctx) \ + (torture_setting_bool(_tctx, "openx_deny_dos_support", true)) +#define TARGET_RETURNS_RANGE_NOT_LOCKED(_tctx) \ + (torture_setting_bool(_tctx, "range_not_locked_on_file_close", true)) +/* + test SMBlock and SMBunlock ops +*/ +static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (!TARGET_SUPPORTS_SMBLOCK(tctx)) + torture_skip(tctx, "Target does not support the SMBlock PDU"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing RAW_LOCK_LOCK\n"); + io.generic.level = RAW_LOCK_LOCK; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + torture_comment(tctx, "Trying 0/0 lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 0; + io.lock.in.offset = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Trying 0/1 lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 1; + io.lock.in.offset = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying 0xEEFFFFFF lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 4000; + io.lock.in.offset = 0xEEFFFFFF; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying 0xEEFFFFFF lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 4000; + io.lock.in.offset = 0xEEFFFFFF; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying max lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 4000; + io.lock.in.offset = 0xEF000000; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying wrong pid unlock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.file.fnum = fnum; + io.lock.in.count = 4002; + io.lock.in.offset = 10001; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + cli->session->pid--; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test locking&X ops +*/ +static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing RAW_LOCK_LOCKX\n"); + io.generic.level = RAW_LOCK_LOCKX; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 10; + lock[0].count = 1; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(tctx, "Trying 0xEEFFFFFF lock\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 4000; + lock[0].offset = 0xEEFFFFFF; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying 0xEF000000 lock\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 4000; + lock[0].offset = 0xEF000000; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying zero lock\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 0; + lock[0].offset = ~0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying max lock\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 0; + lock[0].offset = ~0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying 2^63\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 1; + lock[0].offset = 1; + lock[0].offset <<= 63; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying 2^63 - 1\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 1; + lock[0].offset = 1; + lock[0].offset <<= 63; + lock[0].offset--; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(tctx, "Trying max lock 2\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].count = 1; + lock[0].offset = ~0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid++; + lock[0].count = 2; + status = smb_raw_lock(cli->tree, &io); + if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(tctx)) + CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE); + else + CHECK_STATUS(status, NT_STATUS_OK); + lock[0].pid--; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + lock[0].count = 1; + status = smb_raw_lock(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test high pid +*/ +static bool test_pidhigh(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c = 1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing high pid\n"); + io.generic.level = RAW_LOCK_LOCKX; + + cli->session->pid = 1; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + if (smbcli_write(cli->tree, fnum, 0, &c, 0, 1) != 1) { + torture_result(tctx, TORTURE_FAIL, + "Failed to write 1 byte - %s\n", + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 0; + lock[0].count = 0xFFFFFFFF; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) { + torture_result(tctx, TORTURE_FAIL, + "Failed to read 1 byte - %s\n", + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + cli->session->pid = 2; + + if (smbcli_read(cli->tree, fnum, &c, 0, 1) == 1) { + torture_result(tctx, TORTURE_FAIL, + "pid is incorrect handled for read with lock!\n"); + ret = false; + goto done; + } + + cli->session->pid = 0x10001; + + if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) { + torture_result(tctx, TORTURE_FAIL, + "High pid is used on this server!\n"); + ret = false; + } else { + torture_warning(tctx, "High pid is not used on this server (correct)\n"); + } + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test locking&X async operation +*/ +static bool test_async(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct smbcli_session *session; + struct smb_composite_sesssetup setup; + struct smbcli_tree *tree; + union smb_tcon tcon; + const char *host, *share; + union smb_lock io; + struct smb_lock_entry lock[2]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + time_t t; + struct smbcli_request *req, *req2; + struct smbcli_session_options options; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + torture_comment(tctx, "Testing LOCKING_ANDX_CANCEL_LOCK\n"); + io.generic.level = RAW_LOCK_LOCKX; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 110; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + t = time_mono(NULL); + + torture_comment(tctx, "Testing cancel by CANCEL_LOCK\n"); + + /* setup a timed lock */ + io.lockx.in.timeout = 10000; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + /* cancel the wrong range */ + lock[0].offset = 0; + io.lockx.in.timeout = 0; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation)); + + /* cancel with the wrong bits set */ + lock[0].offset = 100; + io.lockx.in.timeout = 0; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation)); + + /* cancel the right range */ + lock[0].offset = 100; + io.lockx.in.timeout = 0; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* receive the failed lock request */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel was not immediate (%s)\n", __location__)); + + /* MS-CIFS (2.2.4.32.1) states that a cancel is honored if and only + * if the lock vector contains one entry. When given multiple cancel + * requests in a single PDU we expect the server to return an + * error. Samba4 handles this correctly. Windows servers seem to + * accept the request but only cancel the first lock. Samba3 + * now does what Windows does (JRA). + */ + torture_comment(tctx, "Testing multiple cancel\n"); + + /* acquire second lock */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* setup 2 timed locks */ + t = time_mono(NULL); + io.lockx.in.timeout = 10000; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock[0]; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + io.lockx.in.locks = &lock[1]; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + /* try to cancel both locks in the same packet */ + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = lock; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_warning(tctx, "Target server accepted a lock cancel " + "request with multiple locks. This violates " + "MS-CIFS 2.2.4.32.1.\n"); + + /* receive the failed lock requests */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "first lock was not cancelled immediately (%s)\n", + __location__)); + + /* send cancel to second lock */ + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | + LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "second lock was not cancelled immediately (%s)\n", + __location__)); + + /* cleanup the second lock */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* If a lock request contained multiple ranges and we are cancelling + * one while it's still pending, what happens? */ + torture_comment(tctx, "Testing cancel 1/2 lock request\n"); + + /* Send request with two ranges */ + io.lockx.in.timeout = -1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = lock; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup pending lock (%s)\n", __location__)); + + /* Try to cancel the first lock range */ + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Locking request should've failed and second range should be + * unlocked */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Cleanup both locks */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = lock; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Testing cancel 2/2 lock request\n"); + + /* Lock second range so it contends */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Send request with two ranges */ + io.lockx.in.timeout = -1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = lock; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup pending lock (%s)\n", __location__)); + + /* Try to cancel the second lock range */ + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[1]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Locking request should've failed and first range should be + * unlocked */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Cleanup both locks */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.locks = lock; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Testing cancel by unlock\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.timeout = 5000; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + t = time_mono(NULL); + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by unlock was not immediate (%s) - took %d secs\n", + __location__, (int)(time_mono(NULL)-t))); + + torture_comment(tctx, "Testing cancel by close\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + { + /* + * Make the test block on the second lock + * request. This is to regression-test 64c0367. + */ + uint64_t tmp = lock[1].offset; + lock[1].offset = lock[0].offset; + lock[0].offset = tmp; + } + + t = time_mono(NULL); + io.lockx.in.timeout = 10000; + io.lockx.in.lock_cnt = 2; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_request_simple_recv(req); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by close was not immediate (%s)\n", __location__)); + + { + /* + * Undo the change for 64c0367 + */ + uint64_t tmp = lock[1].offset; + lock[1].offset = lock[0].offset; + lock[0].offset = tmp; + } + + torture_comment(tctx, "create a new sessions\n"); + session = smbcli_session_init(cli->transport, tctx, false, options); + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + setup.in.credentials = samba_cmdline_get_creds(); + setup.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(session, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + session->vuid = setup.out.vuid; + + torture_comment(tctx, "create new tree context\n"); + share = torture_setting_string(tctx, "share", NULL); + host = torture_setting_string(tctx, "host", NULL); + tree = smbcli_tree_init(session, tctx, false); + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + tcon.tconx.in.device = "A:"; + status = smb_raw_tcon(tree, tctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + tree->tid = tcon.tconx.out.tid; + + torture_comment(tctx, "Testing cancel by exit\n"); + if (TARGET_SUPPORTS_SMBEXIT(tctx)) { + fname = BASEDIR "\\test_exit.txt"; + fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + io.lockx.in.timeout = 10000; + t = time_mono(NULL); + req = smb_raw_lock_send(tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", + __location__)); + + status = smb_raw_exit(session); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_request_simple_recv(req); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by exit was not immediate (%s)\n", + __location__)); + } + else { + torture_comment(tctx, + " skipping test, SMBExit not supported\n"); + } + + torture_comment(tctx, "Testing cancel by ulogoff\n"); + fname = BASEDIR "\\test_ulogoff.txt"; + fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + io.lockx.in.timeout = 10000; + t = time_mono(NULL); + req = smb_raw_lock_send(tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + status = smb_raw_ulogoff(session); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_request_simple_recv(req); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) { + if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) { + torture_result(tctx, TORTURE_FAIL, + "lock not canceled by ulogoff - %s " + "(ignored because of vfs_vifs fails it)\n", + nt_errstr(status)); + smb_tree_disconnect(tree); + smb_raw_exit(session); + goto done; + } + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + } else { + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + } + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by ulogoff was not immediate (%s)\n", __location__)); + + torture_comment(tctx, "Testing cancel by tdis\n"); + tree->session = cli->session; + + fname = BASEDIR "\\test_tdis.txt"; + fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_lock(tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + io.lockx.in.timeout = 10000; + t = time_mono(NULL); + req = smb_raw_lock_send(tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + status = smb_tree_disconnect(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smbcli_request_simple_recv(req); + if (TARGET_RETURNS_RANGE_NOT_LOCKED(tctx)) + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + else + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "lock cancel by tdis was not immediate (%s)\n", __location__)); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test NT_STATUS_LOCK_NOT_GRANTED vs. NT_STATUS_FILE_LOCK_CONFLICT +*/ +static bool test_errorcode(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + union smb_open op; + struct smb_lock_entry lock[2]; + NTSTATUS status; + bool ret = true; + int fnum, fnum2; + const char *fname; + struct smbcli_request *req; + time_t start; + int t; + int delay; + uint16_t deny_mode = 0; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n"); + + torture_comment(tctx, "Testing with timeout = 0\n"); + fname = BASEDIR "\\test0.txt"; + t = 0; + + /* + * the first run is with t = 0, + * the second with t > 0 (=1) + */ +next_run: + /* + * use the DENY_DOS mode, that creates two fnum's of one low-level + * file handle, this demonstrates that the cache is per fnum, not + * per file handle + */ + if (TARGET_SUPPORTS_OPENX_DENY_DOS(tctx)) + deny_mode = OPENX_MODE_DENY_DOS; + else + deny_mode = OPENX_MODE_DENY_NONE; + + op.openx.level = RAW_OPEN_OPENX; + op.openx.in.fname = fname; + op.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + op.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | deny_mode; + op.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + op.openx.in.search_attrs = 0; + op.openx.in.file_attrs = 0; + op.openx.in.write_time = 0; + op.openx.in.size = 0; + op.openx.in.timeout = 0; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.openx.out.file.fnum; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = op.openx.out.file.fnum; + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = t; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * demonstrate that the first conflicting lock on each handle give LOCK_NOT_GRANTED + * this also demonstrates that the error code cache is per file handle + * (LOCK_NOT_GRANTED is only be used when timeout is 0!) + */ + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + + /* demonstrate that each following conflict gives FILE_LOCK_CONFLICT */ + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* demonstrate that the smbpid doesn't matter */ + lock[0].pid++; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + lock[0].pid--; + + /* + * demonstrate that a successful lock with count = 0 and the same offset, + * doesn't reset the error cache + */ + lock[0].offset = 100; + lock[0].count = 0; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * demonstrate that a successful lock with count = 0 and outside the locked range, + * doesn't reset the error cache + */ + lock[0].offset = 110; + lock[0].count = 0; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 99; + lock[0].count = 0; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* demonstrate that a changing count doesn't reset the error cache */ + lock[0].offset = 100; + lock[0].count = 5; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 100; + lock[0].count = 15; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * demonstrate that a lock with count = 0 and inside the locked range, + * fails and resets the error cache + */ + lock[0].offset = 101; + lock[0].count = 0; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* demonstrate that a changing offset resets the error cache */ + lock[0].offset = 105; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 95; + lock[0].count = 9; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * demonstrate that a successful lock in a different range + * doesn't reset the cache, the failing lock on the 2nd handle + * resets the cache + */ + lock[0].offset = 120; + lock[0].count = 15; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.file.fnum = fnum; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED)); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* end of the loop */ + if (t == 0) { + smb_raw_exit(cli->session); + t = 1; + torture_comment(tctx, "Testing with timeout > 0 (=%d)\n", + t); + fname = BASEDIR "\\test1.txt"; + goto next_run; + } + + t = 4000; + torture_comment(tctx, "Testing special cases with timeout > 0 (=%d)\n", + t); + + /* + * the following 3 test sections demonstrate that + * the cache is only set when the error is reported + * to the client (after the timeout went by) + */ + smb_raw_exit(cli->session); + torture_comment(tctx, "Testing a conflict while a lock is pending\n"); + fname = BASEDIR "\\test2.txt"; + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + start = time_mono(NULL); + io.lockx.in.timeout = t; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + io.lockx.in.timeout = 0; + lock[0].offset = 105; + lock[0].count = 10; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + delay = t / 1000; + if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) { + delay /= 2; + } + + torture_assert(tctx,!(time_mono(NULL) < start+delay), talloc_asprintf(tctx, + "lock comes back to early timeout[%d] delay[%d]" + "(%s)\n", t, delay, __location__)); + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + smbcli_close(cli->tree, fnum); + fname = BASEDIR "\\test3.txt"; + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + start = time_mono(NULL); + io.lockx.in.timeout = t; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + io.lockx.in.timeout = 0; + lock[0].offset = 105; + lock[0].count = 10; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + delay = t / 1000; + if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) { + delay /= 2; + } + + torture_assert(tctx,!(time_mono(NULL) < start+delay), talloc_asprintf(tctx, + "lock comes back to early timeout[%d] delay[%d]" + "(%s)\n", t, delay, __location__)); + + lock[0].offset = 100; + lock[0].count = 10; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + smbcli_close(cli->tree, fnum); + fname = BASEDIR "\\test4.txt"; + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to reopen %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + start = time_mono(NULL); + io.lockx.in.timeout = t; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed lock (%s)\n", __location__)); + + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + delay = t / 1000; + if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) { + delay /= 2; + } + + torture_assert(tctx,!(time_mono(NULL) < start+delay), talloc_asprintf(tctx, + "lock comes back to early timeout[%d] delay[%d]" + "(%s)\n", t, delay, __location__)); + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test LOCKING_ANDX_CHANGE_LOCKTYPE +*/ +static bool test_changetype(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t c = 0; + const char *fname = BASEDIR "\\test.txt"; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCKING_ANDX_CHANGE_LOCKTYPE\n"); + io.generic.level = RAW_LOCK_LOCKX; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) { + torture_result(tctx, TORTURE_FAIL, + "allowed write on read locked region (%s)\n", __location__); + ret = false; + goto done; + } + + /* windows server don't seem to support this */ + io.lockx.in.mode = LOCKING_ANDX_CHANGE_LOCKTYPE; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks)); + + if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) { + torture_result(tctx, TORTURE_FAIL, + "allowed write after lock change (%s)\n", __location__); + ret = false; + goto done; + } + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +struct double_lock_test { + struct smb_lock_entry lock1; + struct smb_lock_entry lock2; + NTSTATUS exp_status; +}; + +/** + * Tests zero byte locks. + */ +static struct double_lock_test zero_byte_tests[] = { + /* {pid, offset, count}, {pid, offset, count}, status */ + + /** First, takes a zero byte lock at offset 10. Then: + * - Taking 0 byte lock at 10 should succeed. + * - Taking 1 byte locks at 9,10,11 should succeed. + * - Taking 2 byte lock at 9 should fail. + * - Taking 2 byte lock at 10 should succeed. + * - Taking 3 byte lock at 9 should fail. + */ + {{1000, 10, 0}, {1001, 10, 0}, NT_STATUS_OK}, + {{1000, 10, 0}, {1001, 9, 1}, NT_STATUS_OK}, + {{1000, 10, 0}, {1001, 10, 1}, NT_STATUS_OK}, + {{1000, 10, 0}, {1001, 11, 1}, NT_STATUS_OK}, + {{1000, 10, 0}, {1001, 9, 2}, NT_STATUS_LOCK_NOT_GRANTED}, + {{1000, 10, 0}, {1001, 10, 2}, NT_STATUS_OK}, + {{1000, 10, 0}, {1001, 9, 3}, NT_STATUS_LOCK_NOT_GRANTED}, + + /** Same, but opposite order. */ + {{1001, 10, 0}, {1000, 10, 0}, NT_STATUS_OK}, + {{1001, 9, 1}, {1000, 10, 0}, NT_STATUS_OK}, + {{1001, 10, 1}, {1000, 10, 0}, NT_STATUS_OK}, + {{1001, 11, 1}, {1000, 10, 0}, NT_STATUS_OK}, + {{1001, 9, 2}, {1000, 10, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + {{1001, 10, 2}, {1000, 10, 0}, NT_STATUS_OK}, + {{1001, 9, 3}, {1000, 10, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + + /** Zero zero case. */ + {{1000, 0, 0}, {1001, 0, 0}, NT_STATUS_OK}, +}; + +static bool test_zerobytelocks(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + NTSTATUS status; + bool ret = true; + int fnum, i; + const char *fname = BASEDIR "\\zero.txt"; + + torture_comment(tctx, "Testing zero length byte range locks:\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.generic.level = RAW_LOCK_LOCKX; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* Setup initial parameters */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; /* Exclusive */ + io.lockx.in.timeout = 0; + + /* Try every combination of locks in zero_byte_tests. The first lock is + * assumed to succeed. The second lock may contend, depending on the + * expected status. */ + for (i = 0; + i < ARRAY_SIZE(zero_byte_tests); + i++) { + torture_comment(tctx, " ... {%d, %llu, %llu} + {%d, %llu, %llu} = %s\n", + zero_byte_tests[i].lock1.pid, + (unsigned long long) zero_byte_tests[i].lock1.offset, + (unsigned long long) zero_byte_tests[i].lock1.count, + zero_byte_tests[i].lock2.pid, + (unsigned long long) zero_byte_tests[i].lock2.offset, + (unsigned long long) zero_byte_tests[i].lock2.count, + nt_errstr(zero_byte_tests[i].exp_status)); + + /* Lock both locks. */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + + io.lockx.in.locks = discard_const_p(struct smb_lock_entry, + &zero_byte_tests[i].lock1); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.locks = discard_const_p(struct smb_lock_entry, + &zero_byte_tests[i].lock2); + status = smb_raw_lock(cli->tree, &io); + + if (NT_STATUS_EQUAL(zero_byte_tests[i].exp_status, + NT_STATUS_LOCK_NOT_GRANTED)) { + /* Allow either of the failure messages and keep going + * if we see the wrong status. */ + CHECK_STATUS_OR_CONT(status, + NT_STATUS_LOCK_NOT_GRANTED, + NT_STATUS_FILE_LOCK_CONFLICT); + + } else { + CHECK_STATUS_CONT(status, + zero_byte_tests[i].exp_status); + } + + /* Unlock both locks. */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + } + + io.lockx.in.locks = discard_const_p(struct smb_lock_entry, + &zero_byte_tests[i].lock1); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +static bool test_unlock(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + NTSTATUS status; + bool ret = true; + int fnum1, fnum2; + const char *fname = BASEDIR "\\unlock.txt"; + struct smb_lock_entry lock1; + struct smb_lock_entry lock2; + + torture_comment(tctx, "Testing LOCKX unlock:\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum2 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* Setup initial parameters */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.timeout = 0; + + lock1.pid = cli->session->pid; + lock1.offset = 0; + lock1.count = 10; + lock2.pid = cli->session->pid - 1; + lock2.offset = 0; + lock2.count = 10; + + /** + * Take exclusive lock, then unlock it with a shared-unlock call. + */ + torture_comment(tctx, " taking exclusive lock.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = 0; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " unlock the exclusive with a shared unlock call.\n"); + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " try shared lock on pid2/fnum2, testing the unlock.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + io.lockx.in.file.fnum = fnum2; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /** + * Unlock a shared lock with an exclusive-unlock call. + */ + torture_comment(tctx, " unlock new shared lock with exclusive unlock call.\n"); + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = 0; + io.lockx.in.file.fnum = fnum2; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " try exclusive lock on pid1, testing the unlock.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = 0; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /** + * Test unlocking of 0-byte locks. + */ + + torture_comment(tctx, " lock shared and exclusive 0-byte locks, testing that Windows " + "always unlocks the exclusive first.\n"); + lock1.pid = cli->session->pid; + lock1.offset = 10; + lock1.count = 0; + lock2.pid = cli->session->pid; + lock2.offset = 5; + lock2.count = 10; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + + /* lock 0-byte shared + * Note: Order of the shared/exclusive locks doesn't matter. */ + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* lock 0-byte exclusive */ + io.lockx.in.mode = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test contention */ + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + io.lockx.in.locks = &lock2; + io.lockx.in.file.fnum = fnum2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED, + NT_STATUS_FILE_LOCK_CONFLICT); + + /* unlock */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test - can we take a shared lock? */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + io.lockx.in.file.fnum = fnum2; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + + /* XXX Samba 3 will fail this test. This is temporary(because this isn't + * new to Win7, it succeeds in WinXP too), until I can come to a + * resolution as to whether Samba should support this or not. There is + * code to preference unlocking exclusive locks before shared locks, + * but its wrapped with "#ifdef ZERO_ZERO". -zkirsch */ + if (TARGET_IS_SAMBA3(tctx)) { + CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED, + NT_STATUS_FILE_LOCK_CONFLICT); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + } + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + + /* XXX Same as above. */ + if (TARGET_IS_SAMBA3(tctx)) { + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + } + + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum1); + smbcli_close(cli->tree, fnum2); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +static bool test_multiple_unlock(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + NTSTATUS status; + bool ret = true; + int fnum1; + const char *fname = BASEDIR "\\unlock_multiple.txt"; + struct smb_lock_entry lock1; + struct smb_lock_entry lock2; + struct smb_lock_entry locks[2]; + + torture_comment(tctx, "Testing LOCKX multiple unlock:\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* Setup initial parameters */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.timeout = 0; + + lock1.pid = cli->session->pid; + lock1.offset = 0; + lock1.count = 10; + lock2.pid = cli->session->pid; + lock2.offset = 10; + lock2.count = 10; + + locks[0] = lock1; + locks[1] = lock2; + + io.lockx.in.file.fnum = fnum1; + io.lockx.in.mode = 0; /* exclusive */ + + /** Test1: Take second lock, but not first. */ + torture_comment(tctx, " unlock 2 locks, first one not locked. Expect no locks " + "unlocked. \n"); + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to unlock both locks. */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = locks; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* Second lock should not be unlocked. */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /** Test2: Take first lock, but not second. */ + torture_comment(tctx, " unlock 2 locks, second one not locked. Expect first lock " + "unlocked.\n"); + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to unlock both locks. */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = locks; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* First lock should be unlocked. */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test3: Request 2 locks, second will contend. What happens to the + * first? */ + torture_comment(tctx, " request 2 locks, second one will contend. " + "Expect both to fail.\n"); + + /* Lock the second range */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock2; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Request both locks */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.locks = locks; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* First lock should be unlocked. */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = locks; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test4: Request unlock and lock. The lock contends, is the unlock + * then re-locked? */ + torture_comment(tctx, " request unlock and lock, second one will " + "contend. Expect the unlock to succeed.\n"); + + /* Lock both ranges */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.locks = locks; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Attempt to unlock the first range and lock the second */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = locks; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* The first lock should've been unlocked */ + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + io.lockx.in.ulock_cnt = 2; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = locks; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum1); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/** + * torture_locktest5 covers stacking pretty well, but its missing two tests: + * - stacking an exclusive on top of shared fails + * - stacking two exclusives fail + */ +static bool test_stacking(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_lock io; + NTSTATUS status; + bool ret = true; + int fnum1; + const char *fname = BASEDIR "\\stacking.txt"; + struct smb_lock_entry lock1; + + torture_comment(tctx, "Testing stacking:\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.generic.level = RAW_LOCK_LOCKX; + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* Setup initial parameters */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.timeout = 0; + + lock1.pid = cli->session->pid; + lock1.offset = 0; + lock1.count = 10; + + /** + * Try to take a shared lock, then stack an exclusive. + */ + torture_comment(tctx, " stacking an exclusive on top of a shared lock fails.\n"); + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED, + NT_STATUS_FILE_LOCK_CONFLICT); + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /** + * Prove that two exclusive locks do not stack. + */ + torture_comment(tctx, " two exclusive locks do not stack.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED, + NT_STATUS_FILE_LOCK_CONFLICT); + + /* cleanup */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum1); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/** + * Test how 0-byte read requests contend with byte range locks + */ +static bool test_zerobyteread(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + union smb_read rd; + NTSTATUS status; + bool ret = true; + int fnum1, fnum2; + const char *fname = BASEDIR "\\zerobyteread.txt"; + struct smb_lock_entry lock1; + uint8_t c = 1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.generic.level = RAW_LOCK_LOCKX; + + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum2 != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* Setup initial parameters */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.timeout = 0; + + lock1.pid = cli->session->pid; + lock1.offset = 0; + lock1.count = 10; + + ZERO_STRUCT(rd); + rd.readx.level = RAW_READ_READX; + + torture_comment(tctx, "Testing zero byte read on lock range:\n"); + + /* Take an exclusive lock */ + torture_comment(tctx, " taking exclusive lock.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read */ + torture_comment(tctx, " reading 0 bytes.\n"); + rd.readx.in.file.fnum = fnum2; + rd.readx.in.offset = 5; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = &c; + status = smb_raw_read(cli->tree, &rd); + torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Unlock lock */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Testing zero byte read on zero byte lock " + "range:\n"); + + /* Take an exclusive lock */ + torture_comment(tctx, " taking exclusive 0-byte lock.\n"); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + lock1.offset = 5; + lock1.count = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read before the lock */ + torture_comment(tctx, " reading 0 bytes before the lock.\n"); + rd.readx.in.file.fnum = fnum2; + rd.readx.in.offset = 4; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = &c; + status = smb_raw_read(cli->tree, &rd); + torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read on the lock */ + torture_comment(tctx, " reading 0 bytes on the lock.\n"); + rd.readx.in.file.fnum = fnum2; + rd.readx.in.offset = 5; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = &c; + status = smb_raw_read(cli->tree, &rd); + torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read after the lock */ + torture_comment(tctx, " reading 0 bytes after the lock.\n"); + rd.readx.in.file.fnum = fnum2; + rd.readx.in.offset = 6; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = &c; + status = smb_raw_read(cli->tree, &rd); + torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Unlock lock */ + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + io.lockx.in.file.fnum = fnum1; + io.lockx.in.locks = &lock1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum1); + smbcli_close(cli->tree, fnum2); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} +/* + test multi Locking&X operation +*/ +static bool test_multilock(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock_test.txt"; + time_t t; + struct smbcli_request *req; + struct smbcli_session_options options; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Now request the same locks on a different + * context as blocking locks with infinite timeout. + */ + + io.lockx.in.timeout = 20000; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* Unlock lock[0] */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Start the clock. */ + t = time_mono(NULL); + + /* Unlock lock[1] */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* receive the successful blocked lock requests */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test multi2 Locking&X operation + This test is designed to show that + lock precedence on the server is based + on the order received, not on the ability + to grant. For example: + + A blocked lock request containing 2 locks + will be satisfied before a subsequent blocked + lock request over one of the same regions, + even if that region is then unlocked. E.g. + + (a) lock 100->109, 120->129 (granted) + (b) lock 100->109, 120-129 (blocks) + (c) lock 100->109 (blocks) + (d) unlock 100->109 + + lock (c) will not be granted as lock (b) + will take precedence. +*/ +static bool test_multilock2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock2_test.txt"; + time_t t; + struct smbcli_request *req; + struct smbcli_request *req2; + struct smbcli_session_options options; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &options); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock 2\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Now request the same locks on a different + * context as blocking locks. + */ + + io.lockx.in.timeout = 20000; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * Request the first lock again on a separate context. + * Wait 2 seconds. This should time out (the previous + * multi-lock request should take precedence). + */ + + io.lockx.in.timeout = 2000; + lock[0].pid = cli->session->pid+2; + io.lockx.in.lock_cnt = 1; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* Unlock lock[0] */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Did the second lock complete (should time out) ? */ + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* Start the clock. */ + t = time_mono(NULL); + + /* Unlock lock[1] */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* receive the successful blocked lock requests */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test multi3 Locking&X operation + This test is designed to show that + lock precedence on the server is based + on the order received, not on the ability + to grant. + + Compared to test_multilock2() (above) + this test demonstrates that completely + unrelated ranges work independently. + + For example: + + A blocked lock request containing 2 locks + will be satisfied before a subsequent blocked + lock request over one of the same regions, + even if that region is then unlocked. But + a lock of a different region goes through. E.g. + + All locks are LOCKING_ANDX_EXCLUSIVE_LOCK (rw). + + (a) lock 100->109, 120->129 (granted) + (b) lock 100->109, 120->129 (blocks, timeout=20s) + (c) lock 100->109 (blocks, timeout=2s) + (d) lock 110->119 (granted) + (e) lock 110->119 (blocks, timeout=20s) + (f) unlock 100->109 (a) + (g) lock 100->109 (not granted, blocked by (b)) + (h) lock 100->109 (not granted, blocked by itself (b)) + (i) lock (c) will not be granted(conflict, times out) + as lock (b) will take precedence. + (j) unlock 110-119 (d) + (k) lock (e) completes and is not blocked by (a) nor (b) + (l) lock 100->109 (not granted(conflict), blocked by (b)) + (m) lock 100->109 (not granted(conflict), blocked by itself (b)) + (n) unlock 120-129 (a) + (o) lock (b) completes +*/ +static bool test_multilock3(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + union smb_lock io3; + struct smb_lock_entry lock3[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock3_test.txt"; + time_t t; + struct smbcli_request *req = NULL; + struct smbcli_request *req2 = NULL; + struct smbcli_request *req4 = NULL; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock 3\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * a) + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * b) + * Now request the same locks on a different + * context as blocking locks. + */ + io.lockx.in.timeout = 20000; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * c) + * Request the first lock again on a separate context. + * Wait 2 seconds. This should time out (the previous + * multi-lock request should take precedence). + */ + io.lockx.in.timeout = 2000; + lock[0].pid = cli->session->pid+2; + io.lockx.in.lock_cnt = 1; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * d) + * Lock regions 110->119 + */ + io3.lockx.level = RAW_LOCK_LOCKX; + io3.lockx.in.file.fnum = fnum; + io3.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 0; + io3.lockx.in.lock_cnt = 1; + io3.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock3[0].pid = cli->session->pid+3; + lock3[0].offset = 110; + lock3[0].count = 10; + io3.lockx.in.locks = &lock3[0]; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * e) + * try 110-119 again + */ + io3.lockx.in.timeout = 20000; + lock3[0].pid = cli->session->pid+4; + req4 = smb_raw_lock_send(cli->tree, &io3); + torture_assert(tctx,(req4 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * f) + * Unlock (a) lock[0] 100-109 + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * g) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+5; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * h) + * try to lock lock[0] 100-109 again with + * the pid that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req2->state <= SMBCLI_REQUEST_RECV, + "req2 should still wait"); + + /* + * i) + * Did the second lock complete (should time out) ? + */ + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + torture_assert(tctx, req4->state <= SMBCLI_REQUEST_RECV, + "req4 should still wait"); + + /* + * j) + * Unlock (d) lock[0] 110-119 + */ + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 1; + io3.lockx.in.lock_cnt = 0; + lock3[0].pid = cli->session->pid+3; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * k) + * receive the successful blocked lock request (e) + * on 110-119 while the 100-109/120-129 is still waiting. + */ + status = smbcli_request_simple_recv(req4); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * l) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+6; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* + * m) + * try to lock lock[0] 100-109 again with + * the pid that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* Start the clock. */ + t = time_mono(NULL); + + /* + * n) + * Unlock lock[1] 120-129 */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * o) + * receive the successful blocked lock request (b) + */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test multi4 Locking&X operation + This test is designed to show that + lock precedence on the server is based + on the order received, not on the ability + to grant. + + Compared to test_multilock3() (above) + this test demonstrates that pending read-only/shared + locks doesn't block shared locks others. + + The outstanding requests build an implicit + database that's checked before checking + the already granted locks in the real database. + + For example: + + A blocked read-lock request containing 2 locks + will be still be blocked, while one region + is still write-locked. While it doesn't block + other read-lock requests for the other region. E.g. + + (a) lock(rw) 100->109, 120->129 (granted) + (b) lock(ro) 100->109, 120->129 (blocks, timeout=20s) + (c) lock(ro) 100->109 (blocks, timeout=MAX) + (d) lock(rw) 110->119 (granted) + (e) lock(rw) 110->119 (blocks, timeout=20s) + (f) unlock 100->109 (a) + (g) lock(ro) (c) completes and is not blocked by (a) nor (b) + (h) lock(rw) 100->109 (not granted, blocked by (c)) + (i) lock(rw) 100->109 (pid (b)) (not granted(conflict), blocked by (c)) + (j) unlock 110-119 + (k) lock (e) completes and is not blocked by (a) nor (b) + (l) lock 100->109 (not granted(conflict), blocked by (b)) + (m) lock 100->109 (pid (b)) (not granted(conflict), blocked by itself (b)) + (n) unlock 120-129 (a) + (o) lock (b) completes +*/ +static bool test_multilock4(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + union smb_lock io3; + struct smb_lock_entry lock3[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock4_test.txt"; + time_t t; + struct smbcli_request *req = NULL; + struct smbcli_request *req2 = NULL; + struct smbcli_request *req4 = NULL; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock 4\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * a) + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * b) + * Now request the same locks on a different + * context as blocking locks. But readonly. + */ + io.lockx.in.timeout = 20000; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * c) + * Request the first lock again on a separate context. + * Wait forever. The previous multi-lock request (b) + * should take precedence. Also readonly. + */ + io.lockx.in.timeout = UINT32_MAX; + lock[0].pid = cli->session->pid+2; + io.lockx.in.lock_cnt = 1; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * d) + * Lock regions 110->119 + */ + io3.lockx.level = RAW_LOCK_LOCKX; + io3.lockx.in.file.fnum = fnum; + io3.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 0; + io3.lockx.in.lock_cnt = 1; + io3.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock3[0].pid = cli->session->pid+3; + lock3[0].offset = 110; + lock3[0].count = 10; + io3.lockx.in.locks = &lock3[0]; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * e) + * try 110-119 again + */ + io3.lockx.in.timeout = 20000; + lock3[0].pid = cli->session->pid+4; + req4 = smb_raw_lock_send(cli->tree, &io3); + torture_assert(tctx,(req4 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * f) + * Unlock (a) lock[0] 100-109 + */ + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * g) + * receive the successful blocked lock request (c) + * on 110-119 while (b) 100-109/120-129 is still waiting. + */ + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * h) + * try to lock lock[0] 100-109 again + * (read/write) + */ + lock[0].pid = cli->session->pid+5; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * i) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting. + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + torture_assert(tctx, req4->state <= SMBCLI_REQUEST_RECV, + "req4 should still wait"); + + /* + * j) + * Unlock (d) lock[0] 110-119 + */ + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 1; + io3.lockx.in.lock_cnt = 0; + lock3[0].pid = cli->session->pid+3; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * k) + * receive the successful blocked + * lock request (e) on 110-119. + */ + status = smbcli_request_simple_recv(req4); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * l) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+6; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * m) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* Start the clock. */ + t = time_mono(NULL); + + /* + * n) + * Unlock (a) lock[1] 120-129 + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * o) + * receive the successful blocked lock request (b) + */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test multi5 Locking&X operation + This test is designed to show that + lock precedence on the server is based + on the order received, not on the ability + to grant. + + Compared to test_multilock3() (above) + this test demonstrates that the initial + lock request that block the following + exclusive locks can be a shared lock. + + For example: + + All locks except (a) are LOCKING_ANDX_EXCLUSIVE_LOCK (rw). + + (a) lock(ro) 100->109, 120->129 (granted) + (b) lock 100->109, 120->129 (blocks, timeout=20s) + (c) lock 100->109 (blocks, timeout=2s) + (d) lock 110->119 (granted) + (e) lock 110->119 (blocks, timeout=20s) + (f) unlock 100->109 (a) + (g) lock 100->109 (not granted, blocked by (b)) + (h) lock 100->109 (not granted, blocked by itself (b)) + (i) lock (c) will not be granted(conflict, times out) + as lock (b) will take precedence. + (j) unlock 110-119 (d) + (k) lock (e) completes and is not blocked by (a) nor (b) + (l) lock 100->109 (not granted(conflict), blocked by (b)) + (m) lock 100->109 (not granted(conflict), blocked by itself (b)) + (n) unlock 120-129 (a) + (o) lock (b) completes +*/ +static bool test_multilock5(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + union smb_lock io3; + struct smb_lock_entry lock3[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock5_test.txt"; + time_t t; + struct smbcli_request *req = NULL; + struct smbcli_request *req2 = NULL; + struct smbcli_request *req4 = NULL; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock 5\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * a) + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + * (read only) + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * b) + * Now request the same locks on a different + * context as blocking locks. + * (read write) + */ + io.lockx.in.timeout = 20000; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * c) + * Request the first lock again on a separate context. + * Wait 2 seconds. This should time out (the previous + * multi-lock request should take precedence). + * (read write) + */ + io.lockx.in.timeout = 2000; + lock[0].pid = cli->session->pid+2; + io.lockx.in.lock_cnt = 1; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * d) + * Lock regions 110->119 + */ + io3.lockx.level = RAW_LOCK_LOCKX; + io3.lockx.in.file.fnum = fnum; + io3.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 0; + io3.lockx.in.lock_cnt = 1; + io3.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock3[0].pid = cli->session->pid+3; + lock3[0].offset = 110; + lock3[0].count = 10; + io3.lockx.in.locks = &lock3[0]; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * e) + * try 110-119 again + */ + io3.lockx.in.timeout = 20000; + lock3[0].pid = cli->session->pid+4; + req4 = smb_raw_lock_send(cli->tree, &io3); + torture_assert(tctx,(req4 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * f) + * Unlock (a) lock[0] 100-109 + * + * Note we send LOCKING_ANDX_EXCLUSIVE_LOCK + * while the lock used LOCKING_ANDX_SHARED_LOCK + * to check if that also works. + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * g) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+5; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * h) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting. + * (read write) + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req2->state <= SMBCLI_REQUEST_RECV, + "req2 should still wait"); + + /* + * i) + * Did the second lock complete (should time out) ? + */ + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + torture_assert(tctx, req4->state <= SMBCLI_REQUEST_RECV, + "req4 should still wait"); + + /* + * j) + * Unlock (d) lock[0] 110-119 + */ + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 1; + io3.lockx.in.lock_cnt = 0; + lock3[0].pid = cli->session->pid+3; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * k) + * receive the successful blocked lock requests + * on 110-119 while the 100-109/120-129 is still waiting. + */ + status = smbcli_request_simple_recv(req4); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * l) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+6; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * m) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* Start the clock. */ + t = time_mono(NULL); + + /* + * n) + * Unlock (a) lock[1] 120-129 + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * o) + * receive the successful blocked lock request (b) + */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test multi6 Locking&X operation + This test is designed to show that + lock precedence on the server is based + on the order received, not on the ability + to grant. + + Compared to test_multilock4() (above) + this test demonstrates the behavior if + only just the first blocking lock + being a shared lock. + + For example: + + All locks except (b) are LOCKING_ANDX_EXCLUSIVE_LOCK (rw). + + (a) lock 100->109, 120->129 (granted) + (b) lock(ro) 100->109, 120->129 (blocks, timeout=20s) + (c) lock 100->109 (blocks, timeout=2s) + (d) lock 110->119 (granted) + (e) lock 110->119 (blocks, timeout=20s) + (f) unlock 100->109 (a) + (g) lock 100->109 (not granted, blocked by (b)) + (h) lock 100->109 (not granted, blocked by itself (b)) + (i) lock (c) will not be granted(conflict, times out) + as lock (b) will take precedence. + (j) unlock 110-119 (d) + (k) lock (e) completes and is not blocked by (a) nor (b) + (l) lock 100->109 (not granted(conflict), blocked by (b)) + (m) lock 100->109 (not granted(conflict), blocked by itself (b)) + (n) unlock 120-129 (a) + (o) lock (b) completes +*/ +static bool test_multilock6(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_lock io; + struct smb_lock_entry lock[2]; + union smb_lock io3; + struct smb_lock_entry lock3[1]; + NTSTATUS status; + bool ret = true; + int fnum; + const char *fname = BASEDIR "\\multilock6_test.txt"; + time_t t; + struct smbcli_request *req = NULL; + struct smbcli_request *req2 = NULL; + struct smbcli_request *req4 = NULL; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing LOCKING_ANDX multi-lock 6\n"); + io.generic.level = RAW_LOCK_LOCKX; + + /* Create the test file. */ + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree))); + + /* + * a) + * Lock regions 100->109, 120->129 as + * two separate write locks in one request. + */ + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 2; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid; + lock[0].offset = 100; + lock[0].count = 10; + lock[1].pid = cli->session->pid; + lock[1].offset = 120; + lock[1].count = 10; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * b) + * Now request the same locks on a different + * context as blocking locks. + * (read only) + */ + io.lockx.in.timeout = 20000; + io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK; + lock[0].pid = cli->session->pid+1; + lock[1].pid = cli->session->pid+1; + req = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * c) + * Request the first lock again on a separate context. + * Wait 2 seconds. This should time out (the previous + * multi-lock request should take precedence). + */ + io.lockx.in.timeout = 2000; + io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock[0].pid = cli->session->pid+2; + io.lockx.in.lock_cnt = 1; + req2 = smb_raw_lock_send(cli->tree, &io); + torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * d) + * Lock regions 110->119 + */ + io3.lockx.level = RAW_LOCK_LOCKX; + io3.lockx.in.file.fnum = fnum; + io3.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 0; + io3.lockx.in.lock_cnt = 1; + io3.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK; + lock3[0].pid = cli->session->pid+3; + lock3[0].offset = 110; + lock3[0].count = 10; + io3.lockx.in.locks = &lock3[0]; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * e) + * try 110-119 again + */ + io3.lockx.in.timeout = 20000; + lock3[0].pid = cli->session->pid+4; + req4 = smb_raw_lock_send(cli->tree, &io3); + torture_assert(tctx,(req4 != NULL), talloc_asprintf(tctx, + "Failed to setup timed locks (%s)\n", __location__)); + + /* + * f) + * Unlock (a) lock[0] 100-109 + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[0]; + lock[0].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * g) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+5; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * h) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req2->state <= SMBCLI_REQUEST_RECV, + "req2 should still wait"); + + /* + * i) + * Did the second lock (c) complete (should time out) ? + */ + status = smbcli_request_simple_recv(req2); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + torture_assert(tctx, req4->state <= SMBCLI_REQUEST_RECV, + "req4 should still wait"); + + /* + * j) + * Unlock (d) lock[0] 110-119 + */ + io3.lockx.in.timeout = 0; + io3.lockx.in.ulock_cnt = 1; + io3.lockx.in.lock_cnt = 0; + lock3[0].pid = cli->session->pid+3; + status = smb_raw_lock(cli->tree, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * k) + * receive the successful blocked lock request (e) + * on 110-119 while (b) 100-109/120-129 is still waiting. + */ + status = smbcli_request_simple_recv(req4); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * l) + * try to lock lock[0] 100-109 again + */ + lock[0].pid = cli->session->pid+6; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + /* + * m) + * try to lock lock[0] 100-109 again with the pid (b) + * that's still waiting + */ + lock[0].pid = cli->session->pid+1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_assert(tctx, req->state <= SMBCLI_REQUEST_RECV, + "req should still wait"); + + /* Start the clock. */ + t = time_mono(NULL); + + /* + * n) + * Unlock (a) lock[1] 120-129 + */ + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.locks = &lock[1]; + lock[1].pid = cli->session->pid; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * o) + * receive the successful blocked lock request (b) + */ + status = smbcli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Fail if this took more than 2 seconds. */ + torture_assert(tctx,!(time_mono(NULL) > t+2), talloc_asprintf(tctx, + "Blocking locks were not granted immediately (%s)\n", + __location__)); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + basic testing of lock calls +*/ +struct torture_suite *torture_raw_lock(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "lock"); + + torture_suite_add_1smb_test(suite, "lockx", test_lockx); + torture_suite_add_1smb_test(suite, "lock", test_lock); + torture_suite_add_1smb_test(suite, "pidhigh", test_pidhigh); + torture_suite_add_1smb_test(suite, "async", test_async); + torture_suite_add_1smb_test(suite, "errorcode", test_errorcode); + torture_suite_add_1smb_test(suite, "changetype", test_changetype); + + torture_suite_add_1smb_test(suite, "stacking", test_stacking); + torture_suite_add_1smb_test(suite, "unlock", test_unlock); + torture_suite_add_1smb_test(suite, "multiple_unlock", + test_multiple_unlock); + torture_suite_add_1smb_test(suite, "zerobytelocks", test_zerobytelocks); + torture_suite_add_1smb_test(suite, "zerobyteread", test_zerobyteread); + torture_suite_add_1smb_test(suite, "multilock", test_multilock); + torture_suite_add_1smb_test(suite, "multilock2", test_multilock2); + torture_suite_add_1smb_test(suite, "multilock3", test_multilock3); + torture_suite_add_1smb_test(suite, "multilock4", test_multilock4); + torture_suite_add_1smb_test(suite, "multilock5", test_multilock5); + torture_suite_add_1smb_test(suite, "multilock6", test_multilock6); + + return suite; +} diff --git a/source4/torture/raw/lockbench.c b/source4/torture/raw/lockbench.c new file mode 100644 index 0000000..f122976 --- /dev/null +++ b/source4/torture/raw/lockbench.c @@ -0,0 +1,447 @@ +/* + Unix SMB/CIFS implementation. + + locking benchmark + + Copyright (C) Andrew Tridgell 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "torture/raw/proto.h" +#include "libcli/smb/smbXcli_base.h" +#include "../lib/util/util_net.h" + +#define BASEDIR "\\benchlock" +#define FNAME BASEDIR "\\lock.dat" + +static int nprocs; +static int lock_failed; +static int num_connected; + +enum lock_stage {LOCK_INITIAL, LOCK_LOCK, LOCK_UNLOCK}; + +struct benchlock_state { + struct torture_context *tctx; + struct tevent_context *ev; + struct smbcli_tree *tree; + TALLOC_CTX *mem_ctx; + int client_num; + int fnum; + enum lock_stage stage; + int lock_offset; + int unlock_offset; + int count; + int lastcount; + struct smbcli_request *req; + struct smb_composite_connect reconnect; + struct tevent_timer *te; + + /* these are used for reconnections */ + const char **dest_ports; + const char *dest_host; + const char *called_name; + const char *service_type; +}; + +static void lock_completion(struct smbcli_request *); + +/* + send the next lock request +*/ +static void lock_send(struct benchlock_state *state) +{ + union smb_lock io; + struct smb_lock_entry lock; + + switch (state->stage) { + case LOCK_INITIAL: + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + state->lock_offset = 0; + state->unlock_offset = 0; + lock.offset = state->lock_offset; + break; + case LOCK_LOCK: + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + state->lock_offset = (state->lock_offset+1)%(nprocs+1); + lock.offset = state->lock_offset; + break; + case LOCK_UNLOCK: + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + lock.offset = state->unlock_offset; + state->unlock_offset = (state->unlock_offset+1)%(nprocs+1); + break; + } + + lock.count = 1; + lock.pid = state->tree->session->pid; + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 100000; + io.lockx.in.locks = &lock; + io.lockx.in.file.fnum = state->fnum; + + state->req = smb_raw_lock_send(state->tree, &io); + if (state->req == NULL) { + DEBUG(0,("Failed to setup lock\n")); + lock_failed++; + } + state->req->async.private_data = state; + state->req->async.fn = lock_completion; +} + +static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data); + + +static void reopen_file(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct benchlock_state *state = (struct benchlock_state *)private_data; + + /* reestablish our open file */ + state->fnum = smbcli_open(state->tree, FNAME, O_RDWR|O_CREAT, DENY_NONE); + if (state->fnum == -1) { + printf("Failed to open %s on connection %d\n", FNAME, state->client_num); + exit(1); + } + + num_connected++; + + DEBUG(0,("reconnect to %s finished (%u connected)\n", state->dest_host, + num_connected)); + + state->stage = LOCK_INITIAL; + lock_send(state); +} + +/* + complete an async reconnect + */ +static void reopen_connection_complete(struct composite_context *ctx) +{ + struct benchlock_state *state = (struct benchlock_state *)ctx->async.private_data; + NTSTATUS status; + struct smb_composite_connect *io = &state->reconnect; + + status = smb_composite_connect_recv(ctx, state->mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + return; + } + + talloc_free(state->tree); + state->tree = io->out.tree; + + /* do the reopen as a separate event */ + tevent_add_timer(state->ev, state->mem_ctx, timeval_zero(), reopen_file, state); +} + + + +/* + reopen a connection + */ +static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct benchlock_state *state = (struct benchlock_state *)private_data; + struct composite_context *ctx; + struct smb_composite_connect *io = &state->reconnect; + char *host, *share; + + state->te = NULL; + + if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) { + DEBUG(0,("Can't find host/share for reconnect?!\n")); + exit(1); + } + + io->in.dest_host = state->dest_host; + io->in.dest_ports = state->dest_ports; + io->in.gensec_settings = lpcfg_gensec_settings(state->mem_ctx, state->tctx->lp_ctx); + io->in.socket_options = lpcfg_socket_options(state->tctx->lp_ctx); + io->in.called_name = state->called_name; + io->in.service = share; + io->in.service_type = state->service_type; + io->in.credentials = samba_cmdline_get_creds(); + io->in.fallback_to_anonymous = false; + io->in.workgroup = lpcfg_workgroup(state->tctx->lp_ctx); + lpcfg_smbcli_options(state->tctx->lp_ctx, &io->in.options); + lpcfg_smbcli_session_options(state->tctx->lp_ctx, &io->in.session_options); + + /* kill off the remnants of the old connection */ + talloc_free(state->tree); + state->tree = NULL; + + ctx = smb_composite_connect_send(io, state->mem_ctx, + lpcfg_resolve_context(state->tctx->lp_ctx), + state->ev); + if (ctx == NULL) { + DEBUG(0,("Failed to setup async reconnect\n")); + exit(1); + } + + ctx->async.fn = reopen_connection_complete; + ctx->async.private_data = state; +} + + +/* + called when a lock completes +*/ +static void lock_completion(struct smbcli_request *req) +{ + struct benchlock_state *state = (struct benchlock_state *)req->async.private_data; + NTSTATUS status = smbcli_request_simple_recv(req); + state->req = NULL; + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + state->tree = NULL; + num_connected--; + DEBUG(0,("reopening connection to %s\n", state->dest_host)); + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + } else { + DEBUG(0,("Lock failed - %s\n", nt_errstr(status))); + lock_failed++; + } + return; + } + + switch (state->stage) { + case LOCK_INITIAL: + state->stage = LOCK_LOCK; + break; + case LOCK_LOCK: + state->stage = LOCK_UNLOCK; + break; + case LOCK_UNLOCK: + state->stage = LOCK_LOCK; + break; + } + + state->count++; + lock_send(state); +} + + +static void echo_completion(struct smbcli_request *req) +{ + struct benchlock_state *state = (struct benchlock_state *)req->async.private_data; + NTSTATUS status = smbcli_request_simple_recv(req); + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + state->tree = NULL; + num_connected--; + DEBUG(0,("reopening connection to %s\n", state->dest_host)); + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + } +} + +static void report_rate(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct benchlock_state *state = talloc_get_type(private_data, + struct benchlock_state); + int i; + for (i=0;isession->transport, &p); + req->async.private_data = &state[i]; + req->async.fn = echo_completion; + } +} + +/* + benchmark locking calls +*/ +bool torture_bench_lock(struct torture_context *torture) +{ + bool ret = true; + TALLOC_CTX *mem_ctx = talloc_new(torture); + int i, j; + int timelimit = torture_setting_int(torture, "timelimit", 10); + struct timeval tv; + struct benchlock_state *state; + int total = 0, minops=0; + struct smbcli_state *cli; + bool progress; + off_t offset; + int initial_locks = torture_setting_int(torture, "initial_locks", 0); + + progress = torture_setting_bool(torture, "progress", true); + + nprocs = torture_setting_int(torture, "nprocs", 4); + + state = talloc_zero_array(mem_ctx, struct benchlock_state, nprocs); + + printf("Opening %d connections\n", nprocs); + for (i=0;iev; + if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) { + return false; + } + talloc_steal(state[i].mem_ctx, cli); + state[i].tree = cli->tree; + + dest_ss = smbXcli_conn_remote_sockaddr( + state[i].tree->session->transport->conn); + dest_str = print_sockaddr(addrstr, sizeof(addrstr), dest_ss); + dest_port = get_sockaddr_port(dest_ss); + + state[i].dest_host = talloc_strdup(state[i].mem_ctx, dest_str); + state[i].dest_ports = talloc_array(state[i].mem_ctx, + const char *, 2); + state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports, + "%u", dest_port); + state[i].dest_ports[1] = NULL; + state[i].called_name = talloc_strdup(state[i].mem_ctx, + smbXcli_conn_remote_name(cli->tree->session->transport->conn)); + state[i].service_type = talloc_strdup(state[i].mem_ctx, "?????"); + } + + num_connected = i; + + if (!torture_setup_dir(cli, BASEDIR)) { + goto failed; + } + + for (i=0;i 0) { + printf("Initializing %d locks on each proc.\n", + initial_locks); + } + + for (j = 0; j < initial_locks; j++) { + offset = (0xFFFFFED8LLU * (i+2)) + j; + if (!NT_STATUS_IS_OK(smbcli_lock64(state[i].tree, + state[i].fnum, offset, 1, 0, WRITE_LOCK))) { + printf("Failed initializing, lock=%d\n", j); + goto failed; + } + } + + state[i].stage = LOCK_INITIAL; + lock_send(&state[i]); + } + + tv = timeval_current(); + + if (progress) { + tevent_add_timer(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state); + } + + printf("Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + tevent_loop_once(torture->ev); + + if (lock_failed) { + DEBUG(0,("locking failed\n")); + goto failed; + } + } + + printf("%.2f ops/second\n", total/timeval_elapsed(&tv)); + minops = state[0].count; + for (i=0;isession); + } + + smbcli_deltree(state[0].tree, BASEDIR); + talloc_free(mem_ctx); + printf("\n"); + return ret; + +failed: + smbcli_deltree(state[0].tree, BASEDIR); + talloc_free(mem_ctx); + return false; +} diff --git a/source4/torture/raw/lookuprate.c b/source4/torture/raw/lookuprate.c new file mode 100644 index 0000000..4243e35 --- /dev/null +++ b/source4/torture/raw/lookuprate.c @@ -0,0 +1,318 @@ +/* + File lookup rate test. + + Copyright (C) James Peach 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "torture/smbtorture.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\lookuprate" +#define MISSINGNAME BASEDIR "\\foo" + +#define FUZZ_PERCENT 10 + +#define usec_to_sec(s) ((s) / 1000000) +#define sec_to_usec(s) ((s) * 1000000) + +struct rate_record +{ + unsigned dirent_count; + unsigned querypath_persec; + unsigned findfirst_persec; +}; + +static struct rate_record records[] = +{ + { 0, 0, 0 }, /* Base (optimal) lookup rate. */ + { 100, 0, 0}, + { 1000, 0, 0}, + { 10000, 0, 0}, + { 100000, 0, 0} +}; + +typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path); + +/* Test whether rhs is within fuzz% of lhs. */ +static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent) +{ + double fuzz = (double)lhs * (double)percent/100.0; + + if (((double)rhs >= ((double)lhs - fuzz)) && + ((double)rhs <= ((double)lhs + fuzz))) { + return true; + } + + return false; + +} + +static NTSTATUS fill_directory(struct smbcli_tree *tree, + const char * path, unsigned count) +{ + NTSTATUS status; + char *fname = NULL; + unsigned i; + unsigned current; + + struct timeval start; + struct timeval now; + + status = smbcli_mkdir(tree, path); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + printf("filling directory %s with %u files... ", path, count); + fflush(stdout); + + current = random(); + start = timeval_current(); + + for (i = 0; i < count; ++i) { + int fnum; + + ++current; + fname = talloc_asprintf(NULL, "%s\\fill%u", + path, current); + + fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT, + DENY_NONE); + if (fnum < 0) { + talloc_free(fname); + return smbcli_nt_error(tree); + } + + smbcli_close(tree, fnum); + talloc_free(fname); + } + + if (count) { + double rate; + now = timeval_current(); + rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start)); + printf("%u/sec\n", (unsigned)rate); + } else { + printf("done\n"); + } + + return NT_STATUS_OK; +} + +static NTSTATUS squash_lookup_error(NTSTATUS status) +{ + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_OK; + } + + /* We don't care if the file isn't there. */ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) { + return NT_STATUS_OK; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return NT_STATUS_OK; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) { + return NT_STATUS_OK; + } + + return status; +} + +/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */ +static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path) +{ + NTSTATUS status; + time_t ftimes[3]; + size_t fsize; + uint16_t fmode; + + status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2], + &fsize, &fmode); + + return squash_lookup_error(status); +} + +/* Look up a pathname using TRANS2_FIND_FIRST2. */ +static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path) +{ + NTSTATUS status = NT_STATUS_OK; + + if (smbcli_list(tree, path, 0, NULL, NULL) < 0) { + status = smbcli_nt_error(tree); + } + + return squash_lookup_error(status); +} + +static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree, + lookup_function lookup, const char * path, unsigned * rate) +{ + NTSTATUS status; + + struct timeval start; + struct timeval now; + unsigned count = 0; + int64_t elapsed = 0; + +#define LOOKUP_PERIOD_SEC (2) + + start = timeval_current(); + while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) { + + status = lookup(tree, path); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ++count; + now = timeval_current(); + elapsed = usec_time_diff(&now, &start); + } + +#undef LOOKUP_PERIOD_SEC + + *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed)); + return NT_STATUS_OK; +} + +static bool remove_working_directory(struct smbcli_tree *tree, + const char * path) +{ + int tries; + + /* Using smbcli_deltree to delete a very large number of files + * doesn't work against all servers. Work around this by + * retrying. + */ + for (tries = 0; tries < 5; ) { + int ret; + + ret = smbcli_deltree(tree, BASEDIR); + if (ret == -1) { + tries++; + printf("(%s) failed to deltree %s: %s\n", + __location__, BASEDIR, + smbcli_errstr(tree)); + continue; + } + + return true; + } + + return false; + +} + +/* Verify that looking up a file name takes constant time. + * + * This test samples the lookup rate for a non-existent filename in a + * directory, while varying the number of files in the directory. The + * lookup rate should continue to approximate the lookup rate for the + * empty directory case. + */ +bool torture_bench_lookup(struct torture_context *torture) +{ + NTSTATUS status; + bool result = false; + + int i; + struct smbcli_state *cli = NULL; + + if (!torture_open_connection(&cli, torture, 0)) { + goto done; + } + + remove_working_directory(cli->tree, BASEDIR); + + for (i = 0; i < ARRAY_SIZE(records); ++i) { + printf("Testing lookup rate with %u directory entries\n", + records[i].dirent_count); + + status = fill_directory(cli->tree, BASEDIR, + records[i].dirent_count); + if (!NT_STATUS_IS_OK(status)) { + printf("failed to fill directory: %s\n", nt_errstr(status)); + goto done; + } + + status = lookup_rate_convert(cli->tree, querypath_lookup, + MISSINGNAME, &records[i].querypath_persec); + if (!NT_STATUS_IS_OK(status)) { + printf("querypathinfo of %s failed: %s\n", + MISSINGNAME, nt_errstr(status)); + goto done; + } + + status = lookup_rate_convert(cli->tree, findfirst_lookup, + MISSINGNAME, &records[i].findfirst_persec); + if (!NT_STATUS_IS_OK(status)) { + printf("findfirst of %s failed: %s\n", + MISSINGNAME, nt_errstr(status)); + goto done; + } + + printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n", + records[i].dirent_count, + records[i].querypath_persec, + records[i].findfirst_persec); + + if (!remove_working_directory(cli->tree, BASEDIR)) { + goto done; + } + } + + /* Ok. We have run all our tests. Walk through the records we + * accumulated and figure out whether the lookups took constant + * time or not. + */ + result = true; + for (i = 0; i < ARRAY_SIZE(records); ++i) { + if (!fuzzily_equal(records[0].querypath_persec, + records[i].querypath_persec, + FUZZ_PERCENT)) { + printf("querypath rate for %d entries differed by " + "more than %d%% from base rate\n", + records[i].dirent_count, FUZZ_PERCENT); + result = false; + } + + if (!fuzzily_equal(records[0].findfirst_persec, + records[i].findfirst_persec, + FUZZ_PERCENT)) { + printf("findfirst rate for %d entries differed by " + "more than %d%% from base rate\n", + records[i].dirent_count, FUZZ_PERCENT); + result = false; + } + } + +done: + if (cli) { + remove_working_directory(cli->tree, BASEDIR); + talloc_free(cli); + } + + return result; +} + +/* vim: set sts=8 sw=8 : */ diff --git a/source4/torture/raw/missing.txt b/source4/torture/raw/missing.txt new file mode 100644 index 0000000..0f4104b --- /dev/null +++ b/source4/torture/raw/missing.txt @@ -0,0 +1,160 @@ +- RAW-CONTEXT passes on nt4 but TCON doesn't !!?? + +- all messaging commands + +- writebraw + +- writebmpx + +- acl ops + +- readbmpx + +- rap commands + +- rpc commands + +- SMBcopy + +- SMBtcon + +- SMBecho + +- SMBfunique + +- SMBsearch vs SMBffirst? + +- SMBfclose + +- SMBkeepalive + +- secondary trans2 and nttrans + +- trans2 ioctl + +- trans2 session setup + +- trans2 DFS ops + +- unix ops + +-------------- +done: + +mkdir +rmdir +open +create +close +flush +unlink +mv +getatr +setatr +read +write +lock +unlock +ctemp +mknew +chkpath +exit +lseek +tconX +tdis +negprot +dskattr +search +lockread +writeunlock +readbraw +setattrE +getattrE +lockingX +ioctl +openX +readX +writeX +sesssetupX +trans2 +findclose +ulogoffX +nttrans +ntcreateX +ntcancel +trans2_open +trans2_findfirst +trans2_findnext? +trans2_qfsinfo +trans2_setfsinfo +trans2_qpathinfo +trans2_setpathinfo +trans2_qfileinfo +trans2_setfileinfo +trans2_fsctl +trans2_mkdir +trans2_findnext +NTrename +SMB_QFS_ALLOCATION +SMB_QFS_VOLUME +SMB_QFS_VOLUME_INFO +SMB_QFS_SIZE_INFO +SMB_QFS_DEVICE_INFO +SMB_QFS_ATTRIBUTE_INFO +SMB_QFS_VOLUME_INFORMATION +SMB_QFS_SIZE_INFORMATION +SMB_QFS_DEVICE_INFORMATION +SMB_QFS_ATTRIBUTE_INFORMATION +SMB_QFS_QUOTA_INFORMATION +SMB_QFS_FULL_SIZE_INFORMATION +SMB_QFS_OBJECTID_INFORMATION +SMB_QFILEINFO_STANDARD +SMB_QFILEINFO_EA_SIZE +SMB_QFILEINFO_ALL_EAS +SMB_QFILEINFO_IS_NAME_VALID +SMB_QFILEINFO_BASIC_INFO +SMB_QFILEINFO_STANDARD_INFO +SMB_QFILEINFO_EA_INFO +SMB_QFILEINFO_NAME_INFO +SMB_QFILEINFO_ALL_INFO +SMB_QFILEINFO_ALT_NAME_INFO +SMB_QFILEINFO_STREAM_INFO +SMB_QFILEINFO_COMPRESSION_INFO +SMB_QFILEINFO_BASIC_INFORMATION +SMB_QFILEINFO_STANDARD_INFORMATION +SMB_QFILEINFO_INTERNAL_INFORMATION +SMB_QFILEINFO_EA_INFORMATION +SMB_QFILEINFO_ACCESS_INFORMATION +SMB_QFILEINFO_NAME_INFORMATION +SMB_QFILEINFO_POSITION_INFORMATION +SMB_QFILEINFO_MODE_INFORMATION +SMB_QFILEINFO_ALIGNMENT_INFORMATION +SMB_QFILEINFO_ALL_INFORMATION +SMB_QFILEINFO_ALT_NAME_INFORMATION +SMB_QFILEINFO_STREAM_INFORMATION +SMB_QFILEINFO_COMPRESSION_INFORMATION +SMB_QFILEINFO_NETWORK_OPEN_INFORMATION +SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION +SMB_SFILEINFO_STANDARD +SMB_SFILEINFO_EA_SET +SMB_SFILEINFO_BASIC_INFO +SMB_SFILEINFO_DISPOSITION_INFO +SMB_SFILEINFO_ALLOCATION_INFO +SMB_SFILEINFO_END_OF_FILE_INFO +SMB_SFILEINFO_UNIX_BASIC +SMB_SFILEINFO_UNIX_LINK +SMB_SFILEINFO_BASIC_INFORMATION +SMB_SFILEINFO_RENAME_INFORMATION +SMB_SFILEINFO_DISPOSITION_INFORMATION +SMB_SFILEINFO_POSITION_INFORMATION +SMB_SFILEINFO_MODE_INFORMATION +SMB_SFILEINFO_ALLOCATION_INFORMATION +SMB_SFILEINFO_END_OF_FILE_INFORMATION +SMB_FIND_STANDARD +SMB_FIND_EA_SIZE +SMB_FIND_DIRECTORY_INFO +SMB_FIND_FULL_DIRECTORY_INFO +SMB_FIND_NAME_INFO +SMB_FIND_BOTH_DIRECTORY_INFO +SMB_FIND_261 +SMB_FIND_262 diff --git a/source4/torture/raw/mkdir.c b/source4/torture/raw/mkdir.c new file mode 100644 index 0000000..4775016 --- /dev/null +++ b/source4/torture/raw/mkdir.c @@ -0,0 +1,171 @@ +/* + Unix SMB/CIFS implementation. + RAW_MKDIR_* and RAW_RMDIR_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\mkdirtest" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +/* + test mkdir ops +*/ +static bool test_mkdir(struct smbcli_state *cli, struct torture_context *tctx) +{ + union smb_mkdir md; + struct smb_rmdir rd; + const char *path = BASEDIR "\\mkdir.dir"; + NTSTATUS status; + bool ret = true; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* + basic mkdir + */ + md.mkdir.level = RAW_MKDIR_MKDIR; + md.mkdir.in.path = path; + + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing mkdir collision\n"); + + /* 2nd create */ + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + /* basic rmdir */ + rd.in.path = path; + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + printf("Testing mkdir collision with file\n"); + + /* name collision with a file */ + smbcli_close(cli->tree, create_complex_file(cli, tctx, path)); + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + printf("Testing rmdir with file\n"); + + /* delete a file with rmdir */ + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + smbcli_unlink(cli->tree, path); + + printf("Testing invalid dir\n"); + + /* create an invalid dir */ + md.mkdir.in.path = "..\\..\\.."; + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + printf("Testing t2mkdir\n"); + + /* try a t2mkdir - need to work out why this fails! */ + md.t2mkdir.level = RAW_MKDIR_T2MKDIR; + md.t2mkdir.in.path = path; + md.t2mkdir.in.num_eas = 0; + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing t2mkdir bad path\n"); + md.t2mkdir.in.path = talloc_asprintf(tctx, "%s\\bad_path\\bad_path", + BASEDIR); + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND); + + printf("Testing t2mkdir with EAs\n"); + + /* with EAs */ + md.t2mkdir.level = RAW_MKDIR_T2MKDIR; + md.t2mkdir.in.path = path; + md.t2mkdir.in.num_eas = 3; + md.t2mkdir.in.eas = talloc_array(tctx, struct ea_struct, md.t2mkdir.in.num_eas); + md.t2mkdir.in.eas[0].flags = 0; + md.t2mkdir.in.eas[0].name.s = "EAONE"; + md.t2mkdir.in.eas[0].value = data_blob_talloc(tctx, "blah", 4); + md.t2mkdir.in.eas[1].flags = 0; + md.t2mkdir.in.eas[1].name.s = "EA TWO"; + md.t2mkdir.in.eas[1].value = data_blob_talloc(tctx, "foo bar", 7); + md.t2mkdir.in.eas[2].flags = 0; + md.t2mkdir.in.eas[2].name.s = "EATHREE"; + md.t2mkdir.in.eas[2].value = data_blob_talloc(tctx, "xx1", 3); + status = smb_raw_mkdir(cli->tree, &md); + + if (torture_setting_bool(tctx, "samba3", false) + && NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)) { + d_printf("EAS not supported -- not treating as fatal\n"); + } + else { + /* + * In Samba3, don't see this error as fatal + */ + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_check_ea(cli, path, "EAONE", "blah"); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_check_ea(cli, path, "EA TWO", "foo bar"); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_check_ea(cli, path, "EATHREE", "xx1"); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + basic testing of all RAW_MKDIR_* calls +*/ +bool torture_raw_mkdir(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + + if (!test_mkdir(cli, torture)) { + ret = false; + } + + return ret; +} diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c new file mode 100644 index 0000000..4fd5a9e --- /dev/null +++ b/source4/torture/raw/mux.c @@ -0,0 +1,342 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for multiplexing + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\test_mux" + +/* + test the delayed reply to a open that leads to a sharing violation +*/ +static bool test_mux_open(struct torture_context *tctx, struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + NTSTATUS status; + int fnum1, fnum2; + bool ret = true; + struct smbcli_request *req1, *req2; + struct timeval tv; + double d; + + torture_comment(tctx, "Testing multiplexed open/open/close\n"); + + torture_comment(tctx, "send first open\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR "\\open.dat"; + status = smb_raw_open(cli->tree, mem_ctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "send first open"); + fnum1 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "send 2nd open, non-conflicting\n"); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "send 2nd open, non-conflicting"); + fnum2 = io.ntcreatex.out.file.fnum; + + tv = timeval_current(); + + torture_comment(tctx, "send 3rd open, conflicting\n"); + io.ntcreatex.in.share_access = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, "send 3rd open, conflicting"); + + d = timeval_elapsed(&tv); + if (d < 0.5 || d > 1.5) { + torture_comment(tctx, "bad timeout for conflict - %.2f should be 1.0\n", d); + } else { + torture_comment(tctx, "open delay %.2f\n", d); + } + + torture_comment(tctx, "send async open, conflicting\n"); + tv = timeval_current(); + req1 = smb_raw_open_send(cli->tree, &io); + + torture_comment(tctx, "send 2nd async open, conflicting\n"); + tv = timeval_current(); + req2 = smb_raw_open_send(cli->tree, &io); + + torture_comment(tctx, "close first sync open\n"); + smbcli_close(cli->tree, fnum1); + + torture_comment(tctx, "cancel 2nd async open (should be ignored)\n"); + smb_raw_ntcancel(req2); + + d = timeval_elapsed(&tv); + if (d > 0.25) { + torture_comment(tctx, "bad timeout after cancel - %.2f should be <0.25\n", d); + torture_assert(tctx, d <= 0.25, "bad timeout after cancel"); + } + + torture_comment(tctx, "close the 2nd sync open\n"); + smbcli_close(cli->tree, fnum2); + + torture_comment(tctx, "see if the 1st async open now succeeded\n"); + status = smb_raw_open_recv(req1, mem_ctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "see if the 1st async open now succeeded"); + + d = timeval_elapsed(&tv); + if (d > 0.25) { + torture_comment(tctx, "bad timeout for async conflict - %.2f should be <0.25\n", d); + torture_assert(tctx, d <= 0.25, "bad timeout for async conflict"); + } else { + torture_comment(tctx, "async open delay %.2f\n", d); + } + + torture_comment(tctx, "2nd async open should have timed out\n"); + status = smb_raw_open_recv(req2, mem_ctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, "2nd async open should have timed out"); + d = timeval_elapsed(&tv); + if (d < 0.8) { + torture_comment(tctx, "bad timeout for async conflict - %.2f should be 1.0\n", d); + } + + torture_comment(tctx, "close the 1st async open\n"); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + return ret; +} + + +/* + test a write that hits a byte range lock and send the close after the write +*/ +static bool test_mux_write(struct torture_context *tctx, struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + int fnum; + bool ret = true; + struct smbcli_request *req; + + torture_comment(tctx, "Testing multiplexed lock/write/close\n"); + + fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_comment(tctx, "open failed in mux_write - %s\n", smbcli_errstr(cli->tree)); + torture_assert(tctx, fnum != -1, "open failed in mux_write"); + } + + cli->session->pid = 1; + + status = smbcli_lock(cli->tree, fnum, 0, 4, 0, WRITE_LOCK); + + /* lock a range */ + if (NT_STATUS_IS_ERR(status)) { + torture_assert_ntstatus_ok(tctx, status, "lock failed in mux_write"); + } + + cli->session->pid = 2; + + /* send an async write */ + io.generic.level = RAW_WRITE_WRITEX; + io.writex.in.file.fnum = fnum; + io.writex.in.offset = 0; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.count = 4; + io.writex.in.data = (const uint8_t *)&fnum; + req = smb_raw_write_send(cli->tree, &io); + + /* unlock the range */ + cli->session->pid = 1; + smbcli_unlock(cli->tree, fnum, 0, 4); + + /* and recv the async write reply */ + status = smb_raw_write_recv(req, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_LOCK_CONFLICT, "recv the async write reply"); + + smbcli_close(cli->tree, fnum); + + return ret; +} + + +/* + test a lock that conflicts with an existing lock +*/ +static bool test_mux_lock(struct torture_context *tctx, struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + int fnum; + bool ret = true; + struct smbcli_request *req; + struct smb_lock_entry lock[1]; + struct timeval t; + + torture_comment(tctx, "TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n"); + + fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_comment(tctx, "open failed in mux_lock - %s\n", smbcli_errstr(cli->tree)); + torture_assert(tctx, fnum != -1, "open failed in mux_lock"); + } + + torture_comment(tctx, "establishing a lock\n"); + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.ulock_cnt = 0; + lock[0].pid = 1; + lock[0].offset = 0; + lock[0].count = 4; + io.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "establishing a lock"); + + torture_comment(tctx, "the second lock will conflict with the first\n"); + lock[0].pid = 2; + io.lockx.in.timeout = 1000; + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_LOCK_CONFLICT, "the second lock will conflict with the first"); + + torture_comment(tctx, "this will too, but we'll unlock while waiting\n"); + t = timeval_current(); + req = smb_raw_lock_send(cli->tree, &io); + + torture_comment(tctx, "unlock the first range\n"); + lock[0].pid = 1; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "unlock the first range"); + + torture_comment(tctx, "recv the async reply\n"); + status = smbcli_request_simple_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "recv the async reply"); + + torture_comment(tctx, "async lock took %.2f msec\n", timeval_elapsed(&t) * 1000); + torture_assert(tctx, timeval_elapsed(&t) <= 0.1, "failed to trigger early lock retry\n"); + + torture_comment(tctx, "reopening with an exit\n"); + smb_raw_exit(cli->session); + fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + + torture_comment(tctx, "Now trying with a cancel\n"); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.file.fnum = fnum; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.ulock_cnt = 0; + lock[0].pid = 1; + lock[0].offset = 0; + lock[0].count = 4; + io.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "Now trying with a cancel"); + + lock[0].pid = 2; + io.lockx.in.timeout = 1000; + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_LOCK_CONFLICT, "Now trying with a cancel pid 2"); + + req = smb_raw_lock_send(cli->tree, &io); + + /* cancel the blocking lock */ + smb_raw_ntcancel(req); + + torture_comment(tctx, "sending 2nd cancel\n"); + /* the 2nd cancel is totally harmless, but tests the server trying to + cancel an already cancelled request */ + smb_raw_ntcancel(req); + + torture_comment(tctx, "sent 2nd cancel\n"); + + lock[0].pid = 1; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "clear lock"); + + status = smbcli_request_simple_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_LOCK_CONFLICT, "recv 2nd cancel"); + + torture_comment(tctx, "cancel a lock using exit to close file\n"); + lock[0].pid = 1; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.timeout = 1000; + + status = smb_raw_lock(cli->tree, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, "cancel a lock using exit to close file"); + + t = timeval_current(); + lock[0].pid = 2; + req = smb_raw_lock_send(cli->tree, &io); + + smb_raw_exit(cli->session); + smb_raw_exit(cli->session); + smb_raw_exit(cli->session); + smb_raw_exit(cli->session); + + torture_comment(tctx, "recv the async reply\n"); + status = smbcli_request_simple_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RANGE_NOT_LOCKED, "recv the async reply"); + torture_comment(tctx, "async lock exit took %.2f msec\n", timeval_elapsed(&t) * 1000); + torture_assert(tctx, timeval_elapsed(&t) <= 0.1, "failed to trigger early lock failure\n"); + + return ret; +} + + + +/* + basic testing of multiplexing notify +*/ +bool torture_raw_mux(struct torture_context *torture, struct smbcli_state *cli) +{ + bool ret = true; + TALLOC_CTX *frame; + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + frame = talloc_stackframe(); + + ret &= test_mux_open(torture, cli, frame); + ret &= test_mux_write(torture, cli, frame); + ret &= test_mux_lock(torture, cli, frame); + + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + TALLOC_FREE(frame); + return ret; +} diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c new file mode 100644 index 0000000..f3c3806 --- /dev/null +++ b/source4/torture/raw/notify.c @@ -0,0 +1,2297 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for change notify + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "system/filesys.h" +#include "torture/util.h" +#include "torture/raw/proto.h" +#include "lib/events/events.h" + +#define BASEDIR "\\test_notify" + +#define CHECK_WSTR(tctx, field, value, flags) \ +do { \ + torture_assert_str_equal(tctx, field.s, value, "values don't match"); \ + torture_assert(tctx, \ + !wire_bad_flags(&field, STR_UNICODE, cli->transport), \ + "wire_bad_flags"); \ +} while (0) + +#define BASEDIR_CN1_DIR BASEDIR "_CN1_DIR" + +/* + basic testing of change notify on directories +*/ +static bool test_notify_dir(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + union smb_close cl; + int i, count, fnum, fnum2; + struct smbcli_request *req, *req2; + extern int torture_numops; + + torture_comment(tctx, "TESTING CHANGE NOTIFY ON DIRECTORIES\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_DIR), + "Failed to setup up test directory: " BASEDIR_CN1_DIR); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_DIR; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum2 = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + torture_comment(tctx, "Testing notify cancel\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smb_raw_ntcancel(req); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + torture_comment(tctx, "Testing notify mkdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_mkdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "more than one change"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "Testing notify rmdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_rmdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "more than one change"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "Testing notify mkdir - rmdir - mkdir - rmdir\n"); + + smbcli_mkdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + smbcli_rmdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + smbcli_mkdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + smbcli_rmdir(cli2->tree, BASEDIR_CN1_DIR "\\subdir-name"); + smb_msleep(200); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 4, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[1].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[1].name, "subdir-name", + STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[2].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[2].name, "subdir-name", + STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[3].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[3].name, "subdir-name", + STR_UNICODE); + + count = torture_numops; + torture_comment(tctx, "Testing buffered notify on create of %d files\n", count); + for (i=0;itree, fname, O_CREAT|O_RDWR, DENY_NONE); + torture_assert_int_not_equal_goto(tctx, fnum3, -1, ret, done, + talloc_asprintf(tctx, "Failed to create %s - %s", + fname, smbcli_errstr(cli->tree))); + talloc_free(fname); + smbcli_close(cli->tree, fnum3); + } + + /* (1st notify) setup a new notify on a different directory handle. + This new notify won't see the events above. */ + notify.nttrans.in.file.fnum = fnum2; + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* (2nd notify) whereas this notify will see the above buffered events, + and it directly returns the buffered events */ + notify.nttrans.in.file.fnum = fnum; + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + status = smbcli_unlink(cli->tree, BASEDIR_CN1_DIR "\\nonexistent.txt"); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smbcli_unlink"); + + /* (1st unlink) as the 2nd notify directly returns, + this unlink is only seen by the 1st notify and + the 3rd notify (later) */ + torture_comment(tctx, "Testing notify on unlink for the first file\n"); + status = smbcli_unlink(cli2->tree, BASEDIR_CN1_DIR "\\test0.txt"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smbcli_unlink"); + + /* receive the reply from the 2nd notify */ + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + count, ret, done, + "wrong number of changes"); + for (i=1;itree, ¬ify); + + status = smbcli_unlink(cli->tree, BASEDIR_CN1_DIR "\\nonexistent.txt"); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smbcli_unlink"); + + torture_comment(tctx, "Testing notify on wildcard unlink for %d files\n", count-1); + /* (2nd unlink) do a wildcard unlink */ + status = smbcli_unlink_wcard(cli2->tree, BASEDIR_CN1_DIR "\\test*.txt"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + /* receive the 3rd notify */ + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "test0.txt", + STR_UNICODE); + + /* and we now see the rest of the unlink calls on both directory handles */ + notify.nttrans.in.file.fnum = fnum; + sleep(3); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + count - 1, ret, done, + "wrong number of changes"); + for (i=0;itree, ¬ify); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + count - 1, ret, done, + "wrong number of changes"); + for (i=0;itree, ¬ify); + + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_close"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 0, ret, done, "no changes expected"); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_DIR); + return ret; +} + +/* + * Check notify reply for a rename action. Not sure if this is a valid thing + * to do, but depending on timing between inotify and messaging we get the + * add/remove/modify in any order. This routines tries to find the action/name + * pair in any of the three following notify_changes. + */ + +static bool check_rename_reply(struct torture_context *tctx, + struct smbcli_state *cli, + int line, + struct notify_changes *actions, + uint32_t action, const char *name) +{ + int i; + + for (i=0; i<3; i++) { + if (actions[i].action == action) { + CHECK_WSTR(tctx, actions[i].name, name, STR_UNICODE); + return true; + } + } + + torture_result(tctx, TORTURE_FAIL, + __location__": (%d) expected action %d, not found\n", + line, action); + return false; +} + +/* + testing of recursive change notify +*/ + +#define BASEDIR_CN1_RECUR BASEDIR "_CN1_RECUR" + +static bool test_notify_recursive(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req1, *req2; + + torture_comment(tctx, "TESTING CHANGE NOTIFY WITH RECURSION\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_RECUR), + "Failed to setup up test directory: " BASEDIR_CN1_RECUR); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_RECUR; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, on file or directory name + changes. Setup both with and without recursion */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION; + notify.nttrans.in.file.fnum = fnum; + + notify.nttrans.in.recursive = true; + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + notify.nttrans.in.recursive = false; + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* cancel initial requests so the buffer is setup */ + smb_raw_ntcancel(req1); + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + smb_raw_ntcancel(req2); + status = smb_raw_changenotify_recv(req2, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + /* + * Make notifies a bit more interesting in a cluster by doing + * the changes against different nodes with --unclist + */ + smbcli_mkdir(cli->tree, BASEDIR_CN1_RECUR "\\subdir-name"); + smbcli_mkdir(cli2->tree, BASEDIR_CN1_RECUR "\\subdir-name\\subname1"); + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_RECUR "\\subdir-name\\subname2", + O_CREAT, 0)); + smbcli_rename(cli2->tree, BASEDIR_CN1_RECUR "\\subdir-name\\subname1", + BASEDIR_CN1_RECUR "\\subdir-name\\subname1-r"); + smbcli_rename(cli->tree, + BASEDIR_CN1_RECUR "\\subdir-name\\subname2", + BASEDIR_CN1_RECUR "\\subname2-r"); + smbcli_rename(cli2->tree, BASEDIR_CN1_RECUR "\\subname2-r", + BASEDIR_CN1_RECUR "\\subname3-r"); + + notify.nttrans.in.completion_filter = 0; + notify.nttrans.in.recursive = true; + smb_msleep(200); + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + smbcli_rmdir(cli->tree, BASEDIR_CN1_RECUR "\\subdir-name\\subname1-r"); + smbcli_rmdir(cli2->tree, BASEDIR_CN1_RECUR "\\subdir-name"); + smbcli_unlink(cli->tree, BASEDIR_CN1_RECUR "\\subname3-r"); + + smb_msleep(200); + notify.nttrans.in.recursive = false; + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 11, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[1].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[1].name, + "subdir-name\\subname1", STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[2].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[2].name, + "subdir-name\\subname2", STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[3].action, + NOTIFY_ACTION_OLD_NAME, ret, done, + "wrong action (exp: OLD_NAME)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[3].name, + "subdir-name\\subname1", STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[4].action, + NOTIFY_ACTION_NEW_NAME, ret, done, + "wrong action (exp: NEW_NAME)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[4].name, + "subdir-name\\subname1-r", STR_UNICODE); + + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[5], + NOTIFY_ACTION_ADDED, "subname2-r"); + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[5], + NOTIFY_ACTION_REMOVED, "subdir-name\\subname2"); + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[5], + NOTIFY_ACTION_MODIFIED, "subname2-r"); + + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[8], + NOTIFY_ACTION_OLD_NAME, "subname2-r"); + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[8], + NOTIFY_ACTION_NEW_NAME, "subname3-r"); + ret &= check_rename_reply(tctx, + cli, __LINE__, ¬ify.nttrans.out.changes[8], + NOTIFY_ACTION_MODIFIED, "subname3-r"); + + if (!ret) { + goto done; + } + + status = smb_raw_changenotify_recv(req2, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 3, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, + "subdir-name\\subname1-r", STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[1].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[1].name, "subdir-name", + STR_UNICODE); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[2].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[2].name, "subname3-r", + STR_UNICODE); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_RECUR); + return ret; +} + +/* + testing of change notify mask change +*/ + +#define BASEDIR_CN1_CNMC BASEDIR "_CN1_CNMC" + +static bool test_notify_mask_change(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req1, *req2; + + torture_comment(tctx, "TESTING CHANGE NOTIFY WITH MASK CHANGE\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_CNMC), + "Failed to setup up test directory: " BASEDIR_CN1_CNMC); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_CNMC; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, on file or directory name + changes. Setup both with and without recursion */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES; + notify.nttrans.in.file.fnum = fnum; + + notify.nttrans.in.recursive = true; + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + notify.nttrans.in.recursive = false; + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* cancel initial requests so the buffer is setup */ + smb_raw_ntcancel(req1); + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + smb_raw_ntcancel(req2); + status = smb_raw_changenotify_recv(req2, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + notify.nttrans.in.recursive = true; + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* Set to hidden then back again. */ + smbcli_close(cli->tree, + smbcli_open(cli->tree,BASEDIR_CN1_CNMC "\\tname1", O_CREAT, 0)); + smbcli_setatr(cli->tree, BASEDIR_CN1_CNMC "\\tname1", + FILE_ATTRIBUTE_HIDDEN, 0); + smbcli_unlink(cli->tree, BASEDIR_CN1_CNMC "\\tname1"); + + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_MODIFIED, ret, done, + "wrong action (exp: MODIFIED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "tname1", + STR_UNICODE); + + /* Now try and change the mask to include other events. + * This should not work - once the mask is set on a directory + * fnum it seems to be fixed until the fnum is closed. */ + + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION; + notify.nttrans.in.recursive = true; + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + notify.nttrans.in.recursive = false; + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + smbcli_mkdir(cli->tree, BASEDIR_CN1_CNMC "\\subdir-name"); + smbcli_mkdir(cli->tree, BASEDIR_CN1_CNMC "\\subdir-name\\subname1"); + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_CNMC "\\subdir-name\\subname2", + O_CREAT, 0)); + smbcli_rename(cli->tree, + BASEDIR_CN1_CNMC "\\subdir-name\\subname1", + BASEDIR_CN1_CNMC "\\subdir-name\\subname1-r"); + smbcli_rename(cli->tree, + BASEDIR_CN1_CNMC "\\subdir-name\\subname2", + BASEDIR_CN1_CNMC "\\subname2-r"); + smbcli_rename(cli->tree, + BASEDIR_CN1_CNMC "\\subname2-r", + BASEDIR_CN1_CNMC "\\subname3-r"); + + smbcli_rmdir(cli->tree, BASEDIR_CN1_CNMC "\\subdir-name\\subname1-r"); + smbcli_rmdir(cli->tree, BASEDIR_CN1_CNMC "\\subdir-name"); + smbcli_unlink(cli->tree, BASEDIR_CN1_CNMC "\\subname3-r"); + + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_MODIFIED, ret, done, + "wrong action (exp: MODIFIED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subname2-r", + STR_UNICODE); + + status = smb_raw_changenotify_recv(req2, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_MODIFIED, ret, done, + "wrong action (exp: MODIFIED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subname3-r", + STR_UNICODE); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_CNMC); + return ret; +} + + +/* + testing of mask bits for change notify +*/ + +#define BASEDIR_CN1_NOTM BASEDIR "_CN1_NOTM" + +static bool test_notify_mask(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + union smb_chkpath chkpath; + int fnum, fnum2; + uint32_t mask; + int i; + char c = 1; + struct timeval tv; + NTTIME t; + + torture_comment(tctx, "TESTING CHANGE NOTIFY COMPLETION FILTERS\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NOTM), + "Failed to setup up test directory: " BASEDIR_CN1_NOTM); + + tv = timeval_current_ofs(1000, 0); + t = timeval_to_nttime(&tv); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_NOTM; + + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.recursive = true; + + chkpath.chkpath.in.path = "\\"; + +#define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, expected, nchanges) \ + do { \ + smbcli_getatr(cli->tree, test_name, NULL, NULL, NULL); \ + for (mask=i=0;i<32;i++) { \ + struct smbcli_request *req; \ + status = smb_raw_open(cli->tree, tctx, &io); \ + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, \ + "smb_raw_open"); \ + fnum = io.ntcreatex.out.file.fnum; \ + setup \ + notify.nttrans.in.file.fnum = fnum; \ + notify.nttrans.in.completion_filter = ((uint32_t)1<tree, ¬ify); \ + smb_raw_chkpath(cli->tree, &chkpath); \ + op \ + smb_msleep(200); smb_raw_ntcancel(req); \ + status = smb_raw_changenotify_recv(req, tctx, ¬ify); \ + cleanup \ + smbcli_close(cli->tree, fnum); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \ + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, \ + "smbcli_close"); \ + /* special case to cope with file rename behaviour */ \ + if (nchanges == 2 && notify.nttrans.out.num_changes == 1 && \ + notify.nttrans.out.changes[0].action == NOTIFY_ACTION_MODIFIED && \ + ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \ + Action == NOTIFY_ACTION_OLD_NAME) { \ + torture_comment(tctx, "(rename file special handling OK)\n"); \ + } else { \ + torture_assert_int_equal_goto(tctx, \ + notify.nttrans.out.num_changes,\ + nchanges, ret, done, \ + talloc_asprintf(tctx, \ + "nchanges=%d expected=%d action=%d " \ + "filter=0x%08x\n", \ + notify.nttrans.out.num_changes, \ + nchanges, \ + notify.nttrans.out.changes[0].action, \ + notify.nttrans.in.completion_filter)); \ + torture_assert_int_equal_goto(tctx, \ + notify.nttrans.out.changes[0].action, \ + Action, ret, done, \ + talloc_asprintf(tctx, \ + "nchanges=%d action=%d " \ + "expectedAction=%d filter=0x%08x\n", \ + notify.nttrans.out.num_changes, \ + notify.nttrans.out.changes[0].action, \ + Action, \ + notify.nttrans.in.completion_filter)); \ + torture_assert_str_equal_goto(tctx, \ + notify.nttrans.out.changes[0].name.s, \ + "tname1", ret, done, \ + talloc_asprintf(tctx, \ + "nchanges=%d action=%d filter=0x%08x " \ + "name=%s expected_name=tname1\n", \ + notify.nttrans.out.num_changes, \ + notify.nttrans.out.changes[0].action, \ + notify.nttrans.in.completion_filter, \ + notify.nttrans.out.changes[0].name.s));\ + } \ + mask |= ((uint32_t)1<tree, BASEDIR_CN1_NOTM "\\tname1");, + smbcli_rmdir(cli2->tree, BASEDIR_CN1_NOTM "\\tname1");, + NOTIFY_ACTION_ADDED, + FILE_NOTIFY_CHANGE_DIR_NAME, 1); + + torture_comment(tctx, "Testing create file\n"); + NOTIFY_MASK_TEST("Testing create file",;, + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_NOTM "\\tname1", + O_CREAT, 0));, + smbcli_unlink(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1");, + NOTIFY_ACTION_ADDED, + FILE_NOTIFY_CHANGE_FILE_NAME, 1); + + torture_comment(tctx, "Testing unlink\n"); + NOTIFY_MASK_TEST("Testing unlink", + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_NOTM "\\tname1", + O_CREAT, 0));, + smbcli_unlink(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1");, + ;, + NOTIFY_ACTION_REMOVED, + FILE_NOTIFY_CHANGE_FILE_NAME, 1); + + torture_comment(tctx, "Testing rmdir\n"); + NOTIFY_MASK_TEST("Testing rmdir", + smbcli_mkdir(cli->tree, BASEDIR_CN1_NOTM "\\tname1");, + smbcli_rmdir(cli2->tree, BASEDIR_CN1_NOTM "\\tname1");, + ;, + NOTIFY_ACTION_REMOVED, + FILE_NOTIFY_CHANGE_DIR_NAME, 1); + + torture_comment(tctx, "Testing rename file\n"); + NOTIFY_MASK_TEST("Testing rename file", + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_NOTM "\\tname1", + O_CREAT, 0));, + smbcli_rename(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1", + BASEDIR_CN1_NOTM "\\tname2");, + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname2");, + NOTIFY_ACTION_OLD_NAME, + FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2); + + torture_comment(tctx, "Testing rename dir\n"); + NOTIFY_MASK_TEST("Testing rename dir", + smbcli_mkdir(cli->tree, BASEDIR_CN1_NOTM "\\tname1");, + smbcli_rename(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1", + BASEDIR_CN1_NOTM "\\tname2");, + smbcli_rmdir(cli->tree, BASEDIR_CN1_NOTM "\\tname2");, + NOTIFY_ACTION_OLD_NAME, + FILE_NOTIFY_CHANGE_DIR_NAME, 2); + + torture_comment(tctx, "Testing set path attribute\n"); + NOTIFY_MASK_TEST("Testing set path attribute", + smbcli_close(cli->tree, + smbcli_open(cli->tree, + BASEDIR_CN1_NOTM "\\tname1", O_CREAT, 0));, + smbcli_setatr(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);, + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1");, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_ATTRIBUTES, 1); + + torture_comment(tctx, "Testing set path write time\n"); + NOTIFY_MASK_TEST("Testing set path write time", + smbcli_close(cli->tree, smbcli_open(cli->tree, + BASEDIR_CN1_NOTM "\\tname1", O_CREAT, 0));, + smbcli_setatr(cli2->tree, + BASEDIR_CN1_NOTM "\\tname1", + FILE_ATTRIBUTE_NORMAL, 1000);, + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1");, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_LAST_WRITE, 1); + + torture_comment(tctx, "Testing set file attribute\n"); + NOTIFY_MASK_TEST("Testing set file attribute", + fnum2 = create_complex_file(cli2, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_fsetatr(cli2->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);, + (smbcli_close(cli2->tree, fnum2), + smbcli_unlink(cli2->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_ATTRIBUTES, 1); + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_comment(tctx, "Samba3 does not yet support create times " + "everywhere\n"); + } + else { + torture_comment(tctx, "Testing set file create time\n"); + NOTIFY_MASK_TEST("Testing set file create time", + fnum2 = create_complex_file(cli, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);, + (smbcli_close(cli->tree, fnum2), + smbcli_unlink(cli->tree, + BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_CREATION, 1); + } + + torture_comment(tctx, "Testing set file access time\n"); + NOTIFY_MASK_TEST("Testing set file access time", + fnum2 = create_complex_file(cli, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);, + (smbcli_close(cli->tree, fnum2), + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_LAST_ACCESS, 1); + + torture_comment(tctx, "Testing set file write time\n"); + NOTIFY_MASK_TEST("Testing set file write time", + fnum2 = create_complex_file(cli, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);, + (smbcli_close(cli->tree, fnum2), + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_LAST_WRITE, 1); + + torture_comment(tctx, "Testing set file change time\n"); + NOTIFY_MASK_TEST("Testing set file change time", + fnum2 = create_complex_file(cli, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);, + (smbcli_close(cli->tree, fnum2), + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + 0, 1); + + + torture_comment(tctx, "Testing write\n"); + NOTIFY_MASK_TEST("Testing write", + fnum2 = create_complex_file(cli2, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_write(cli2->tree, fnum2, 1, &c, 10000, 1);, + (smbcli_close(cli2->tree, fnum2), + smbcli_unlink(cli->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + 0, 1); + + torture_comment(tctx, "Testing truncate\n"); + NOTIFY_MASK_TEST("Testing truncate", + fnum2 = create_complex_file(cli2, tctx, + BASEDIR_CN1_NOTM "\\tname1");, + smbcli_ftruncate(cli2->tree, fnum2, 10000);, + (smbcli_close(cli2->tree, fnum2), + smbcli_unlink(cli2->tree, BASEDIR_CN1_NOTM "\\tname1"));, + NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES, 1); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_NOTM); + return ret; +} + +/* + basic testing of change notify on files +*/ + +#define BASEDIR_CN1_FILE BASEDIR "_CN1_FILE" + +static bool test_notify_file(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_close cl; + union smb_notify notify; + struct smbcli_request *req; + int fnum; + const char *fname = BASEDIR_CN1_FILE "\\file.txt"; + + torture_comment(tctx, "TESTING CHANGE NOTIFY ON FILES\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_FILE), + "Failed to setup up test directory: " BASEDIR_CN1_FILE); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME; + notify.nttrans.in.recursive = false; + + torture_comment(tctx, "Testing if notifies on file handles are invalid (should be)\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_INVALID_PARAMETER, + ret, done, + "smb_raw_changenotify_recv"); + + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_close"); + + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smbcli_unlink"); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_FILE); + return ret; +} + +/* + basic testing of change notifies followed by a tdis +*/ +#define BASEDIR_CN1_TDIS BASEDIR "_CN1_TDIS" + +static bool test_notify_tdis(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req; + struct smbcli_state *cli = NULL; + + torture_comment(tctx, "TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n"); + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR_CN1_TDIS), + "Failed to setup up test directory: " BASEDIR_CN1_TDIS); + + torture_assert(tctx, torture_open_connection(&cli, tctx, 0), + "Failed to open connection."); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_TDIS; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + status = smbcli_tdis(cli); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smbcli_tdis"); + cli->tree = NULL; + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 0, ret, done, "no changes expected"); + +done: + torture_close_connection(cli); + smbcli_deltree(cli1->tree, BASEDIR_CN1_TDIS); + return ret; +} + +/* + basic testing of change notifies followed by a exit +*/ + +#define BASEDIR_CN1_EX BASEDIR "_CN1_EX" + +static bool test_notify_exit(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req; + struct smbcli_state *cli = NULL; + + torture_comment(tctx, "TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n"); + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR_CN1_EX), + "Failed to setup up test directory: " BASEDIR_CN1_EX); + + torture_assert(tctx, torture_open_connection(&cli, tctx, 0), + "Failed to open connection."); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_EX; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + status = smb_raw_exit(cli->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_exit"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 0, ret, done, "no changes expected"); + +done: + torture_close_connection(cli); + smbcli_deltree(cli1->tree, BASEDIR_CN1_EX); + return ret; +} + +/* + basic testing of change notifies followed by a ulogoff +*/ + +#define BASEDIR_CN1_UL BASEDIR "_CN1_UL" + +static bool test_notify_ulogoff(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req; + struct smbcli_state *cli = NULL; + + torture_comment(tctx, "TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n"); + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR_CN1_UL), + "Failed to setup up test directory: " BASEDIR_CN1_UL); + + torture_assert(tctx, torture_open_connection(&cli, tctx, 0), + "Failed to open connection."); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_UL; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + status = smb_raw_ulogoff(cli->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_ulogoff"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 0, ret, done, "no changes expected"); + +done: + torture_close_connection(cli); + smbcli_deltree(cli1->tree, BASEDIR_CN1_UL); + return ret; +} + +static void tcp_dis_handler(struct smbcli_transport *t, void *p) +{ + struct smbcli_state *cli = (struct smbcli_state *)p; + smbcli_transport_dead(cli->transport, NT_STATUS_LOCAL_DISCONNECT); + cli->transport = NULL; + cli->tree = NULL; +} +/* + basic testing of change notifies followed by tcp disconnect +*/ + +#define BASEDIR_CN1_TCPDIS BASEDIR "_CN1_TCPDIS" + +static bool test_notify_tcp_dis(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req; + struct smbcli_state *cli = NULL; + + torture_comment(tctx, "TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n"); + + torture_assert(tctx, torture_setup_dir(cli1, BASEDIR_CN1_TCPDIS), + "Failed to setup up test directory: " + BASEDIR_CN1_TCPDIS); + + torture_assert(tctx, torture_open_connection(&cli, tctx, 0), + "Failed to open connection."); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_TCPDIS; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250000, cli); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_LOCAL_DISCONNECT, + ret, done, + "smb_raw_changenotify_recv"); + +done: + torture_close_connection(cli); + smbcli_deltree(cli1->tree, BASEDIR_CN1_TCPDIS); + return ret; +} + +/* + test setting up two change notify requests on one handle +*/ + +#define BASEDIR_CN1_DBL BASEDIR "_CN1_DBL" + +static bool test_notify_double(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req1, *req2; + + torture_comment(tctx, "TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_DBL), + "Failed to setup up test directory: " BASEDIR_CN1_DBL); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_DBL; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + req2 = smb_raw_changenotify_send(cli->tree, ¬ify); + + smbcli_mkdir(cli->tree, BASEDIR_CN1_DBL "\\subdir-name"); + + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + smbcli_mkdir(cli->tree, BASEDIR_CN1_DBL "\\subdir-name2"); + + status = smb_raw_changenotify_recv(req2, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name2", + STR_UNICODE); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_DBL); + return ret; +} + + +/* + test multiple change notifies at different depths and with/without recursion +*/ + +#define BASEDIR_CN1_TNT BASEDIR "_CN1_TNT" + +static bool test_notify_tree(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + bool ret = true; + union smb_notify notify; + union smb_open io; + struct smbcli_request *req; + struct timeval tv; + struct { + const char *path; + bool recursive; + uint32_t filter; + int expected; + int fnum; + int counted; + } dirs[] = { + { + .path = BASEDIR_CN1_TNT "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 30, + }, + { + .path = BASEDIR_CN1_TNT "\\zqy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 8, + }, + { + .path = BASEDIR_CN1_TNT "\\atsy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 4, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\foo", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\blah", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 13, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\blah", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 7, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\blah\\a", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\blah\\b", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\blah\\c", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\abc\\fooblah", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\zqy\\xx", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\zqy\\yyy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_CN1_TNT "\\zqy\\..", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 40, + }, + { + .path = BASEDIR_CN1_TNT, + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 40, + }, + { + .path = BASEDIR_CN1_TNT, + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 6, + }, + { + .path = BASEDIR_CN1_TNT "\\atsy", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 4, + }, + { + .path = BASEDIR_CN1_TNT "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 24, + }, + { + .path = BASEDIR_CN1_TNT "\\abc", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_FILE_NAME, + .expected = 0, + }, + { + .path = BASEDIR_CN1_TNT "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_FILE_NAME, + .expected = 0, + }, + { + .path = BASEDIR_CN1_TNT "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 24, + }, + }; + int i; + NTSTATUS status; + bool all_done = false; + + torture_comment(tctx, "TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_TNT), + "Failed to setup up test directory: " BASEDIR_CN1_TNT); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 20000; + + /* + setup the directory tree, and the notify buffer on each directory + */ + for (i=0;itree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + dirs[i].fnum = io.ntcreatex.out.file.fnum; + + notify.nttrans.in.completion_filter = dirs[i].filter; + notify.nttrans.in.file.fnum = dirs[i].fnum; + notify.nttrans.in.recursive = dirs[i].recursive; + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smb_raw_ntcancel(req); + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + } + + /* trigger 2 events in each dir */ + for (i=0;itree, path); + smbcli_rmdir(cli2->tree, path); + talloc_free(path); + } + + /* give a bit of time for the events to propagate */ + tv = timeval_current(); + + do { + /* count events that have happened in each dir */ + for (i=0;itree, ¬ify); + smb_raw_ntcancel(req); + notify.nttrans.out.num_changes = 0; + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + dirs[i].counted += notify.nttrans.out.num_changes; + } + + all_done = true; + + for (i=0;i=0;i--) { + smbcli_close(cli->tree, dirs[i].fnum); + smbcli_rmdir(cli->tree, dirs[i].path); + } + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_TNT); + return ret; +} + +/* + Test response when cached server events exceed single NT NOTFIY response + packet size. +*/ + +#define BASEDIR_CN1_NO BASEDIR "_CN1_NO" + +static bool test_notify_overflow(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + int count = 100; + struct smbcli_request *req1; + int i; + + torture_comment(tctx, "TESTING CHANGE NOTIFY EVENT OVERFLOW\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NO), + "Failed to setup up test directory: " BASEDIR_CN1_NO); + + /* get a handle on the directory */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_NO; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, on name changes. */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + + notify.nttrans.in.recursive = true; + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* cancel initial requests so the buffer is setup */ + smb_raw_ntcancel(req1); + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, + "smb_raw_changenotify_recv"); + + /* open a lot of files, filling up the server side notify buffer */ + torture_comment(tctx, "Testing overflowed buffer notify on create of %d files\n", + count); + for (i=0;itree, fname, O_CREAT|O_RDWR, + DENY_NONE); + torture_assert_int_not_equal_goto(tctx, fnum2, -1, ret, done, + talloc_asprintf(tctx, "Failed to create %s - %s", + fname, smbcli_errstr(cli->tree))); + talloc_free(fname); + smbcli_close(cli->tree, fnum2); + } + + /* expect that 0 events will be returned with NT_STATUS_OK */ + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 0, ret, done, "no changes expected"); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_NO); + return ret; +} + +/* + Test if notifications are returned for changes to the base directory. + They shouldn't be. +*/ + +#define BASEDIR_CN1_NBASE BASEDIR "_CN1_NBASE" + +static bool test_notify_basedir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req1; + + torture_comment(tctx, "TESTING CHANGE NOTIFY BASEDIR EVENTS\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NBASE), + "Failed to setup up test directory: " BASEDIR_CN1_NBASE); + + /* get a handle on the directory */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_NBASE; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* create a test file that will also be modified */ + smbcli_close(cli->tree, smbcli_open(cli->tree, + BASEDIR_CN1_NBASE "\\tname1", + O_CREAT, 0)); + + /* ask for a change notify, on attribute changes. */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + req1 = smb_raw_changenotify_send(cli->tree, ¬ify); + + /* set attribute on the base dir */ + smbcli_setatr(cli->tree, BASEDIR_CN1_NBASE, FILE_ATTRIBUTE_HIDDEN, 0); + + /* set attribute on a file to assure we receive a notification */ + smbcli_setatr(cli->tree, BASEDIR_CN1_NBASE "\\tname1", + FILE_ATTRIBUTE_HIDDEN, 0); + smb_msleep(200); + + /* check how many responses were given, expect only 1 for the file */ + status = smb_raw_changenotify_recv(req1, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_MODIFIED, ret, done, + "wrong action (exp: MODIFIED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "tname1", + STR_UNICODE); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_NBASE); + return ret; +} + + +/* + create a secondary tree connect - used to test for a bug in Samba3 messaging + with change notify +*/ + +static struct smbcli_tree *secondary_tcon(struct smbcli_state *cli, + struct torture_context *tctx) +{ + NTSTATUS status; + const char *share, *host; + struct smbcli_tree *tree; + union smb_tcon tcon; + + share = torture_setting_string(tctx, "share", NULL); + host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "create a second tree context on the same session\n"); + tree = smbcli_tree_init(cli->session, tctx, false); + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + tcon.tconx.in.device = "A:"; + status = smb_raw_tcon(tree, tctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tree); + torture_comment(tctx, "Failed to create secondary tree\n"); + return NULL; + } + + tree->tid = tcon.tconx.out.tid; + torture_comment(tctx, "tid1=%d tid2=%d\n", cli->tree->tid, tree->tid); + + return tree; +} + + +/* + very simple change notify test +*/ + +#define BASEDIR_CN1_NTCON BASEDIR "_CN1_NTCON" + +static bool test_notify_tcon(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum; + struct smbcli_request *req; + extern int torture_numops; + struct smbcli_tree *tree = NULL; + + torture_comment(tctx, "TESTING SIMPLE CHANGE NOTIFY\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NTCON), + "Failed to setup up test directory: " BASEDIR_CN1_NTCON); + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_NTCON; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_open"); + + /* ask for a change notify, + on file or directory name changes */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = true; + + torture_comment(tctx, "Testing notify mkdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "Testing notify rmdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "SIMPLE CHANGE NOTIFY OK\n"); + + torture_comment(tctx, "TESTING WITH SECONDARY TCON\n"); + tree = secondary_tcon(cli, tctx); + torture_assert_not_null_goto(tctx, tree, ret, done, + "failed to create secondary tcon"); + + torture_comment(tctx, "Testing notify mkdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "Testing notify rmdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "CHANGE NOTIFY WITH TCON OK\n"); + + torture_comment(tctx, "Disconnecting secondary tree\n"); + status = smb_tree_disconnect(tree); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_tree_disconnect"); + talloc_free(tree); + + torture_comment(tctx, "Testing notify mkdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_ADDED, ret, done, + "wrong action (exp: ADDED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "Testing notify rmdir\n"); + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb_raw_changenotify_recv"); + torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes, + 1, ret, done, "wrong number of changes"); + torture_assert_int_equal_goto(tctx, + notify.nttrans.out.changes[0].action, + NOTIFY_ACTION_REMOVED, ret, done, + "wrong action (exp: REMOVED)"); + CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name", + STR_UNICODE); + + torture_comment(tctx, "CHANGE NOTIFY WITH TDIS OK\n"); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_NTCON); + return ret; +} + +struct cb_data { + struct smbcli_request *req; + bool timed_out; +}; + +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct cb_data *cbp = (struct cb_data *)private_data; + cbp->req->state = SMBCLI_REQUEST_ERROR; + cbp->timed_out = true; +} + +/* + testing alignment of multiple change notify infos +*/ + +#define BASEDIR_CN1_NALIGN BASEDIR "_CN1_NALIGN" + +static bool test_notify_alignment(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_notify notify; + union smb_open io; + int fnum, fnum2; + struct smbcli_request *req; + const char *fname = BASEDIR_CN1_NALIGN "\\starter"; + const char *fnames[] = { "a", + "ab", + "abc", + "abcd" }; + bool fnames_received[] = {false, + false, + false, + false}; + size_t total_names_received = 0; + size_t num_names = ARRAY_SIZE(fnames); + size_t i; + char *fpath = NULL; + + torture_comment(tctx, "TESTING CHANGE NOTIFY REPLY ALIGNMENT\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NALIGN), + "Failed to setup up test directory: " BASEDIR_CN1_NALIGN); + + /* get a handle on the directory */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR_CN1_NALIGN; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "smb_raw_open"); + fnum = io.ntcreatex.out.file.fnum; + + /* ask for a change notify, on file creation */ + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + notify.nttrans.in.buffer_size = 1000; + notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME; + notify.nttrans.in.file.fnum = fnum; + notify.nttrans.in.recursive = false; + + /* start change tracking */ + req = smb_raw_changenotify_send(cli->tree, ¬ify); + + fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree)); + smbcli_close(cli->tree, fnum2); + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + torture_assert_ntstatus_ok(tctx, status, "smb_raw_changenotify_recv"); + + /* create 4 files that will cause CHANGE_NOTIFY_INFO structures + * to be returned in the same packet with all possible 4-byte padding + * permutations. As per MS-CIFS 2.2.7.4.2 these structures should be + * 4-byte aligned. */ + + for (i = 0; i < num_names; i++) { + fpath = talloc_asprintf(tctx, "%s\\%s", + BASEDIR_CN1_NALIGN, fnames[i]); + fnum2 = smbcli_open(cli->tree, fpath, + O_CREAT|O_RDWR, DENY_NONE); + torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree)); + smbcli_close(cli->tree, fnum2); + talloc_free(fpath); + } + + /* + * Slow cloud filesystems mean we might + * not get everything in one go. Keep going + * until we get them all. + */ + while (total_names_received < num_names) { + struct tevent_timer *te = NULL; + struct cb_data to_data = {0}; + + /* + * We send a notify packet, and let + * smb_raw_changenotify_recv() do + * the alignment checking for us. + */ + req = smb_raw_changenotify_send(cli->tree, ¬ify); + torture_assert(tctx, + req != NULL, + "smb_raw_changenotify_send failed\n"); + + /* Ensure we don't wait more than 30 seconds. */ + to_data.req = req; + to_data.timed_out = false; + + te = tevent_add_timer(tctx->ev, + req, + tevent_timeval_current_ofs(30, 0), + timeout_cb, + &to_data); + if (te == NULL) { + torture_fail(tctx, "tevent_add_timer fail\n"); + } + + status = smb_raw_changenotify_recv(req, tctx, ¬ify); + if (!NT_STATUS_IS_OK(status)) { + if (to_data.timed_out == true) { + torture_fail(tctx, "smb_raw_changenotify_recv " + "timed out\n"); + } + } + + torture_assert_ntstatus_ok(tctx, status, + "smb_raw_changenotify_recv"); + + for (i = 0; i < notify.nttrans.out.num_changes; i++) { + size_t j; + + /* Ensure it was an 'add'. */ + torture_assert(tctx, + notify.nttrans.out.changes[i].action == + NOTIFY_ACTION_ADDED, + ""); + + for (j = 0; j < num_names; j++) { + if (strcmp(notify.nttrans.out.changes[i].name.s, + fnames[j]) == 0) { + if (fnames_received[j] == true) { + const char *err = + talloc_asprintf(tctx, + "Duplicate " + "name %s\n", + fnames[j]); + if (err == NULL) { + torture_fail(tctx, + "talloc " + "fail\n"); + } + /* already got this. */ + torture_fail(tctx, err); + } + fnames_received[j] = true; + break; + } + } + if (j == num_names) { + /* No name match. */ + const char *err = talloc_asprintf(tctx, + "Unexpected name %s\n", + notify.nttrans.out.changes[i].name.s); + if (err == NULL) { + torture_fail(tctx, "talloc fail\n"); + } + torture_fail(tctx, err); + } + total_names_received++; + } + } + + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR_CN1_NALIGN); + return true; +} + +struct torture_suite *torture_raw_notify(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "notify"); + + torture_suite_add_1smb_test(suite, "tcon", test_notify_tcon); + torture_suite_add_2smb_test(suite, "dir", test_notify_dir); + torture_suite_add_2smb_test(suite, "mask", test_notify_mask); + torture_suite_add_2smb_test(suite, "recursive", test_notify_recursive); + torture_suite_add_1smb_test(suite, "mask_change", + test_notify_mask_change); + torture_suite_add_1smb_test(suite, "file", test_notify_file); + torture_suite_add_1smb_test(suite, "tdis", test_notify_tdis); + torture_suite_add_1smb_test(suite, "exit", test_notify_exit); + torture_suite_add_1smb_test(suite, "ulogoff", test_notify_ulogoff); + torture_suite_add_1smb_test(suite, "tcp_dis", test_notify_tcp_dis); + torture_suite_add_1smb_test(suite, "double", test_notify_double); + torture_suite_add_2smb_test(suite, "tree", test_notify_tree); + torture_suite_add_1smb_test(suite, "overflow", test_notify_overflow); + torture_suite_add_1smb_test(suite, "basedir", test_notify_basedir); + torture_suite_add_1smb_test(suite, "alignment", test_notify_alignment); + + return suite; +} diff --git a/source4/torture/raw/offline.c b/source4/torture/raw/offline.c new file mode 100644 index 0000000..262003f --- /dev/null +++ b/source4/torture/raw/offline.c @@ -0,0 +1,514 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + test offline files + */ + +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\testoffline" + +static int nconnections; +static int numstates; +static int num_connected; +static int test_failed; +extern int torture_numops; +extern int torture_entries; +static bool test_finished; + +enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST}; + +static double latencies[OP_ENDOFLIST]; +static double worst_latencies[OP_ENDOFLIST]; + +#define FILE_SIZE 8192 + + +struct offline_state { + struct torture_context *tctx; + struct tevent_context *ev; + struct smbcli_tree *tree; + TALLOC_CTX *mem_ctx; + int client; + int fnum; + uint32_t count; + uint32_t lastcount; + uint32_t fnumber; + uint32_t offline_count; + uint32_t online_count; + char *fname; + struct smb_composite_loadfile *loadfile; + struct smb_composite_savefile *savefile; + struct smbcli_request *req; + enum offline_op op; + struct timeval tv_start; +}; + +static void test_offline(struct offline_state *state); + + +static char *filename(TALLOC_CTX *ctx, int i) +{ + char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i); + return s; +} + + +/* + called when a loadfile completes + */ +static void loadfile_callback(struct composite_context *ctx) +{ + struct offline_state *state = ctx->async.private_data; + NTSTATUS status; + int i; + + status = smb_composite_loadfile_recv(ctx, state->mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to read file '%s' - %s\n", + state->loadfile->in.fname, nt_errstr(status)); + test_failed++; + return; + } + + /* check the data is correct */ + if (state->loadfile->out.size != FILE_SIZE) { + printf("Wrong file size %u - expected %u\n", + state->loadfile->out.size, FILE_SIZE); + test_failed++; + return; + } + + for (i=0;iloadfile->out.data[i] != 1+(state->fnumber % 255)) { + printf("Bad data in file %u (got %u expected %u)\n", + state->fnumber, + state->loadfile->out.data[i], + 1+(state->fnumber % 255)); + test_failed++; + return; + } + } + + talloc_steal(state->loadfile, state->loadfile->out.data); + + state->count++; + talloc_free(state->loadfile); + state->loadfile = NULL; + + if (!test_finished) { + test_offline(state); + } +} + + +/* + called when a savefile completes + */ +static void savefile_callback(struct composite_context *ctx) +{ + struct offline_state *state = ctx->async.private_data; + NTSTATUS status; + + status = smb_composite_savefile_recv(ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to save file '%s' - %s\n", + state->savefile->in.fname, nt_errstr(status)); + test_failed++; + } + + state->count++; + talloc_free(state->savefile); + state->savefile = NULL; + + if (!test_finished) { + test_offline(state); + } +} + + +/* + called when a setoffline completes + */ +static void setoffline_callback(struct smbcli_request *req) +{ + struct offline_state *state = req->async.private_data; + NTSTATUS status; + + status = smbcli_request_simple_recv(req); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to set offline file '%s' - %s\n", + state->fname, nt_errstr(status)); + test_failed++; + } + + state->req = NULL; + state->count++; + + if (!test_finished) { + test_offline(state); + } +} + + +/* + called when a getoffline completes + */ +static void getoffline_callback(struct smbcli_request *req) +{ + struct offline_state *state = req->async.private_data; + NTSTATUS status; + union smb_fileinfo io; + + ZERO_STRUCT(io); + + io.getattr.level = RAW_FILEINFO_GETATTR; + + status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to get offline file '%s' - %s\n", + state->fname, nt_errstr(status)); + test_failed++; + } + + if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) { + state->offline_count++; + } else { + state->online_count++; + } + + state->req = NULL; + state->count++; + + if (!test_finished) { + test_offline(state); + } +} + + +/* + send the next offline file fetch request +*/ +static void test_offline(struct offline_state *state) +{ + struct composite_context *ctx; + double lat; + + lat = timeval_elapsed(&state->tv_start); + if (latencies[state->op] < lat) { + latencies[state->op] = lat; + } + + state->op = (enum offline_op) (random() % OP_ENDOFLIST); + + state->fnumber = random() % torture_numops; + talloc_free(state->fname); + state->fname = filename(state->mem_ctx, state->fnumber); + + state->tv_start = timeval_current(); + + switch (state->op) { + case OP_LOADFILE: + state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile); + state->loadfile->in.fname = state->fname; + + ctx = smb_composite_loadfile_send(state->tree, state->loadfile); + if (ctx == NULL) { + printf("Failed to setup loadfile for %s\n", state->fname); + test_failed = true; + } + + talloc_steal(state->loadfile, ctx); + + ctx->async.fn = loadfile_callback; + ctx->async.private_data = state; + break; + + case OP_SAVEFILE: + state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile); + + state->savefile->in.fname = state->fname; + state->savefile->in.data = talloc_size(state->savefile, FILE_SIZE); + state->savefile->in.size = FILE_SIZE; + memset(state->savefile->in.data, 1+(state->fnumber%255), FILE_SIZE); + + ctx = smb_composite_savefile_send(state->tree, state->savefile); + if (ctx == NULL) { + printf("Failed to setup savefile for %s\n", state->fname); + test_failed = true; + } + + talloc_steal(state->savefile, ctx); + + ctx->async.fn = savefile_callback; + ctx->async.private_data = state; + break; + + case OP_SETOFFLINE: { + union smb_setfileinfo io; + ZERO_STRUCT(io); + io.setattr.level = RAW_SFILEINFO_SETATTR; + io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE; + io.setattr.in.file.path = state->fname; + /* make the file 1 hour old, to get past minimum age restrictions + for HSM systems */ + io.setattr.in.write_time = time(NULL) - 60*60; + + state->req = smb_raw_setpathinfo_send(state->tree, &io); + if (state->req == NULL) { + printf("Failed to setup setoffline for %s\n", state->fname); + test_failed = true; + } + + state->req->async.fn = setoffline_callback; + state->req->async.private_data = state; + break; + } + + case OP_GETOFFLINE: { + union smb_fileinfo io; + ZERO_STRUCT(io); + io.getattr.level = RAW_FILEINFO_GETATTR; + io.getattr.in.file.path = state->fname; + + state->req = smb_raw_pathinfo_send(state->tree, &io); + if (state->req == NULL) { + printf("Failed to setup getoffline for %s\n", state->fname); + test_failed = true; + } + + state->req->async.fn = getoffline_callback; + state->req->async.private_data = state; + break; + } + + default: + printf("bad operation??\n"); + break; + } +} + + + + +static void echo_completion(struct smbcli_request *req) +{ + struct offline_state *state = (struct offline_state *)req->async.private_data; + NTSTATUS status = smbcli_request_simple_recv(req); + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + state->tree = NULL; + num_connected--; + DEBUG(0,("lost connection\n")); + test_failed++; + } +} + +static void report_rate(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct offline_state *state = talloc_get_type(private_data, + struct offline_state); + int i; + uint32_t total=0, total_offline=0, total_online=0; + for (i=0;i latencies[state[i].op]) { + latencies[state[i].op] = timeval_elapsed(&state[i].tv_start); + } + state[i].lastcount = state[i].count; + total_online += state[i].online_count; + total_offline += state[i].offline_count; + } + printf("ops/s=%4u offline=%5u online=%4u set_lat=%.1f/%.1f get_lat=%.1f/%.1f save_lat=%.1f/%.1f load_lat=%.1f/%.1f\n", + total, total_offline, total_online, + latencies[OP_SETOFFLINE], + worst_latencies[OP_SETOFFLINE], + latencies[OP_GETOFFLINE], + worst_latencies[OP_GETOFFLINE], + latencies[OP_SAVEFILE], + worst_latencies[OP_SAVEFILE], + latencies[OP_LOADFILE], + worst_latencies[OP_LOADFILE]); + fflush(stdout); + tevent_add_timer(ev, state, timeval_current_ofs(1, 0), report_rate, state); + + for (i=0;i worst_latencies[i]) { + worst_latencies[i] = latencies[i]; + } + latencies[i] = 0; + } + + /* send an echo on each interface to ensure it stays alive - this helps + with IP takeover */ + for (i=0;isession->transport, &p); + req->async.private_data = &state[i]; + req->async.fn = echo_completion; + } +} + +/* + test offline file handling +*/ +bool torture_test_offline(struct torture_context *torture) +{ + bool ret = true; + TALLOC_CTX *mem_ctx = talloc_new(torture); + int i; + int timelimit = torture_setting_int(torture, "timelimit", 10); + struct timeval tv; + struct offline_state *state; + struct smbcli_state *cli; + bool progress; + progress = torture_setting_bool(torture, "progress", true); + + nconnections = torture_setting_int(torture, "nprocs", 4); + numstates = nconnections * torture_entries; + + state = talloc_zero_array(mem_ctx, struct offline_state, numstates); + + printf("Opening %d connections with %d simultaneous operations and %u files\n", nconnections, numstates, torture_numops); + for (i=0;iev; + if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) { + return false; + } + state[i].tree = cli->tree; + state[i].client = i; + /* allow more time for offline files */ + state[i].tree->session->transport->options.request_timeout = 200; + } + + /* the others are repeats on the earlier connections */ + for (i=nconnections;iev; + state[i].tree = state[i % nconnections].tree; + state[i].client = i; + } + + num_connected = i; + + if (!torture_setup_dir(cli, BASEDIR)) { + goto failed; + } + + /* pre-create files */ + printf("Pre-creating %u files ....\n", torture_numops); + for (i=0;iev, state, timeval_current_ofs(1, 0), report_rate, state); + } + + printf("Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + tevent_loop_once(torture->ev); + + if (test_failed) { + DEBUG(0,("test failed\n")); + goto failed; + } + } + + printf("\nWaiting for completion\n"); + test_finished = true; + for (i=0;iev); + } + } + + printf("worst latencies: set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\n", + worst_latencies[OP_SETOFFLINE], + worst_latencies[OP_GETOFFLINE], + worst_latencies[OP_SAVEFILE], + worst_latencies[OP_LOADFILE]); + + smbcli_deltree(state[0].tree, BASEDIR); + talloc_free(mem_ctx); + printf("\n"); + return ret; + +failed: + talloc_free(mem_ctx); + return false; +} diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c new file mode 100644 index 0000000..697079c --- /dev/null +++ b/source4/torture/raw/open.c @@ -0,0 +1,2253 @@ +/* + Unix SMB/CIFS implementation. + RAW_OPEN_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "system/time.h" +#include "system/filesys.h" +#include "lib/events/events.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +/* enum for whether reads/writes are possible on a file */ +enum rdwr_mode {RDWR_NONE, RDWR_RDONLY, RDWR_WRONLY, RDWR_RDWR}; + +#define BASEDIR "\\rawopen" + +/* + check if a open file can be read/written +*/ +static enum rdwr_mode check_rdwr(struct smbcli_tree *tree, int fnum) +{ + uint8_t c = 1; + bool can_read = (smbcli_read(tree, fnum, &c, 0, 1) == 1); + bool can_write = (smbcli_write(tree, fnum, 0, &c, 0, 1) == 1); + if ( can_read && can_write) return RDWR_RDWR; + if ( can_read && !can_write) return RDWR_RDONLY; + if (!can_read && can_write) return RDWR_WRONLY; + return RDWR_NONE; +} + +/* + describe a RDWR mode as a string +*/ +static const char *rdwr_string(enum rdwr_mode m) +{ + switch (m) { + case RDWR_NONE: return "NONE"; + case RDWR_RDONLY: return "RDONLY"; + case RDWR_WRONLY: return "WRONLY"; + case RDWR_RDWR: return "RDWR"; + } + return "-"; +} + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CREATE_FILE do { \ + fnum = create_complex_file(cli, tctx, fname); \ + if (fnum == -1) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Failed to create %s - %s\n", \ + __location__, fname, smbcli_errstr(cli->tree)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_RDWR(fnum, correct) do { \ + enum rdwr_mode m = check_rdwr(cli->tree, fnum); \ + if (m != correct) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect readwrite mode %s - expected %s\n", \ + __location__, rdwr_string(m), rdwr_string(correct)); \ + ret = false; \ + }} while (0) + +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.file.path = fname; \ + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \ + if (labs(t1-t2) > 2) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + timestring(tctx, t1), \ + timestring(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.file.path = fname; \ + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (llabs((int64_t)(t-t2)) > 20000) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + nt_time_string(tctx, t), \ + nt_time_string(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.file.path = fname; \ + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != (finfo.all_info.out.field)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for field %s 0x%x - 0x%x\n", \ + __location__, #field, (unsigned int)(v), (unsigned int)(finfo.all_info.out.field)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (unsigned int)(v), (unsigned int)(correct)); \ + ret = false; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.basic_info.in.file.path = fname; \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_warning(tctx, "(%s) Failed to set attrib 0x%x on %s\n", \ + __location__, (unsigned int)(sattrib), fname); \ + }} while (0) + +/* + test RAW_OPEN_OPEN +*/ +static bool test_open(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_open.txt"; + NTSTATUS status; + int fnum = -1, fnum2; + bool ret = true; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.openold.level = RAW_OPEN_OPEN; + io.openold.in.fname = fname; + io.openold.in.open_mode = OPEN_FLAGS_FCB; + io.openold.in.search_attrs = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + fnum = io.openold.out.file.fnum; + + smbcli_unlink(cli->tree, fname); + CREATE_FILE; + smbcli_close(cli->tree, fnum); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.openold.out.file.fnum; + CHECK_RDWR(fnum2, RDWR_RDWR); + smbcli_close(cli->tree, fnum2); + smbcli_close(cli->tree, fnum); + + /* check the read/write modes */ + io.openold.level = RAW_OPEN_OPEN; + io.openold.in.fname = fname; + io.openold.in.search_attrs = 0; + + io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + CHECK_RDWR(fnum, RDWR_RDONLY); + smbcli_close(cli->tree, fnum); + + io.openold.in.open_mode = OPEN_FLAGS_OPEN_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + CHECK_RDWR(fnum, RDWR_WRONLY); + smbcli_close(cli->tree, fnum); + + io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + smbcli_close(cli->tree, fnum); + + /* check the share modes roughly - not a complete matrix */ + io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + if (io.openold.in.open_mode != io.openold.out.rmode) { + torture_warning(tctx, "(%s) rmode should equal open_mode - 0x%x 0x%x\n", + __location__, io.openold.out.rmode, io.openold.in.open_mode); + } + + io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.openold.out.file.fnum; + CHECK_RDWR(fnum2, RDWR_RDONLY); + smbcli_close(cli->tree, fnum); + smbcli_close(cli->tree, fnum2); + + + /* check the returned write time */ + io.openold.level = RAW_OPEN_OPEN; + io.openold.in.fname = fname; + io.openold.in.search_attrs = 0; + io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openold.out.file.fnum; + + /* check other reply fields */ + CHECK_TIME(io.openold.out.write_time, write_time); + CHECK_ALL_INFO(io.openold.out.size, size); + CHECK_ALL_INFO(io.openold.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_OPENX +*/ +static bool test_openx(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_openx.txt"; + const char *fname_exe = BASEDIR "\\torture_openx.exe"; + NTSTATUS status; + int fnum = -1, fnum2; + bool ret = true; + int i; + struct timeval tv; + struct { + uint16_t open_func; + bool with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { OPENX_OPEN_FUNC_OPEN, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_FAIL, true, NT_STATUS_DOS(ERRDOS, ERRbadaccess) }, + { OPENX_OPEN_FUNC_FAIL, false, NT_STATUS_DOS(ERRDOS, ERRbadaccess) }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK }, + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 1024*1024; + io.openx.in.timeout = 0; + + /* check all combinations of open_func */ + for (i=0; itree)); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + } + io.openx.in.open_func = open_funcs[i].open_func; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d with_file=%d open_func=0x%x)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, + open_funcs[i].open_func); + ret = false; + } + if (NT_STATUS_IS_OK(status)) { + smbcli_close(cli->tree, io.openx.out.file.fnum); + } + if (open_funcs[i].with_file) { + smbcli_unlink(cli->tree, fname); + } + } + + smbcli_unlink(cli->tree, fname); + + /* check the basic return fields */ + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.file.fnum; + + CHECK_ALL_INFO(io.openx.out.size, size); + CHECK_TIME(io.openx.out.write_time, write_time); + CHECK_ALL_INFO(io.openx.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED); + CHECK_VAL(io.openx.out.access, OPENX_MODE_ACCESS_RDWR); + CHECK_VAL(io.openx.out.ftype, 0); + CHECK_VAL(io.openx.out.devstate, 0); + CHECK_VAL(io.openx.out.action, OPENX_ACTION_CREATED); + CHECK_VAL(io.openx.out.size, 1024*1024); + CHECK_ALL_INFO(io.openx.in.size, size); + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* check the fields when the file already existed */ + fnum2 = create_complex_file(cli, tctx, fname); + if (fnum2 == -1) { + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum2); + + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.file.fnum; + + CHECK_ALL_INFO(io.openx.out.size, size); + CHECK_TIME(io.openx.out.write_time, write_time); + CHECK_VAL(io.openx.out.action, OPENX_ACTION_EXISTED); + CHECK_VAL(io.openx.out.unknown, 0); + CHECK_ALL_INFO(io.openx.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED); + smbcli_close(cli->tree, fnum); + + /* now check the search attrib for hidden files - win2003 ignores this? */ + SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN); + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib); + + io.openx.in.search_attrs = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.openx.out.file.fnum); + + io.openx.in.search_attrs = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.openx.out.file.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + smbcli_unlink(cli->tree, fname); + + /* and check attrib on create */ + io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = FILE_ATTRIBUTE_SYSTEM; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(tctx, "samba3", false)) { + CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE, + attrib & ~(FILE_ATTRIBUTE_NONINDEXED| + FILE_ATTRIBUTE_SPARSE)); + } + else { + CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE, + attrib & ~(FILE_ATTRIBUTE_NONINDEXED)); + } + smbcli_close(cli->tree, io.openx.out.file.fnum); + smbcli_unlink(cli->tree, fname); + + /* check timeout on create - win2003 ignores the timeout! */ + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.openx.in.file_attrs = 0; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.file.fnum; + + io.openx.in.timeout = 20000; + tv = timeval_current(); + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + if (timeval_elapsed(&tv) > 3.0) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect timing in openx with timeout " + "- waited %.2f seconds\n", + __location__, timeval_elapsed(&tv)); + ret = false; + } + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* now this is a really weird one - open for execute implies create?! */ + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE; + io.openx.in.search_attrs = 0; + io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 0; + io.openx.in.timeout = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.openx.out.file.fnum); + + /* check the extended return flag */ + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | OPENX_FLAGS_EXTENDED_RETURN; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.openx.out.access_mask, SEC_STD_ALL); + smbcli_close(cli->tree, io.openx.out.file.fnum); + + io.openx.in.fname = "\\A.+,;=[].B"; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* Check the mapping for open exec. */ + + /* First create an .exe file. */ + smbcli_unlink(cli->tree, fname_exe); + fnum = create_complex_file(cli, tctx, fname_exe); + smbcli_close(cli->tree, fnum); + + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname_exe; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 0; + io.openx.in.timeout = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Can we read and write ? */ + CHECK_RDWR(io.openx.out.file.fnum, RDWR_RDONLY); + smbcli_close(cli->tree, io.openx.out.file.fnum); + smbcli_unlink(cli->tree, fname); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_T2OPEN + + many thanks to kukks for a sniff showing how this works with os2->w2k +*/ +static bool test_t2open(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname1 = BASEDIR "\\torture_t2open_yes.txt"; + const char *fname2 = BASEDIR "\\torture_t2open_no.txt"; + const char *fname = BASEDIR "\\torture_t2open_3.txt"; + NTSTATUS status; + int fnum; + bool ret = true; + int i; + struct { + uint16_t open_func; + bool with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { OPENX_OPEN_FUNC_OPEN, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_FAIL, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL, false, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_TRUNC, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC, false, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK }, + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum = create_complex_file(cli, tctx, fname1); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, + "(%s): Failed to create file %s - %s\n", + __location__, fname1, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + + io.t2open.level = RAW_OPEN_T2OPEN; + io.t2open.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.t2open.in.open_mode = OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR; + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.search_attrs = 0; + io.t2open.in.file_attrs = 0; + io.t2open.in.write_time = 0; + io.t2open.in.size = 0; + io.t2open.in.timeout = 0; + + io.t2open.in.num_eas = 3; + io.t2open.in.eas = talloc_array(tctx, struct ea_struct, io.t2open.in.num_eas); + io.t2open.in.eas[0].flags = 0; + io.t2open.in.eas[0].name.s = ".CLASSINFO"; + io.t2open.in.eas[0].value = data_blob_talloc(tctx, "first value", 11); + io.t2open.in.eas[1].flags = 0; + io.t2open.in.eas[1].name.s = "EA TWO"; + io.t2open.in.eas[1].value = data_blob_talloc(tctx, "foo", 3); + io.t2open.in.eas[2].flags = 0; + io.t2open.in.eas[2].name.s = "X THIRD"; + io.t2open.in.eas[2].value = data_blob_talloc(tctx, "xy", 2); + + /* check all combinations of open_func */ + for (i=0; itree, tctx, &io); + if ((io.t2open.in.num_eas != 0) + && NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED) + && torture_setting_bool(tctx, "samba3", false)) { + torture_warning(tctx, "(%s) EAs not supported, not " + "treating as fatal in Samba3 test\n", + __location__); + io.t2open.in.num_eas = 0; + goto again; + } + + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d with_file=%d open_func=0x%x)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, + open_funcs[i].open_func); + ret = false; + } + if (NT_STATUS_IS_OK(status)) { + smbcli_close(cli->tree, io.t2open.out.file.fnum); + } + } + + smbcli_unlink(cli->tree, fname1); + smbcli_unlink(cli->tree, fname2); + + /* check the basic return fields */ + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.write_time = 0; + io.t2open.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.t2open.out.file.fnum; + + CHECK_ALL_INFO(io.t2open.out.size, size); +#if 0 + /* windows appears to leak uninitialised memory here */ + CHECK_VAL(io.t2open.out.write_time, 0); +#endif + CHECK_ALL_INFO(io.t2open.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED); + CHECK_VAL(io.t2open.out.access, OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR); + CHECK_VAL(io.t2open.out.ftype, 0); + CHECK_VAL(io.t2open.out.devstate, 0); + CHECK_VAL(io.t2open.out.action, OPENX_ACTION_CREATED); + smbcli_close(cli->tree, fnum); + + status = torture_check_ea(cli, fname, ".CLASSINFO", "first value"); + CHECK_STATUS(status, io.t2open.in.num_eas + ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED); + status = torture_check_ea(cli, fname, "EA TWO", "foo"); + CHECK_STATUS(status, io.t2open.in.num_eas + ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED); + status = torture_check_ea(cli, fname, "X THIRD", "xy"); + CHECK_STATUS(status, io.t2open.in.num_eas + ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED); + + /* now check the search attrib for hidden files - win2003 ignores this? */ + SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN); + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.t2open.out.file.fnum); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.t2open.out.file.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + smbcli_unlink(cli->tree, fname); + + /* and check attrib on create */ + io.t2open.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.file_attrs = FILE_ATTRIBUTE_SYSTEM; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* check timeout on create - win2003 ignores the timeout! */ + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.file_attrs = 0; + io.t2open.in.timeout = 20000; + io.t2open.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_NTCREATEX +*/ +static bool test_ntcreatex(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + const char *dname = BASEDIR "\\torture_ntcreatex.dir"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + int i; + struct { + uint32_t open_disp; + bool with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_OK }, + { 6, true, NT_STATUS_INVALID_PARAMETER }, + { 6, false, NT_STATUS_INVALID_PARAMETER }, + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* reasonable default parameters */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 1024*1024; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* test the open disposition */ + for (i=0; itree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, + "Failed to create file %s - %s\n", + fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + } + io.ntcreatex.in.open_disposition = open_funcs[i].open_disp; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d with_file=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, + (int)open_funcs[i].open_disp); + ret = false; + } + if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_unlink(cli->tree, fname); + } + } + + /* basic field testing */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + + /* check fields when the file already existed */ + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, tctx, fname); + if (fnum == -1) { + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + + /* create a directory */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.fname = dname; + fname = dname; + + smbcli_rmdir(cli->tree, fname); + smbcli_unlink(cli->tree, fname); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_VAL(io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_DIRECTORY); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.is_directory, 1); + CHECK_VAL(io.ntcreatex.out.size, 0); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_unlink(cli->tree, fname); + + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_NTTRANS_CREATE +*/ +static bool test_nttrans_create(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + const char *dname = BASEDIR "\\torture_ntcreatex.dir"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + int i; + uint32_t ok_mask, not_supported_mask, invalid_parameter_mask; + uint32_t not_a_directory_mask, unexpected_mask; + struct { + uint32_t open_disp; + bool with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_OK }, + { 6, true, NT_STATUS_INVALID_PARAMETER }, + { 6, false, NT_STATUS_INVALID_PARAMETER }, + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* reasonable default parameters */ + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 1024*1024; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.sec_desc = NULL; + io.ntcreatex.in.ea_list = NULL; + + /* test the open disposition */ + for (i=0; itree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, + "Failed to create file %s - %s\n", + fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + } + io.ntcreatex.in.open_disposition = open_funcs[i].open_disp; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d with_file=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, + (int)open_funcs[i].open_disp); + ret = false; + } + if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_unlink(cli->tree, fname); + } + } + + /* basic field testing */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + + /* check fields when the file already existed */ + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + fnum = create_complex_file(cli, tctx, fname); + if (fnum == -1) { + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_close(cli->tree, fnum); + + /* check no-recall - don't pull a file from tape on a HSM */ + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NO_RECALL; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_close(cli->tree, fnum); + + /* Check some create options (these all should be ignored) */ + for (i=0; i < 32; i++) { + uint32_t create_option = + ((uint32_t)1 << i) & NTCREATEX_OPTIONS_MUST_IGNORE_MASK; + if (create_option == 0) { + continue; + } + io.ntcreatex.in.create_options = create_option; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "ntcreatex create option 0x%08x " + "gave %s - should give NT_STATUS_OK\n", + create_option, nt_errstr(status)); + } + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_close(cli->tree, fnum); + } + + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + + /* Check for options that should return NOT_SUPPORTED, OK or INVALID_PARAMETER */ + ok_mask = 0; + not_supported_mask = 0; + invalid_parameter_mask = 0; + not_a_directory_mask = 0; + unexpected_mask = 0; + for (i=0; i < 32; i++) { + uint32_t create_option = (uint32_t)1<tree, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + not_supported_mask |= create_option; + } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + ok_mask |= create_option; + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + invalid_parameter_mask |= create_option; + } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { + not_a_directory_mask |= 1<tree, fname); + + + /* create a directory */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.fname = dname; + fname = dname; + + smbcli_rmdir(cli->tree, fname); + smbcli_unlink(cli->tree, fname); + + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_VAL(io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_DIRECTORY); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.is_directory, 1); + CHECK_VAL(io.ntcreatex.out.size, 0); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + smbcli_unlink(cli->tree, fname); + + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test RAW_OPEN_NTCREATEX with an already opened and byte range locked file + + I've got an application that does a similar sequence of ntcreate&x, + locking&x and another ntcreate&x with + open_disposition==NTCREATEX_DISP_OVERWRITE_IF. Windows 2003 allows the + second open. +*/ +static bool test_ntcreatex_brlocked(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io, io1; + union smb_lock io2; + struct smb_lock_entry lock[1]; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + NTSTATUS status; + bool ret = true; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing ntcreatex with a byte range locked file\n"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = 0x2019f; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.ntcreatex.in.security_flags = NTCREATEX_SECURITY_DYNAMIC | + NTCREATEX_SECURITY_ALL; + io.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io2.lockx.level = RAW_LOCK_LOCKX; + io2.lockx.in.file.fnum = io.ntcreatex.out.file.fnum; + io2.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io2.lockx.in.timeout = 0; + io2.lockx.in.ulock_cnt = 0; + io2.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 0; + lock[0].count = 0x1; + io2.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + + io1.generic.level = RAW_OPEN_NTCREATEX; + io1.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io1.ntcreatex.in.root_fid.fnum = 0; + io1.ntcreatex.in.access_mask = 0x20196; + io1.ntcreatex.in.alloc_size = 0; + io1.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io1.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io1.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io1.ntcreatex.in.create_options = 0; + io1.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io1.ntcreatex.in.security_flags = NTCREATEX_SECURITY_DYNAMIC | + NTCREATEX_SECURITY_ALL; + io1.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_close(cli->tree, io1.ntcreatex.out.file.fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test RAW_OPEN_MKNEW +*/ +static bool test_mknew(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_mknew.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + time_t basetime = (time(NULL) + 3600*24*3) & ~1; + union smb_fileinfo finfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.mknew.level = RAW_OPEN_MKNEW; + io.mknew.in.attrib = 0; + io.mknew.in.write_time = 0; + io.mknew.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.file.fnum; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* make sure write_time works */ + io.mknew.in.write_time = basetime; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.file.fnum; + CHECK_TIME(basetime, write_time); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* make sure file_attrs works */ + io.mknew.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.file.fnum; + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, + attrib & ~FILE_ATTRIBUTE_NONINDEXED); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_CREATE +*/ +static bool test_create(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_create.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + time_t basetime = (time(NULL) + 3600*24*3) & ~1; + union smb_fileinfo finfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.create.level = RAW_OPEN_CREATE; + io.create.in.attrib = 0; + io.create.in.write_time = 0; + io.create.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.create.out.file.fnum; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, io.create.out.file.fnum); + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* make sure write_time works */ + io.create.in.write_time = basetime; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.create.out.file.fnum; + CHECK_TIME(basetime, write_time); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + /* make sure file_attrs works */ + io.create.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.create.out.file.fnum; + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, + attrib & ~FILE_ATTRIBUTE_NONINDEXED); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test RAW_OPEN_CTEMP +*/ +static bool test_ctemp(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + NTSTATUS status; + int fnum = -1; + bool ret = true; + time_t basetime = (time(NULL) + 3600*24*3) & ~1; + union smb_fileinfo finfo; + const char *name, *fname = NULL; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + io.ctemp.level = RAW_OPEN_CTEMP; + io.ctemp.in.attrib = FILE_ATTRIBUTE_HIDDEN; + io.ctemp.in.write_time = basetime; + io.ctemp.in.directory = BASEDIR; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ctemp.out.file.fnum; + + name = io.ctemp.out.name; + + finfo.generic.level = RAW_FILEINFO_NAME_INFO; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + fname = finfo.name_info.out.fname.s; + torture_comment(tctx, "ctemp name=%s real name=%s\n", name, fname); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + test chained RAW_OPEN_OPENX_READX +*/ +static bool test_chained(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_chained.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + const char buf[] = "test"; + char buf2[4]; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fnum = create_complex_file(cli, tctx, fname); + + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + + smbcli_close(cli->tree, fnum); + + io.openxreadx.level = RAW_OPEN_OPENX_READX; + io.openxreadx.in.fname = fname; + io.openxreadx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openxreadx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openxreadx.in.open_func = OPENX_OPEN_FUNC_OPEN; + io.openxreadx.in.search_attrs = 0; + io.openxreadx.in.file_attrs = 0; + io.openxreadx.in.write_time = 0; + io.openxreadx.in.size = 1024*1024; + io.openxreadx.in.timeout = 0; + + io.openxreadx.in.offset = 0; + io.openxreadx.in.mincnt = sizeof(buf2); + io.openxreadx.in.maxcnt = sizeof(buf2); + io.openxreadx.in.remaining = 0; + io.openxreadx.out.data = (uint8_t *)buf2; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openxreadx.out.file.fnum; + + if (memcmp(buf, buf2, MIN(sizeof(buf), sizeof(buf2))) != 0) { + torture_result(tctx, TORTURE_FAIL, + "wrong data in reply buffer\n"); + ret = false; + } + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test RAW_OPEN_OPENX without a leading slash on the path. + NetApp filers are known to fail on this. + +*/ +static bool test_no_leading_slash(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_no_leading_slash.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + const char buf[] = "test"; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + smbcli_unlink(cli->tree, fname); + + /* Create the file */ + fnum = create_complex_file(cli, tctx, fname); + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + smbcli_close(cli->tree, fnum); + + /* Prepare to open the file using path without leading slash */ + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname + 1; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 1024*1024; + io.openx.in.timeout = 0; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.file.fnum; + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test RAW_OPEN_OPENX against an existing directory to + ensure it returns NT_STATUS_FILE_IS_A_DIRECTORY. + Samba 3.2.0 - 3.2.6 are known to fail this. + +*/ +static bool test_openx_over_dir(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\openx_over_dir"; + NTSTATUS status; + int d_fnum = -1; + int fnum = -1; + bool ret = true; + + ZERO_STRUCT(io); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* Create the Directory */ + status = create_directory_handle(cli->tree, fname, &d_fnum); + smbcli_close(cli->tree, d_fnum); + + /* Prepare to open the file over the directory. */ + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 1024*1024; + io.openx.in.timeout = 0; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + fnum = io.openx.out.file.fnum; + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* A little torture test to expose a race condition in Samba 3.0.20 ... :-) */ + +static bool test_raw_open_multi(struct torture_context *tctx, struct smbcli_state *cli_ignored) +{ + struct smbcli_state *cli; + TALLOC_CTX *mem_ctx = talloc_init("torture_test_oplock_multi"); + const char *fname = "\\test_oplock.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smbcli_state **clients; + struct smbcli_request **requests; + union smb_open *ios; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + int i, num_files = 3; + int num_ok = 0; + int num_collision = 0; + + clients = talloc_array(mem_ctx, struct smbcli_state *, num_files); + requests = talloc_array(mem_ctx, struct smbcli_request *, num_files); + ios = talloc_array(mem_ctx, union smb_open, num_files); + if ((tctx->ev == NULL) || (clients == NULL) || (requests == NULL) || + (ios == NULL)) { + torture_result(tctx, TORTURE_FAIL, "(%s): talloc failed\n", + __location__); + return false; + } + + if (!torture_open_connection_share(mem_ctx, &cli, tctx, host, share, tctx->ev)) { + return false; + } + + cli->tree->session->transport->options.request_timeout = 60; + + for (i=0; iev)) { + torture_result(tctx, TORTURE_FAIL, + "(%s): Could not open %d'th connection\n", + __location__, i); + return false; + } + clients[i]->tree->session->transport->options.request_timeout = 60; + } + + /* cleanup */ + smbcli_unlink(cli->tree, fname); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + + for (i=0; itree, &ios[i]); + if (requests[i] == NULL) { + torture_result(tctx, TORTURE_FAIL, + "(%s): could not send %d'th request\n", + __location__, i); + return false; + } + } + + torture_comment(tctx, "waiting for replies\n"); + while (1) { + bool unreplied = false; + for (i=0; istate < SMBCLI_REQUEST_DONE) { + unreplied = true; + break; + } + status = smb_raw_open_recv(requests[i], mem_ctx, + &ios[i]); + + torture_comment(tctx, "File %d returned status %s\n", i, + nt_errstr(status)); + + if (NT_STATUS_IS_OK(status)) { + num_ok += 1; + } + + if (NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { + num_collision += 1; + } + + requests[i] = NULL; + } + if (!unreplied) { + break; + } + + if (tevent_loop_once(tctx->ev) != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s): tevent_loop_once failed\n", __location__); + return false; + } + } + + if ((num_ok != 1) || (num_ok + num_collision != num_files)) { + ret = false; + } + + for (i=0; itree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + io.ntcreatex.in.create_options = 0; + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + smbcli_close(cli->tree, fnum); + + /* Now try and open for delete only - should succeed. */ + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_unlink(cli->tree, fname); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + test chained RAW_OPEN_NTCREATEX_READX + Send chained NTCREATEX_READX on a file that doesn't exist, then create + the file and try again. +*/ +static bool test_chained_ntcreatex_readx(struct torture_context *tctx, struct smbcli_state *cli) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + union smb_open io; + const char *fname = BASEDIR "\\torture_chained.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + const char buf[] = "test"; + char buf2[4]; + + ZERO_STRUCT(io); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Checking RAW_NTCREATEX_READX chained on " + "non-existent file \n"); + + /* ntcreatex parameters */ + io.generic.level = RAW_OPEN_NTCREATEX_READX; + io.ntcreatexreadx.in.flags = 0; + io.ntcreatexreadx.in.root_fid.fnum = 0; + io.ntcreatexreadx.in.access_mask = SEC_FILE_READ_DATA; + io.ntcreatexreadx.in.alloc_size = 0; + io.ntcreatexreadx.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatexreadx.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatexreadx.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatexreadx.in.create_options = 0; + io.ntcreatexreadx.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.ntcreatexreadx.in.security_flags = 0; + io.ntcreatexreadx.in.fname = fname; + + /* readx parameters */ + io.ntcreatexreadx.in.offset = 0; + io.ntcreatexreadx.in.mincnt = sizeof(buf2); + io.ntcreatexreadx.in.maxcnt = sizeof(buf2); + io.ntcreatexreadx.in.remaining = 0; + io.ntcreatexreadx.out.data = (uint8_t *)buf2; + + /* try to open the non-existent file */ + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + fnum = io.ntcreatexreadx.out.file.fnum; + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Checking RAW_NTCREATEX_READX chained on " + "existing file \n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + smbcli_close(cli->tree, fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatexreadx.out.file.fnum; + + if (memcmp(buf, buf2, MIN(sizeof(buf), sizeof(buf2))) != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s): wrong data in reply buffer\n", __location__); + ret = false; + } + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_ntcreatex_opendisp_dir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *dname = BASEDIR "\\torture_ntcreatex_opendisp_dir"; + NTSTATUS status; + bool ret = true; + int i; + struct { + uint32_t open_disp; + bool dir_exists; + NTSTATUS correct_status; + } open_funcs_dir[] = { + { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_INVALID_PARAMETER }, + { 6, true, NT_STATUS_INVALID_PARAMETER }, + { 6, false, NT_STATUS_INVALID_PARAMETER }, + }; + union smb_open io; + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.fname = dname; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + smbcli_rmdir(cli->tree, dname); + smbcli_unlink(cli->tree, dname); + + /* test the open disposition for directories */ + torture_comment(tctx, "Testing open dispositions for directories...\n"); + + for (i=0; itree, dname); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s): Failed to make directory " + "%s - %s\n", __location__, dname, + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + } + + io.ntcreatex.in.open_disposition = open_funcs_dir[i].open_disp; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs_dir[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d dir_exists=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs_dir[i].correct_status), + i, (int)open_funcs_dir[i].dir_exists, + (int)open_funcs_dir[i].open_disp); + ret = false; + } + if (NT_STATUS_IS_OK(status) || open_funcs_dir[i].dir_exists) { + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_rmdir(cli->tree, dname); + } + } + +done: + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/** + * Test what happens when trying to open a file with directory parameters and + * vice-versa. Also test that NTCREATEX_OPTIONS_DIRECTORY is treated as + * mandatory and FILE_ATTRIBUTE_DIRECTORY is advisory for directory + * creation/opening. + */ +static bool test_ntcreatexdir(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + const char *dname = BASEDIR "\\torture_ntcreatex_dir"; + NTSTATUS status; + int i; + + struct { + uint32_t open_disp; + uint32_t file_attr; + uint32_t create_options; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OPEN, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, 0, NTCREATEX_OPTIONS_DIRECTORY, + NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_SUPERSEDE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, FILE_ATTRIBUTE_DIRECTORY, 0, + NT_STATUS_OK }, + + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* setup some base params. */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + * Test the validity checking for create dispositions, which is done + * against the requested parameters rather than what's actually on + * disk. + */ + for (i=0; itree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].open_disp); + return false; + } + /* Close and delete the file. */ + if (NT_STATUS_IS_OK(status)) { + if (open_funcs[i].create_options != 0) { + /* out attrib should be a directory. */ + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_DIRECTORY, "should have " + "created a directory"); + + smbcli_close(cli->tree, + io.ntcreatex.out.file.fnum); + + /* Make sure unlink fails. */ + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_IS_A_DIRECTORY, + "unlink should fail for a directory"); + + status = smbcli_rmdir(cli->tree, fname); + torture_assert_ntstatus_ok(tctx, status, + "rmdir failed"); + } else { + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_ARCHIVE, "should not have " + "created a directory"); + + smbcli_close(cli->tree, + io.ntcreatex.out.file.fnum); + + /* Make sure rmdir fails. */ + status = smbcli_rmdir(cli->tree, fname); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_A_DIRECTORY, + "rmdir should fail for a file"); + + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_ok(tctx, status, + "unlink failed"); + } + } + } + + /* Create a file. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "Failed to create file."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open the file with file_attr_dir and check the error. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "FILE_ATTRIBUTE_DIRECTORY " + "doesn't produce a hard failure."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open file with createx_option_dir and check the error. */ + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_A_DIRECTORY, + "NTCREATEX_OPTIONS_DIRECTORY will a file from being opened."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Delete the file and move onto directory testing. */ + smbcli_unlink(cli->tree, fname); + + /* Now try some tests on a directory. */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "Failed to create dir."); + + /* out attrib should be a directory. */ + torture_assert_int_equal(tctx, io.ntcreatex.out.attrib, + FILE_ATTRIBUTE_DIRECTORY, "should have created a directory"); + + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open it with normal attr and check the error. */ + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_ok(tctx, status, "FILE_ATTRIBUTE_NORMAL " + "doesn't produce a hard failure."); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try and open it with file create_options and check the error. */ + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_IS_A_DIRECTORY, + "NTCREATEX_OPTIONS_NON_DIRECTORY_FILE should be returned "); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + smbcli_deltree(cli->tree, BASEDIR); + + return true; +} + +/* + test opening with truncate on an already open file + returns share violation and doesn't truncate the file. + Regression test for bug #10671. +*/ +static bool test_open_for_truncate(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_open_for_truncate.txt"; + NTSTATUS status; + int fnum = -1; + ssize_t val = 0; + char c = '\0'; + bool ret = true; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing open truncate disposition.\n"); + + /* reasonable default parameters */ + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* Write a byte at offset 1k-1. */ + val =smbcli_write(cli->tree, fnum, 0, &c, 1023, 1); + torture_assert_int_equal(tctx, val, 1, "write failed\n"); + + /* Now try and open for read/write with truncate - should fail. */ + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_WRITE|SEC_RIGHTS_FILE_READ; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + /* Ensure file size is still 1k */ + finfo.generic.level = RAW_FILEINFO_GETATTRE; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.getattre.out.size, 1024); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* Ensure truncate actually works */ + finfo.generic.level = RAW_FILEINFO_GETATTRE; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(finfo.getattre.out.size, 0); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/** + * Test for file size to be 0 after create with FILE_SUPERSEDE + */ +static bool test_ntcreatex_supersede(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_setfileinfo sfi; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_ntcreatex_supersede.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* reasonable default parameters */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + + /* extend the file size */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sfi.generic.in.file.fnum = fnum; + sfi.end_of_file_info.in.size = 512; + status = smb_raw_setfileinfo(cli->tree, &sfi); + CHECK_STATUS(status, NT_STATUS_OK); + + /* close the file and re-open with to verify new size */ + smbcli_close(cli->tree, fnum); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_VAL(io.ntcreatex.out.size, 512); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + + /* close and re-open the file with SUPERSEDE flag */ + smbcli_close(cli->tree, fnum); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.create_options = 0; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* The file size in the superseded create response should be 0 */ + CHECK_VAL(io.ntcreatex.out.size, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_SUPERSEDED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* basic testing of all RAW_OPEN_* calls +*/ +struct torture_suite *torture_raw_open(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "open"); + + torture_suite_add_1smb_test(suite, "brlocked", test_ntcreatex_brlocked); + torture_suite_add_1smb_test(suite, "open", test_open); + torture_suite_add_1smb_test(suite, "open-multi", test_raw_open_multi); + torture_suite_add_1smb_test(suite, "openx", test_openx); + torture_suite_add_1smb_test(suite, "ntcreatex", test_ntcreatex); + torture_suite_add_1smb_test(suite, "nttrans-create", test_nttrans_create); + torture_suite_add_1smb_test(suite, "t2open", test_t2open); + torture_suite_add_1smb_test(suite, "mknew", test_mknew); + torture_suite_add_1smb_test(suite, "create", test_create); + torture_suite_add_1smb_test(suite, "ctemp", test_ctemp); + torture_suite_add_1smb_test(suite, "chained-openx", test_chained); + torture_suite_add_1smb_test(suite, "chained-ntcreatex", test_chained_ntcreatex_readx); + torture_suite_add_1smb_test(suite, "no-leading-slash", test_no_leading_slash); + torture_suite_add_1smb_test(suite, "openx-over-dir", test_openx_over_dir); + torture_suite_add_1smb_test(suite, "open-for-delete", test_open_for_delete); + torture_suite_add_1smb_test(suite, "opendisp-dir", test_ntcreatex_opendisp_dir); + torture_suite_add_1smb_test(suite, "ntcreatedir", test_ntcreatexdir); + torture_suite_add_1smb_test(suite, "open-for-truncate", test_open_for_truncate); + torture_suite_add_1smb_test(suite, "ntcreatex_supersede", test_ntcreatex_supersede); + + return suite; +} diff --git a/source4/torture/raw/openbench.c b/source4/torture/raw/openbench.c new file mode 100644 index 0000000..8349f6e --- /dev/null +++ b/source4/torture/raw/openbench.c @@ -0,0 +1,502 @@ +/* + Unix SMB/CIFS implementation. + + open benchmark + + Copyright (C) Andrew Tridgell 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "torture/raw/proto.h" +#include "libcli/smb/smbXcli_base.h" +#include "../lib/util/util_net.h" + +#define BASEDIR "\\benchopen" + +static int nprocs; +static int open_failed; +static int close_failed; +static char **fnames; +static int num_connected; +static struct tevent_timer *report_te; + +struct benchopen_state { + struct torture_context *tctx; + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + struct smbcli_state *cli; + struct smbcli_tree *tree; + int client_num; + int close_fnum; + int open_fnum; + int close_file_num; + int open_file_num; + int pending_file_num; + int next_file_num; + int count; + int lastcount; + union smb_open open_parms; + int open_retries; + union smb_close close_parms; + struct smbcli_request *req_open; + struct smbcli_request *req_close; + struct smb_composite_connect reconnect; + struct tevent_timer *te; + + /* these are used for reconnections */ + const char **dest_ports; + const char *dest_host; + const char *called_name; + const char *service_type; +}; + +static void next_open(struct benchopen_state *state); +static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data); + + +/* + complete an async reconnect + */ +static void reopen_connection_complete(struct composite_context *ctx) +{ + struct benchopen_state *state = (struct benchopen_state *)ctx->async.private_data; + NTSTATUS status; + struct smb_composite_connect *io = &state->reconnect; + + status = smb_composite_connect_recv(ctx, state->mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + return; + } + + state->tree = io->out.tree; + + num_connected++; + + DEBUG(0,("[%u] reconnect to %s finished (%u connected)\n", + state->client_num, state->dest_host, num_connected)); + + state->open_fnum = -1; + state->close_fnum = -1; + next_open(state); +} + + + +/* + reopen a connection + */ +static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct benchopen_state *state = (struct benchopen_state *)private_data; + struct composite_context *ctx; + struct smb_composite_connect *io = &state->reconnect; + char *host, *share; + + state->te = NULL; + + if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) { + DEBUG(0,("Can't find host/share for reconnect?!\n")); + exit(1); + } + + io->in.dest_host = state->dest_host; + io->in.dest_ports = state->dest_ports; + io->in.socket_options = lpcfg_socket_options(state->tctx->lp_ctx); + io->in.called_name = state->called_name; + io->in.service = share; + io->in.service_type = state->service_type; + io->in.credentials = samba_cmdline_get_creds(); + io->in.fallback_to_anonymous = false; + io->in.workgroup = lpcfg_workgroup(state->tctx->lp_ctx); + io->in.gensec_settings = lpcfg_gensec_settings(state->mem_ctx, state->tctx->lp_ctx); + lpcfg_smbcli_options(state->tctx->lp_ctx, &io->in.options); + lpcfg_smbcli_session_options(state->tctx->lp_ctx, &io->in.session_options); + + /* kill off the remnants of the old connection */ + talloc_free(state->tree); + state->tree = NULL; + state->open_fnum = -1; + state->close_fnum = -1; + + ctx = smb_composite_connect_send(io, state->mem_ctx, + lpcfg_resolve_context(state->tctx->lp_ctx), + state->ev); + if (ctx == NULL) { + DEBUG(0,("Failed to setup async reconnect\n")); + exit(1); + } + + ctx->async.fn = reopen_connection_complete; + ctx->async.private_data = state; +} + +static void open_completed(struct smbcli_request *req); +static void close_completed(struct smbcli_request *req); + + +static void next_open(struct benchopen_state *state) +{ + state->count++; + + state->pending_file_num = state->next_file_num; + state->next_file_num = (state->next_file_num+1) % (3*nprocs); + + DEBUG(2,("[%d] opening %u\n", state->client_num, state->pending_file_num)); + state->open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + state->open_parms.ntcreatex.in.flags = 0; + state->open_parms.ntcreatex.in.root_fid.fnum = 0; + state->open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + state->open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + state->open_parms.ntcreatex.in.alloc_size = 0; + state->open_parms.ntcreatex.in.share_access = 0; + state->open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + state->open_parms.ntcreatex.in.create_options = 0; + state->open_parms.ntcreatex.in.impersonation = 0; + state->open_parms.ntcreatex.in.security_flags = 0; + state->open_parms.ntcreatex.in.fname = fnames[state->pending_file_num]; + + state->req_open = smb_raw_open_send(state->tree, &state->open_parms); + state->req_open->async.fn = open_completed; + state->req_open->async.private_data = state; +} + + +static void next_close(struct benchopen_state *state) +{ + if (state->close_fnum == -1) { + return; + } + DEBUG(2,("[%d] closing %d (fnum[%d])\n", + state->client_num, state->close_file_num, state->close_fnum)); + state->close_parms.close.level = RAW_CLOSE_CLOSE; + state->close_parms.close.in.file.fnum = state->close_fnum; + state->close_parms.close.in.write_time = 0; + + state->req_close = smb_raw_close_send(state->tree, &state->close_parms); + state->req_close->async.fn = close_completed; + state->req_close->async.private_data = state; +} + +/* + called when a open completes +*/ +static void open_completed(struct smbcli_request *req) +{ + struct benchopen_state *state = (struct benchopen_state *)req->async.private_data; + TALLOC_CTX *tmp_ctx = talloc_new(state->mem_ctx); + NTSTATUS status; + + status = smb_raw_open_recv(req, tmp_ctx, &state->open_parms); + + talloc_free(tmp_ctx); + + state->req_open = NULL; + + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + talloc_free(state->cli); + state->tree = NULL; + state->cli = NULL; + num_connected--; + DEBUG(0,("[%u] reopening connection to %s\n", + state->client_num, state->dest_host)); + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + return; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + DEBUG(2,("[%d] retrying open %d\n", + state->client_num, state->pending_file_num)); + state->open_retries++; + state->req_open = smb_raw_open_send(state->tree, &state->open_parms); + state->req_open->async.fn = open_completed; + state->req_open->async.private_data = state; + return; + } + + if (!NT_STATUS_IS_OK(status)) { + open_failed++; + DEBUG(0,("[%u] open failed %d - %s\n", + state->client_num, state->pending_file_num, + nt_errstr(status))); + return; + } + + state->close_file_num = state->open_file_num; + state->close_fnum = state->open_fnum; + state->open_file_num = state->pending_file_num; + state->open_fnum = state->open_parms.ntcreatex.out.file.fnum; + + DEBUG(2,("[%d] open completed %d (fnum[%d])\n", + state->client_num, state->open_file_num, state->open_fnum)); + + if (state->close_fnum != -1) { + next_close(state); + } + + next_open(state); +} + +/* + called when a close completes +*/ +static void close_completed(struct smbcli_request *req) +{ + struct benchopen_state *state = (struct benchopen_state *)req->async.private_data; + NTSTATUS status = smbcli_request_simple_recv(req); + + state->req_close = NULL; + + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + talloc_free(state->cli); + state->tree = NULL; + state->cli = NULL; + num_connected--; + DEBUG(0,("[%u] reopening connection to %s\n", + state->client_num, state->dest_host)); + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + return; + } + + if (!NT_STATUS_IS_OK(status)) { + close_failed++; + DEBUG(0,("[%u] close failed %d (fnum[%d]) - %s\n", + state->client_num, state->close_file_num, + state->close_fnum, + nt_errstr(status))); + return; + } + + DEBUG(2,("[%d] close completed %d (fnum[%d])\n", + state->client_num, state->close_file_num, + state->close_fnum)); +} + +static void echo_completion(struct smbcli_request *req) +{ + struct benchopen_state *state = (struct benchopen_state *)req->async.private_data; + NTSTATUS status = smbcli_request_simple_recv(req); + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || + NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) { + talloc_free(state->tree); + state->tree = NULL; + num_connected--; + DEBUG(0,("[%u] reopening connection to %s\n", + state->client_num, state->dest_host)); + talloc_free(state->te); + state->te = tevent_add_timer(state->ev, state->mem_ctx, + timeval_current_ofs(1,0), + reopen_connection, state); + } +} + +static void report_rate(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct benchopen_state *state = talloc_get_type(private_data, + struct benchopen_state); + int i; + for (i=0;isession->transport, &p); + req->async.private_data = &state[i]; + req->async.fn = echo_completion; + } +} + +/* + benchmark open calls +*/ +bool torture_bench_open(struct torture_context *torture) +{ + bool ret = true; + TALLOC_CTX *mem_ctx = talloc_new(torture); + int i; + int timelimit = torture_setting_int(torture, "timelimit", 10); + struct timeval tv; + struct benchopen_state *state; + int total = 0; + int total_retries = 0; + int minops = 0; + bool progress=false; + + progress = torture_setting_bool(torture, "progress", true); + + nprocs = torture_setting_int(torture, "nprocs", 4); + + state = talloc_zero_array(mem_ctx, struct benchopen_state, nprocs); + + printf("Opening %d connections\n", nprocs); + for (i=0;iev; + if (!torture_open_connection_ev(&state[i].cli, i, torture, torture->ev)) { + return false; + } + talloc_steal(state[i].mem_ctx, state[i].cli); + state[i].tree = state[i].cli->tree; + + dest_ss = smbXcli_conn_remote_sockaddr( + state[i].tree->session->transport->conn); + dest_str = print_sockaddr(addrstr, sizeof(addrstr), dest_ss); + dest_port = get_sockaddr_port(dest_ss); + + state[i].dest_host = talloc_strdup(state[i].mem_ctx, dest_str); + state[i].dest_ports = talloc_array(state[i].mem_ctx, + const char *, 2); + state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports, + "%u", dest_port); + state[i].dest_ports[1] = NULL; + state[i].called_name = talloc_strdup(state[i].mem_ctx, + smbXcli_conn_remote_name(state[i].tree->session->transport->conn)); + state[i].service_type = talloc_strdup(state[i].mem_ctx, "?????"); + } + + num_connected = i; + + if (!torture_setup_dir(state[0].cli, BASEDIR)) { + goto failed; + } + + fnames = talloc_array(mem_ctx, char *, 3*nprocs); + for (i=0;i<3*nprocs;i++) { + fnames[i] = talloc_asprintf(fnames, "%s\\file%d.dat", BASEDIR, i); + } + + for (i=0;iev, state, timeval_current_ofs(1, 0), + report_rate, state); + } + + printf("Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + tevent_loop_once(torture->ev); + + if (open_failed) { + DEBUG(0,("open failed\n")); + goto failed; + } + if (close_failed) { + DEBUG(0,("open failed\n")); + goto failed; + } + } + + talloc_free(report_te); + if (progress) { + for (i=0;isession); + } + + smbcli_deltree(state[0].tree, BASEDIR); + talloc_free(mem_ctx); + return ret; + +failed: + talloc_free(mem_ctx); + return false; +} diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c new file mode 100644 index 0000000..173d5ca --- /dev/null +++ b/source4/torture/raw/oplock.c @@ -0,0 +1,4672 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for oplocks + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/resolve/resolve.h" +#include "torture/raw/proto.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_RANGE(v, min, max) do { \ + if ((v) < (min) || (v) > (max)) { \ + torture_warning(tctx, "(%s): wrong value for %s got " \ + "%d - should be between %d and %d\n", \ + __location__, #v, (int)v, (int)min, (int)max); \ + }} while (0) + +#define CHECK_STRMATCH(v, correct) do { \ + if (!v || strstr((v),(correct)) == NULL) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got '%s' - should be '%s'\n", \ + __location__, #v, v?v:"NULL", correct); \ + ret = false; \ + } \ +} while (0) + +#define CHECK_STATUS(tctx, status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + +static struct { + int fnum; + uint8_t level; + int count; + int failures; +} break_info; + +#define BASEDIR "\\test_oplock" + +/* + a handler function for oplock break requests. Ack it as a break to level II if possible +*/ +static bool oplock_handler_ack_to_given(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, + uint8_t level, void *private_data) +{ + struct smbcli_tree *tree = (struct smbcli_tree *)private_data; + const char *name; + + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + switch (level) { + case OPLOCK_BREAK_TO_LEVEL_II: + name = "level II"; + break; + case OPLOCK_BREAK_TO_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + printf("Acking to %s [0x%02X] in oplock handler\n", + name, level); + + return smbcli_oplock_ack(tree, fnum, level); +} + +/* + a handler function for oplock break requests. Ack it as a break to none +*/ +static bool oplock_handler_ack_to_none(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, + uint8_t level, void *private_data) +{ + struct smbcli_tree *tree = (struct smbcli_tree *)private_data; + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + printf("Acking to none in oplock handler\n"); + + return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE); +} + +/* + a handler function for oplock break requests. Let it timeout +*/ +static bool oplock_handler_timeout(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, + uint8_t level, void *private_data) +{ + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + printf("Let oplock break timeout\n"); + return true; +} + +static void oplock_handler_close_recv(struct smbcli_request *req) +{ + NTSTATUS status; + status = smbcli_request_simple_recv(req); + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler_close\n"); + break_info.failures++; + } +} + +/* + a handler function for oplock break requests - close the file +*/ +static bool oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, + uint16_t fnum, uint8_t level, void *private_data) +{ + union smb_close io; + struct smbcli_tree *tree = (struct smbcli_tree *)private_data; + struct smbcli_request *req; + + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.file.fnum = fnum; + io.close.in.write_time = 0; + req = smb_raw_close_send(tree, &io); + if (req == NULL) { + printf("failed to send close in oplock_handler_close\n"); + return false; + } + + req->async.fn = oplock_handler_close_recv; + req->async.private_data = NULL; + + return true; +} + +static bool open_connection_no_level2_oplocks(struct torture_context *tctx, + struct smbcli_state **c) +{ + NTSTATUS status; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + options.use_level2_oplocks = false; + + status = smbcli_full_connection(tctx, c, + torture_setting_string(tctx, "host", NULL), + lpcfg_smb_ports(tctx->lp_ctx), + torture_setting_string(tctx, "share", NULL), + NULL, lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to open connection - %s\n", + nt_errstr(status)); + return false; + } + + return true; +} + +/* + Timer handler function notifies the registering function that time is up +*/ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; + return; +} + +/* + Wait a short period of time to receive a single oplock break request +*/ +static void torture_wait_for_oplock_break(struct torture_context *tctx) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + int old_count = break_info.count; + + /* Wait .1 seconds for an oplock break */ + ne = tevent_timeval_current_ofs(0, 100000); + + if ((te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up)) + == NULL) + { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate."); + goto done; + } + + while (!timesup && break_info.count < old_count + 1) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate."); + goto done; + } + } + +done: + /* We don't know if the timed event fired and was freed, we received + * our oplock break, or some other event triggered the loop. Thus, + * we create a tmp_ctx to be able to safely free/remove the timed + * event in all 3 cases. */ + talloc_free(tmp_ctx); + + return; +} + +static uint8_t get_break_level1_to_none_count(struct torture_context *tctx) +{ + return torture_setting_bool(tctx, "2_step_break_to_none", false) ? + 2 : 1; +} + +static uint8_t get_setinfo_break_count(struct torture_context *tctx) +{ + if (TARGET_IS_W2K12(tctx)) { + return 2; + } + if (TARGET_IS_SAMBA3(tctx)) { + return 2; + } + return 1; +} + +static bool test_raw_oplock_exclusive1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "a 2nd open should not cause a break\n"); + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "unlink it - should also be no break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive2(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "a 2nd open should cause a break to level 2\n"); + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + /* now we have 2 level II oplocks... */ + torture_comment(tctx, "try to unlink it - should not cause a break, but a sharing violation\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "close 1st handle\n"); + smbcli_close(cli1->tree, fnum); + + torture_comment(tctx, "try to unlink it - should not cause a break, but a sharing violation\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "close 2nd handle\n"); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "unlink it\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "setpathinfo EOF should trigger a break to none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + + CHECK_STATUS(tctx, status, NT_STATUS_OK); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_setinfo_break_count(tctx)); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't cause oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* + * Open another non-stat open. This reproduces bug 10216. Make sure it + * won't happen again... + */ + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE_IF disposition causes oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx)); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive6(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_exclusive6_1.dat"; + const char *fname2 = BASEDIR "\\test_exclusive6_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_rename rn; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "rename should not generate a break but get a " + "sharing violation\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_RENAME; + rn.rename.in.pattern1 = fname1; + rn.rename.in.pattern2 = fname2; + rn.rename.in.attrib = 0; + + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli2->tree, &rn); + + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/** + * Exclusive version of batch19 + */ +static bool test_raw_oplock_exclusive7(struct torture_context *tctx, + struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_exclusiv6_1.dat"; + const char *fname2 = BASEDIR "\\test_exclusiv6_2.dat"; + const char *fname3 = BASEDIR "\\test_exclusiv6_3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + uint16_t fnum=0; + uint16_t fnum2 = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + smbcli_unlink(cli1->tree, fname3); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "open a file with an exclusive oplock (share " + "mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "setpathinfo rename info should trigger a break " + "to none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.failures, 0); + + if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K12(tctx)) { + /* XP incorrectly breaks to level2. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else { + /* Exclusive oplocks should not be broken on rename. */ + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.count, 0); + } + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2); + + /* Try breaking to level2 and then see if rename breaks the level2.*/ + ZERO_STRUCT(break_info); + io.ntcreatex.in.fname = fname2; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.failures, 0); + + if (TARGET_IS_WINXP(tctx)) { + /* XP already broke to level2. */ + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.count, 0); + } else if (TARGET_IS_W2K12(tctx)) { + /* no break */ + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + } else { + /* Break to level 2 expected. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } + + ZERO_STRUCT(break_info); + sfi.generic.in.file.path = fname2; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname1+strlen(BASEDIR)+1; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + /* Level2 oplocks are not broken on rename. */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.count, 0); + + /* Close and re-open file with oplock. */ + smbcli_close(cli1->tree, fnum); + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "setfileinfo rename info on a client's own fid " + "should not trigger a break nor a violation\n"); + ZERO_STRUCT(break_info); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.fnum = fnum; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1; + + status = smb_raw_setfileinfo(cli1->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + if (TARGET_IS_WINXP(tctx)) { + /* XP incorrectly breaks to level2. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else { + CHECK_VAL(break_info.count, 0); + } + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3); + +done: + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_exclusive8(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive8.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum1 = 0; + uint16_t fnum2 = 0; + uint16_t fnum3 = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "open a file with an exclusive oplock (share " + "mode: all)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "second open with delete should trigger a " + "break\n"); + + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.flags = 0; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx)); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + + /* Trigger a little panic in "old" samba code.. */ + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum3 = io.ntcreatex.out.file.fnum; + + smbcli_close(cli2->tree, fnum3); + smbcli_close(cli2->tree, fnum2); + smbcli_close(cli1->tree, fnum1); + +done: + smbcli_deltree(cli1->tree, BASEDIR); + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + return ret; +} + +static bool test_raw_oplock_exclusive9(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + int i; + + struct { + uint32_t create_disposition; + uint32_t break_level; + } levels[] = { + { NTCREATEX_DISP_SUPERSEDE, OPLOCK_BREAK_TO_NONE }, + { NTCREATEX_DISP_OPEN, OPLOCK_BREAK_TO_LEVEL_II }, + { NTCREATEX_DISP_OVERWRITE_IF, OPLOCK_BREAK_TO_NONE }, + { NTCREATEX_DISP_OPEN_IF, OPLOCK_BREAK_TO_LEVEL_II }, + }; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + for (i=0; itree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, + EXCLUSIVE_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + io.ntcreatex.in.open_disposition = + levels[i].create_disposition; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, + LEVEL_II_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, levels[i].break_level); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + } + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_level_ii_1(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_level_ii_1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + char c = 0; + ssize_t written; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + status = smbcli_close(cli2->tree, fnum2); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + /* + * fnum1 has a level2 oplock now + */ + + ZERO_STRUCT(break_info); + + /* + * Don't answer the break to none that will come in + */ + + smbcli_oplock_handler(cli1->transport, oplock_handler_timeout, + cli1->tree); + + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.failures, 0); + + /* + * Check that a write does not cause another break. This used to be a + * bug in smbd. + */ + + ZERO_STRUCT(break_info); + written = smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1); + CHECK_VAL(written, 1); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + status = smbcli_close(cli2->tree, fnum2); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + status = smbcli_close(cli1->tree, fnum); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + uint16_t fnum=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH1: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "unlink should generate a break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should generate a self break to none\n"); + smbcli_write(cli1->tree, fnum, 0, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch2(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + uint16_t fnum=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH2: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "unlink should generate a break, which we ack as break to none\n"); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_none, cli1->tree); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should not generate a break\n"); + smbcli_write(cli1->tree, fnum, 0, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH3: if we close on break then the unlink can succeed\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_close, cli1->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + ZERO_STRUCT(break_info); + status = smb_raw_unlink(cli2->tree, &unl); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_read rd; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH4: a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + rd.readx.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = 1; + rd.readx.in.maxcnt = 1; + rd.readx.in.offset = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + status = smb_raw_read(cli1->tree, &rd); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH5: a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch6(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch6.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH6: a 2nd open should give a break to level II if the first open allowed shared read\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + //torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + smbcli_write(cli1->tree, fnum, 0, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch7(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch7.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when we close instead of ack\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_close, cli1->tree); + + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum2); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli2->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch8(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch8.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH8: open with batch oplock\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't cause oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch9(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create file\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "Subsequent normal open should break oplock on attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "third oplocked open should grant level2 without break\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch9a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch9a.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create file\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_CREATED); + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "Subsequent attributes open should not break\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_OPENED); + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "Subsequent normal open should break oplock on attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "third oplocked open should grant level2 without break\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch10(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch10.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock open should grant level2\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + + { + union smb_write wr; + wr.write.level = RAW_WRITE_WRITE; + wr.write.in.file.fnum = fnum; + wr.write.in.count = 1; + wr.write.in.offset = 0; + wr.write.in.remaining = 0; + wr.write.in.data = (const uint8_t *)"x"; + status = smb_raw_write(cli1->tree, &wr); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + } + + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_comment(tctx, "write should trigger a break to none\n"); + { + union smb_write wr; + wr.write.level = RAW_WRITE_WRITE; + wr.write.in.file.fnum = fnum; + wr.write.in.count = 1; + wr.write.in.offset = 0; + wr.write.in.remaining = 0; + wr.write.in.data = (const uint8_t *)"x"; + status = smb_raw_write(cli1->tree, &wr); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + } + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch11(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch11.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* Test if a set-eof on pathname breaks an exclusive oplock. */ + torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_setinfo_break_count(tctx)); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch12(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch12.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* Test if a set-allocation size on pathname breaks an exclusive oplock. */ + torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size breaks oplocks.\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(sfi); + sfi.generic.level = SMB_SFILEINFO_ALLOCATION_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.allocation_info.in.alloc_size = 65536 * 8; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_setinfo_break_count(tctx)); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch13(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch13.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH13: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE disposition causes oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx)); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch14(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch14.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH14: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_SUPERSEDE disposition causes oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx)); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch15(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch15.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* Test if a qpathinfo all info on pathname breaks a batch oplock. */ + torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks a batch oplock (should not).\n"); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.path = fname; + + status = smb_raw_pathinfo(cli2->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch16(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch16.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH16: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE_IF disposition causes oplock break\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx)); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch17(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_batch17_1.dat"; + const char *fname2 = BASEDIR "\\test_batch17_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_rename rn; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "BATCH17: open a file with an batch oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "rename should trigger a break\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_RENAME; + rn.rename.in.pattern1 = fname1; + rn.rename.in.pattern2 = fname2; + rn.rename.in.attrib = 0; + + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli2->tree, &rn); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch18(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_batch18_1.dat"; + const char *fname2 = BASEDIR "\\test_batch18_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_rename rn; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "BATCH18: open a file with an batch oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "ntrename should trigger a break\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_NTRENAME; + rn.ntrename.in.attrib = 0; + rn.ntrename.in.flags = RENAME_FLAG_RENAME; + rn.ntrename.in.old_name = fname1; + rn.ntrename.in.new_name = fname2; + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli2->tree, &rn); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_batch19_1.dat"; + const char *fname2 = BASEDIR "\\test_batch19_2.dat"; + const char *fname3 = BASEDIR "\\test_batch19_3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + smbcli_unlink(cli1->tree, fname3); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "BATCH19: open a file with an batch oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "setpathinfo rename info should trigger a break " + "to none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.failures, 0); + + if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K12(tctx)) { + /* Win XP breaks to level2. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) || + TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) { + /* Win2K3/2k8 incorrectly doesn't break at all. */ + CHECK_VAL(break_info.count, 0); + } else { + /* win7/2k8r2 break to none. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + } + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2); + + /* Close and re-open file with oplock. */ + smbcli_close(cli1->tree, fnum); + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "setfileinfo rename info on a client's own fid " + "should not trigger a break nor a violation\n"); + ZERO_STRUCT(break_info); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.fnum = fnum; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1; + + status = smb_raw_setfileinfo(cli1->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + if (TARGET_IS_WINXP(tctx)) { + /* XP incorrectly breaks to level2. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else { + CHECK_VAL(break_info.count, 0); + } + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3); + +done: + smbcli_close(cli1->tree, fnum); + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/**************************************************** + Called from raw-rename - we need oplock handling for + this test so this is why it's in oplock.c, not rename.c +****************************************************/ + +bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_trans2rename_1.dat"; + const char *fname2 = BASEDIR "\\test_trans2rename_2.dat"; + const char *fname3 = BASEDIR "\\test_trans2rename_3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + smbcli_unlink(cli1->tree, fname3); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "open a file with an exclusive oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2); + + torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.fnum = fnum; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1; + + status = smb_raw_setfileinfo(cli1->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3); + +done: + smbcli_close(cli1->tree, fnum); + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/**************************************************** + Called from raw-rename - we need oplock handling for + this test so this is why it's in oplock.c, not rename.c +****************************************************/ + +bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1) +{ + const char *fname1 = BASEDIR "\\test_nttransrename_1.dat"; + const char *fname2 = BASEDIR "\\test_nttransrename_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi, qpi; + union smb_rename rn; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "nttrans_rename: open a file with an exclusive oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + torture_comment(tctx, "nttrans_rename: should not trigger a break nor a share mode violation\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_NTTRANS; + rn.nttrans.in.file.fnum = fnum; + rn.nttrans.in.flags = 0; + rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_rename(cli1->tree, &rn); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + /* w2k3 does nothing, it doesn't rename the file */ + torture_comment(tctx, "nttrans_rename: the server should have done nothing\n"); + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname1); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname1; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname2; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_comment(tctx, "nttrans_rename: after closing the file the file is still not renamed\n"); + status = smbcli_close(cli1->tree, fnum); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname1; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname2; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_comment(tctx, "nttrans_rename: rename with an invalid handle gives NT_STATUS_INVALID_HANDLE\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_NTTRANS; + rn.nttrans.in.file.fnum = fnum+1; + rn.nttrans.in.flags = 0; + rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_rename(cli1->tree, &rn); + + CHECK_STATUS(tctx, status, NT_STATUS_INVALID_HANDLE); + +done: + smb_raw_exit(cli1->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + + +static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_batch20_1.dat"; + const char *fname2 = BASEDIR "\\test_batch20_2.dat"; + const char *fname3 = BASEDIR "\\test_batch20_3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + uint16_t fnum=0,fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + smbcli_unlink(cli1->tree, fname3); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, "BATCH20: open a file with an batch oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.failures, 0); + + if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K12(tctx)) { + /* Win XP breaks to level2. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) || + TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) { + /* Win2K3/2k8 incorrectly doesn't break at all. */ + CHECK_VAL(break_info.count, 0); + } else { + /* win7/2k8r2 break to none. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + } + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2); + + torture_comment(tctx, "open a file with the new name an batch oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.fname = fname2; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + + if (TARGET_IS_WINXP(tctx)) { + /* XP broke to level2, and doesn't break again. */ + CHECK_VAL(break_info.count, 0); + } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) || + TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) { + /* Win2K3 incorrectly didn't break before so break now. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + } else { + /* win7/2k8r2 broke to none, and doesn't break again. */ + CHECK_VAL(break_info.count, 0); + } + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.fnum = fnum; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1; + + status = smb_raw_setfileinfo(cli1->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qfi.generic.in.file.fnum = fnum2; + + status = smb_raw_fileinfo(cli2->tree, tctx, &qfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3); + + +done: + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch21(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch21.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb_echo e; + uint16_t fnum=0; + char c = 0; + ssize_t wr; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH21: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "writing should not generate a break\n"); + wr = smbcli_write(cli1->tree, fnum, 0, &c, 0, 1); + CHECK_VAL(wr, 1); + CHECK_STATUS(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_OK); + + ZERO_STRUCT(e); + e.in.repeat_count = 1; + status = smb_raw_echo(cli1->transport, &e); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch22(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch22.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum = 0, fnum2 = 0, fnum3 = 0; + struct timeval tv; + int timeout = torture_setting_int(tctx, "oplocktimeout", 30); + int te; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH22: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "a 2nd open should not succeed after the oplock " + "break timeout\n"); + tv = timeval_current(); + smbcli_oplock_handler(cli1->transport, oplock_handler_timeout, cli1->tree); + status = smb_raw_open(cli1->tree, tctx, &io); + + if (TARGET_IS_W2K3(tctx)) { + /* 2k3 has an issue here. xp/win7 are ok. */ + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + } else { + CHECK_STATUS(tctx, status, NT_STATUS_OK); + } + + fnum2 = io.ntcreatex.out.file.fnum; + + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + + /* + * Some servers detect clients that let oplocks timeout, so this check + * only shows a warning message instead failing the test to eliminate + * failures from repeated runs of the test. This isn't ideal, but + * it's better than not running the test at all. + */ + CHECK_RANGE(te, timeout - 1, timeout + 15); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open should succeed after the oplock " + "release without break\n"); + tv = timeval_current(); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); +#if 0 + /* Samba 3.6.0 and above behave as Windows. */ + if (TARGET_IS_SAMBA3(tctx)) { + /* samba3 doesn't grant additional oplocks to bad clients. */ + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + } else { + CHECK_VAL(io.ntcreatex.out.oplock_level, + LEVEL_II_OPLOCK_RETURN); + } +#else + CHECK_VAL(io.ntcreatex.out.oplock_level, + LEVEL_II_OPLOCK_RETURN); +#endif + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + /* it should come in without delay */ + CHECK_RANGE(te+1, 0, timeout); + fnum3 = io.ntcreatex.out.file.fnum; + + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli1->tree, fnum2); + smbcli_close(cli1->tree, fnum3); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch23(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch23.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0,fnum3=0; + struct smbcli_state *cli3 = NULL; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + ret = open_connection_no_level2_oplocks(tctx, &cli3); + CHECK_VAL(ret, true); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + smbcli_oplock_handler(cli3->transport, oplock_handler_ack_to_given, cli3->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH23: a open and ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open without level2 oplock support should generate a break to level2\n"); + status = smb_raw_open(cli3->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum3 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 3rd open with level2 oplock support should not generate a break\n"); + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + smbcli_close(cli3->tree, fnum3); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smb_raw_exit(cli3->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch24(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch24.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum2=0,fnum3=0; + struct smbcli_state *cli3 = NULL; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + ret = open_connection_no_level2_oplocks(tctx, &cli3); + CHECK_VAL(ret, true); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + smbcli_oplock_handler(cli3->transport, oplock_handler_ack_to_given, cli3->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH24: a open without level support and ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli3->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum3 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open with level2 oplock support should generate a break to none\n"); + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum3); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli3->tree, fnum3); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smb_raw_exit(cli3->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_batch25(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch25.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH25: open a file with an batch oplock " + "(share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "setpathinfo attribute info should not trigger " + "a break nor a violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_SETATTR; + sfi.generic.in.file.path = fname; + sfi.setattr.in.attrib = FILE_ATTRIBUTE_HIDDEN; + sfi.setattr.in.write_time = 0; + + status = smb_raw_setpathinfo(cli2->tree, &sfi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/** + * Similar to batch17/18, but test with open share mode rather than + * share_none. + */ +static bool test_raw_oplock_batch26(struct torture_context *tctx, + struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname1 = BASEDIR "\\test_batch26_1.dat"; + const char *fname2 = BASEDIR "\\test_batch26_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_rename rn; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname1); + smbcli_unlink(cli1->tree, fname2); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname1; + + torture_comment(tctx, + "BATCH26: open a file with an batch oplock " + "(share mode: all)\n"); + + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "rename should trigger a break\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_RENAME; + rn.rename.in.pattern1 = fname1; + rn.rename.in.pattern2 = fname2; + rn.rename.in.attrib = 0; + + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli2->tree, &rn); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + + /* Close and reopen with batch again. */ + smbcli_close(cli1->tree, fnum); + ZERO_STRUCT(break_info); + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + /* Now try ntrename. */ + torture_comment(tctx, "ntrename should trigger a break\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_NTRENAME; + rn.ntrename.in.attrib = 0; + rn.ntrename.in.flags = RENAME_FLAG_RENAME; + rn.ntrename.in.old_name = fname1; + rn.ntrename.in.new_name = fname2; + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli2->tree, &rn); + CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/* Test how oplocks work on streams. */ +static bool test_raw_oplock_stream1(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + NTSTATUS status; + union smb_open io; + const char *fname_base = BASEDIR "\\test_stream1.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream, *fname_default_stream; + const char *default_stream = "::$DATA"; + bool ret = true; + int fnum = -1; + int i; + int stream_fnum = -1; + uint32_t batch_req = NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK | NTCREATEX_FLAGS_EXTENDED; + uint32_t exclusive_req = NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_EXTENDED; + +#define NSTREAM_OPLOCK_RESULTS 8 + struct { + const char **fname; + bool open_base_file; + uint32_t oplock_req; + uint32_t oplock_granted; + } stream_oplock_results[NSTREAM_OPLOCK_RESULTS] = { + /* Request oplock on stream without the base file open. */ + {&fname_stream, false, batch_req, NO_OPLOCK_RETURN}, + {&fname_default_stream, false, batch_req, NO_OPLOCK_RETURN}, + {&fname_stream, false, exclusive_req, EXCLUSIVE_OPLOCK_RETURN}, + {&fname_default_stream, false, exclusive_req, EXCLUSIVE_OPLOCK_RETURN}, + + /* Request oplock on stream with the base file open. */ + {&fname_stream, true, batch_req, NO_OPLOCK_RETURN}, + {&fname_default_stream, true, batch_req, NO_OPLOCK_RETURN}, + {&fname_stream, true, exclusive_req, EXCLUSIVE_OPLOCK_RETURN}, + {&fname_default_stream, true, exclusive_req, LEVEL_II_OPLOCK_RETURN}, + + }; + + + /* Only passes against windows at the moment. */ + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "STREAM1 disabled against samba3+4\n"); + } + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); + fname_default_stream = talloc_asprintf(tctx, "%s%s", fname_base, + default_stream); + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + smbcli_unlink(cli1->tree, fname_base); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + + /* Setup generic open parameters. */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + + /* Create the file with a stream */ + io.ntcreatex.in.fname = fname_stream; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + smbcli_close(cli1->tree, io.ntcreatex.out.file.fnum); + + /* Change the disposition to open now that the file has been created. */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + /* Try some permutations of taking oplocks on streams. */ + for (i = 0; i < NSTREAM_OPLOCK_RESULTS; i++) { + const char *fname = *stream_oplock_results[i].fname; + bool open_base_file = stream_oplock_results[i].open_base_file; + uint32_t oplock_req = stream_oplock_results[i].oplock_req; + uint32_t oplock_granted = + stream_oplock_results[i].oplock_granted; + int base_fnum = -1; + + if (open_base_file) { + torture_comment(tctx, "Opening base file: %s with " + "%d\n", fname_base, batch_req); + io.ntcreatex.in.fname = fname_base; + io.ntcreatex.in.flags = batch_req; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(io.ntcreatex.out.oplock_level, + BATCH_OPLOCK_RETURN); + base_fnum = io.ntcreatex.out.file.fnum; + } + + torture_comment(tctx, "%d: Opening stream: %s with %d\n", i, + fname, oplock_req); + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = oplock_req; + + /* Do the open with the desired oplock on the stream. */ + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(io.ntcreatex.out.oplock_level, oplock_granted); + smbcli_close(cli1->tree, io.ntcreatex.out.file.fnum); + + /* Cleanup the base file if it was opened. */ + if (base_fnum != -1) { + smbcli_close(cli2->tree, base_fnum); + } + } + + /* Open the stream with an exclusive oplock. */ + torture_comment(tctx, "Opening stream: %s with %d\n", + fname_stream, exclusive_req); + io.ntcreatex.in.fname = fname_stream; + io.ntcreatex.in.flags = exclusive_req; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + stream_fnum = io.ntcreatex.out.file.fnum; + + /* Open the base file and see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening base file: %s with " + "%d\n", fname_base, batch_req); + io.ntcreatex.in.fname = fname_base; + io.ntcreatex.in.flags = batch_req; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(io.ntcreatex.out.oplock_level, + BATCH_OPLOCK_RETURN); + smbcli_close(cli2->tree, io.ntcreatex.out.file.fnum); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* Open the stream again to see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening stream again: %s with " + "%d\n", fname_base, batch_req); + io.ntcreatex.in.fname = fname_stream; + io.ntcreatex.in.flags = exclusive_req; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(io.ntcreatex.out.oplock_level, + LEVEL_II_OPLOCK_RETURN); + smbcli_close(cli2->tree, io.ntcreatex.out.file.fnum); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + /* Close the stream. */ + if (stream_fnum != -1) { + smbcli_close(cli1->tree, stream_fnum); + } + + done: + smbcli_close(cli1->tree, fnum); + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool test_raw_oplock_doc(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_oplock_doc.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to set up test directory: " BASEDIR); + + /* cleanup */ + smbcli_unlink(cli->tree, fname); + + smbcli_oplock_handler(cli->transport, oplock_handler_ack_to_given, + cli->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "open a file with a batch oplock\n"); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "Set delete-on-close\n"); + status = smbcli_nt_delete_on_close(cli->tree, fnum, true); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "2nd open should not break and get " + "DELETE_PENDING\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_DELETE_PENDING); + CHECK_VAL(break_info.count, 0); + + smbcli_close(cli->tree, fnum); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock, then open it again from a second client + * requesting no oplock. Having two open file handles should break our own + * oplock during BRL acquisition. + */ +static bool test_raw_oplock_brl1(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + uint16_t fnum=0; + uint16_t fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + if (smbcli_write(cli1->tree, fnum, 0, buf, 0, sizeof(buf)) != + sizeof(buf)) + { + torture_comment(tctx, "Failed to create file\n"); + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + status = smb_raw_open(cli2->tree, tctx, &io); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.fnum, fnum); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + status = smbcli_lock(cli1->tree, fnum, 0, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + status = smbcli_lock(cli1->tree, fnum, 2, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.fnum, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; + +} + +/* Open a file with a batch oplock on one client and then acquire a brl. + * We should not contend our own oplock. + */ +static bool test_raw_oplock_brl2(struct torture_context *tctx, struct smbcli_state *cli1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + uint16_t fnum=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + if (smbcli_write(cli1->tree, fnum, 0, buf, 0, sizeof(buf)) != + sizeof(buf)) + { + torture_comment(tctx, "Failed to create file\n"); + goto done; + } + + torture_comment(tctx, "a self BRL acquisition should not break to " + "none\n"); + + status = smbcli_lock(cli1->tree, fnum, 0, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + status = smbcli_lock(cli1->tree, fnum, 2, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_LOCK_NOT_GRANTED); + + /* With one file handle open a BRL should not contend our oplock. + * Thus, no oplock break will be received and the entire break_info + * struct will be 0 */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.fnum, 0); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + +done: + smb_raw_exit(cli1->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock twice from one client and then acquire a + * brl. BRL acquisition should break our own oplock. + */ +static bool test_raw_oplock_brl3(struct torture_context *tctx, + struct smbcli_state *cli1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + uint16_t fnum=0; + uint16_t fnum2=0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + if (smbcli_write(cli1->tree, fnum, 0, buf, 0, sizeof(buf)) != + sizeof(buf)) + { + torture_comment(tctx, "Failed to create file\n"); + ret = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + status = smb_raw_open(cli1->tree, tctx, &io); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.fnum, fnum); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + status = smbcli_lock(cli1->tree, fnum, 0, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + status = smbcli_lock(cli1->tree, fnum, 2, 4, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.fnum, 0); + CHECK_VAL(break_info.failures, 0); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli1->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/* + * Open a file with an exclusive oplock from the 1st client and acquire a + * brl. Then open the same file from the 2nd client that should give oplock + * break with level2 to the 1st and return no oplock to the 2nd. + */ +static bool test_raw_oplock_brl4(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + uint16_t fnum = 0; + uint16_t fnum2 = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, + cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "open with exclusive oplock\n"); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + if (smbcli_write(cli1->tree, fnum, 0, buf, 0, sizeof(buf)) != + sizeof(buf)) + { + torture_comment(tctx, "Failed to create file\n"); + goto done; + } + + status = smbcli_lock(cli1->tree, fnum, 0, 1, 0, WRITE_LOCK); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + torture_comment(tctx, "a 2nd open should give a break to the 1st\n"); + ZERO_STRUCT(break_info); + + status = smb_raw_open(cli2->tree, tctx, &io); + + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.fnum, fnum); + + torture_comment(tctx, "and return no oplock to the 2nd\n"); + fnum2 = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/* + basic testing of oplocks +*/ +struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "oplock"); + + torture_suite_add_2smb_test(suite, "exclusive1", test_raw_oplock_exclusive1); + torture_suite_add_2smb_test(suite, "exclusive2", test_raw_oplock_exclusive2); + torture_suite_add_2smb_test(suite, "exclusive3", test_raw_oplock_exclusive3); + torture_suite_add_2smb_test(suite, "exclusive4", test_raw_oplock_exclusive4); + torture_suite_add_2smb_test(suite, "exclusive5", test_raw_oplock_exclusive5); + torture_suite_add_2smb_test(suite, "exclusive6", test_raw_oplock_exclusive6); + torture_suite_add_2smb_test(suite, "exclusive7", test_raw_oplock_exclusive7); + torture_suite_add_2smb_test(suite, "exclusive8", + test_raw_oplock_exclusive8); + torture_suite_add_2smb_test(suite, "exclusive9", + test_raw_oplock_exclusive9); + torture_suite_add_2smb_test(suite, "level_ii_1", + test_raw_oplock_level_ii_1); + torture_suite_add_2smb_test(suite, "batch1", test_raw_oplock_batch1); + torture_suite_add_2smb_test(suite, "batch2", test_raw_oplock_batch2); + torture_suite_add_2smb_test(suite, "batch3", test_raw_oplock_batch3); + torture_suite_add_2smb_test(suite, "batch4", test_raw_oplock_batch4); + torture_suite_add_2smb_test(suite, "batch5", test_raw_oplock_batch5); + torture_suite_add_2smb_test(suite, "batch6", test_raw_oplock_batch6); + torture_suite_add_2smb_test(suite, "batch7", test_raw_oplock_batch7); + torture_suite_add_2smb_test(suite, "batch8", test_raw_oplock_batch8); + torture_suite_add_2smb_test(suite, "batch9", test_raw_oplock_batch9); + torture_suite_add_2smb_test(suite, "batch9a", test_raw_oplock_batch9a); + torture_suite_add_2smb_test(suite, "batch10", test_raw_oplock_batch10); + torture_suite_add_2smb_test(suite, "batch11", test_raw_oplock_batch11); + torture_suite_add_2smb_test(suite, "batch12", test_raw_oplock_batch12); + torture_suite_add_2smb_test(suite, "batch13", test_raw_oplock_batch13); + torture_suite_add_2smb_test(suite, "batch14", test_raw_oplock_batch14); + torture_suite_add_2smb_test(suite, "batch15", test_raw_oplock_batch15); + torture_suite_add_2smb_test(suite, "batch16", test_raw_oplock_batch16); + torture_suite_add_2smb_test(suite, "batch17", test_raw_oplock_batch17); + torture_suite_add_2smb_test(suite, "batch18", test_raw_oplock_batch18); + torture_suite_add_2smb_test(suite, "batch19", test_raw_oplock_batch19); + torture_suite_add_2smb_test(suite, "batch20", test_raw_oplock_batch20); + torture_suite_add_2smb_test(suite, "batch21", test_raw_oplock_batch21); + torture_suite_add_2smb_test(suite, "batch22", test_raw_oplock_batch22); + torture_suite_add_2smb_test(suite, "batch23", test_raw_oplock_batch23); + torture_suite_add_2smb_test(suite, "batch24", test_raw_oplock_batch24); + torture_suite_add_2smb_test(suite, "batch25", test_raw_oplock_batch25); + torture_suite_add_2smb_test(suite, "batch26", test_raw_oplock_batch26); + torture_suite_add_2smb_test(suite, "stream1", test_raw_oplock_stream1); + torture_suite_add_2smb_test(suite, "doc1", test_raw_oplock_doc); + torture_suite_add_2smb_test(suite, "brl1", test_raw_oplock_brl1); + torture_suite_add_1smb_test(suite, "brl2", test_raw_oplock_brl2); + torture_suite_add_1smb_test(suite, "brl3", test_raw_oplock_brl3); + torture_suite_add_2smb_test(suite, "brl4", test_raw_oplock_brl4); + + return suite; +} + +/* + stress testing of oplocks +*/ +bool torture_bench_oplock(struct torture_context *torture) +{ + struct smbcli_state **cli; + bool ret = true; + TALLOC_CTX *mem_ctx = talloc_new(torture); + int torture_nprocs = torture_setting_int(torture, "nprocs", 4); + int i, count=0; + int timelimit = torture_setting_int(torture, "timelimit", 10); + union smb_open io; + struct timeval tv; + + cli = talloc_array(mem_ctx, struct smbcli_state *, torture_nprocs); + + torture_comment(torture, "Opening %d connections\n", torture_nprocs); + for (i=0;iev)) { + return false; + } + talloc_steal(mem_ctx, cli[i]); + smbcli_oplock_handler(cli[i]->transport, oplock_handler_close, + cli[i]->tree); + } + + if (!torture_setup_dir(cli[0], BASEDIR)) { + ret = false; + goto done; + } + + io.ntcreatex.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR "\\test.dat"; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + tv = timeval_current(); + + /* + we open the same file with SHARE_ACCESS_NONE from all the + connections in a round robin fashion. Each open causes an + oplock break on the previous connection, which is answered + by the oplock_handler_close() to close the file. + + This measures how fast we can pass on oplocks, and stresses + the oplock handling code + */ + torture_comment(torture, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + for (i=0;itree, mem_ctx, &io); + CHECK_STATUS(torture, status, NT_STATUS_OK); + count++; + } + + if (torture_setting_bool(torture, "progress", true)) { + torture_comment(torture, "%.2f ops/second\r", count/timeval_elapsed(&tv)); + } + } + + torture_comment(torture, "%.2f ops/second\n", count/timeval_elapsed(&tv)); + + smb_raw_exit(cli[torture_nprocs-1]->session); + +done: + smb_raw_exit(cli[0]->session); + smbcli_deltree(cli[0]->tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + + +static struct hold_oplock_info { + const char *fname; + bool close_on_break; + uint32_t share_access; + uint16_t fnum; +} hold_info[] = { + { + .fname = BASEDIR "\\notshared_close", + .close_on_break = true, + .share_access = NTCREATEX_SHARE_ACCESS_NONE, + }, + { + .fname = BASEDIR "\\notshared_noclose", + .close_on_break = false, + .share_access = NTCREATEX_SHARE_ACCESS_NONE, + }, + { + .fname = BASEDIR "\\shared_close", + .close_on_break = true, + .share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + }, + { + .fname = BASEDIR "\\shared_noclose", + .close_on_break = false, + .share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + }, +}; + +static bool oplock_handler_hold(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, uint8_t level, + void *private_data) +{ + struct smbcli_tree *tree = (struct smbcli_tree *)private_data; + struct hold_oplock_info *info; + int i; + + for (i=0;iclose_on_break) { + printf("oplock break on %s - closing\n", + info->fname); + oplock_handler_close(transport, tid, fnum, level, private_data); + return true; + } + + printf("oplock break on %s - acking break\n", info->fname); + + return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE); +} + + +/* + used for manual testing of oplocks - especially interaction with + other filesystems (such as NFS and local access) +*/ +bool torture_hold_oplock(struct torture_context *torture, + struct smbcli_state *cli) +{ + struct tevent_context *ev = torture->ev; + int i; + + printf("Setting up open files with oplocks in %s\n", BASEDIR); + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to set up test directory: " BASEDIR); + + smbcli_oplock_handler(cli->transport, oplock_handler_hold, cli->tree); + + /* setup the files */ + for (i=0;itree, cli, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open %s - %s\n", + hold_info[i].fname, nt_errstr(status)); + return false; + } + + if (io.ntcreatex.out.oplock_level != BATCH_OPLOCK_RETURN) { + printf("Oplock not granted for %s - expected %d but got %d\n", + hold_info[i].fname, BATCH_OPLOCK_RETURN, + io.ntcreatex.out.oplock_level); + return false; + } + hold_info[i].fnum = io.ntcreatex.out.file.fnum; + + /* make the file non-zero size */ + if (smbcli_write(cli->tree, hold_info[i].fnum, 0, &c, 0, 1) != 1) { + printf("Failed to write to file\n"); + return false; + } + } + + printf("Waiting for oplock events\n"); + tevent_loop_wait(ev); + + return true; +} diff --git a/source4/torture/raw/pingpong.c b/source4/torture/raw/pingpong.c new file mode 100644 index 0000000..61f1d6b --- /dev/null +++ b/source4/torture/raw/pingpong.c @@ -0,0 +1,248 @@ +/* + Unix SMB/CIFS implementation. + + ping pong test + + Copyright (C) Ronnie Sahlberg 2007 + + Significantly based on and borrowed from lockbench.c by + Copyright (C) Andrew Tridgell 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + filename is specified by + --option=torture:filename=... + + number of locks is specified by + --option=torture:num_locks=... + + locktimeout is specified in ms by + --option=torture:locktimeout=... + + default is 100 seconds + if set to 0 pingpong will instead loop trying the lock operation + over and over until it completes. + + reading from the file can be enabled with + --option=torture:read=true + + writing to the file can be enabled with + --option=torture:write=true + +*/ +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +static void lock_byte(struct smbcli_state *cli, int fd, int offset, int lock_timeout) +{ + union smb_lock io; + struct smb_lock_entry lock; + NTSTATUS status; + +try_again: + ZERO_STRUCT(lock); + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + + lock.count = 1; + lock.offset = offset; + lock.pid = cli->tree->session->pid; + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = lock_timeout; + io.lockx.in.locks = &lock; + io.lockx.in.file.fnum = fd; + + status = smb_raw_lock(cli->tree, &io); + + /* If we don't use timeouts and we got file lock conflict + just try the lock again. + */ + if (lock_timeout==0) { + if ( (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) + ||(NT_STATUS_EQUAL(NT_STATUS_LOCK_NOT_GRANTED, status)) ) { + goto try_again; + } + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Lock failed\n")); + exit(1); + } +} + +static void unlock_byte(struct smbcli_state *cli, int fd, int offset) +{ + union smb_lock io; + struct smb_lock_entry lock; + NTSTATUS status; + + ZERO_STRUCT(lock); + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + + lock.count = 1; + lock.offset = offset; + lock.pid = cli->tree->session->pid; + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 100000; + io.lockx.in.locks = &lock; + io.lockx.in.file.fnum = fd; + + status = smb_raw_lock(cli->tree, &io); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unlock failed\n")); + exit(1); + } +} + +static void write_byte(struct smbcli_state *cli, int fd, uint8_t c, int offset) +{ + union smb_write io; + NTSTATUS status; + + io.generic.level = RAW_WRITE_WRITEX; + io.writex.in.file.fnum = fd; + io.writex.in.offset = offset; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.count = 1; + io.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("write failed\n"); + exit(1); + } +} + +static void read_byte(struct smbcli_state *cli, int fd, uint8_t *c, int offset) +{ + union smb_read io; + NTSTATUS status; + + io.generic.level = RAW_READ_READX; + io.readx.in.file.fnum = fd; + io.readx.in.mincnt = 1; + io.readx.in.maxcnt = 1; + io.readx.in.offset = offset; + io.readx.in.remaining = 0; + io.readx.in.read_for_execute = false; + io.readx.out.data = c; + + status = smb_raw_read(cli->tree, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("read failed\n"); + exit(1); + } +} + +/* + ping pong +*/ +bool torture_ping_pong(struct torture_context *torture) +{ + const char *fn; + int num_locks; + TALLOC_CTX *mem_ctx = talloc_new(torture); + static bool do_reads; + static bool do_writes; + int lock_timeout; + int fd; + struct smbcli_state *cli; + int i; + uint8_t incr=0, last_incr=0; + uint8_t *val; + int count, loops; + struct timeval start; + + fn = torture_setting_string(torture, "filename", NULL); + if (fn == NULL) { + DEBUG(0,("You must specify the filename using --option=torture:filename=...\n")); + return false; + } + + num_locks = torture_setting_int(torture, "num_locks", -1); + if (num_locks == -1) { + DEBUG(0,("You must specify num_locks using --option=torture:num_locks=...\n")); + return false; + } + + do_reads = torture_setting_bool(torture, "read", false); + do_writes = torture_setting_bool(torture, "write", false); + lock_timeout = torture_setting_int(torture, "lock_timeout", 100000); + + if (!torture_open_connection(&cli, torture, 0)) { + DEBUG(0,("Could not open connection\n")); + return false; + } + + fd = smbcli_open(cli->tree, fn, O_RDWR|O_CREAT, DENY_NONE); + if (fd == -1) { + printf("Failed to open %s\n", fn); + exit(1); + } + + write_byte(cli, fd, 0, num_locks); + lock_byte(cli, fd, 0, lock_timeout); + + + start = timeval_current(); + val = talloc_zero_array(mem_ctx, uint8_t, num_locks); + i = 0; + count = 0; + loops = 0; + while (1) { + lock_byte(cli, fd, (i+1)%num_locks, lock_timeout); + + if (do_reads) { + uint8_t c; + read_byte(cli, fd, &c, i); + incr = c-val[i]; + val[i] = c; + } + + if (do_writes) { + uint8_t c = val[i] + 1; + write_byte(cli, fd, c, i); + } + + unlock_byte(cli, fd, i); + + i = (i+1)%num_locks; + count++; + if (loops>num_locks && incr!=last_incr) { + last_incr = incr; + printf("data increment = %u\n", incr); + fflush(stdout); + } + if (timeval_elapsed(&start) > 1.0) { + printf("%8u locks/sec\r", + (unsigned)(2*count/timeval_elapsed(&start))); + fflush(stdout); + start = timeval_current(); + count=0; + } + loops++; + } +} + diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c new file mode 100644 index 0000000..1d29281 --- /dev/null +++ b/source4/torture/raw/qfileinfo.c @@ -0,0 +1,1084 @@ +/* + Unix SMB/CIFS implementation. + RAW_FILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "torture/raw/proto.h" + +static struct { + const char *name; + enum smb_fileinfo_level level; + unsigned int only_paths:1; + unsigned int only_handles:1; + uint32_t capability_mask; + unsigned int expected_ipc_access_denied:1; + NTSTATUS expected_ipc_fnum_status; + NTSTATUS fnum_status, fname_status; + union smb_fileinfo fnum_finfo, fname_finfo; +} levels[] = { + { + .name = "GETATTR", + .level = RAW_FILEINFO_GETATTR, + .only_paths = 1, + .only_handles = 0, + .expected_ipc_access_denied = 1, + }, + { + .name ="GETATTRE", + .level = RAW_FILEINFO_GETATTRE, + .only_paths = 0, + .only_handles = 1, + }, + { + .name ="STANDARD", + .level = RAW_FILEINFO_STANDARD, + }, + { + .name ="EA_SIZE", + .level = RAW_FILEINFO_EA_SIZE, + }, + { + .name ="ALL_EAS", + .level = RAW_FILEINFO_ALL_EAS, + .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="IS_NAME_VALID", + .level = RAW_FILEINFO_IS_NAME_VALID, + .only_paths = 1, + .only_handles = 0, + }, + { + .name ="BASIC_INFO", + .level = RAW_FILEINFO_BASIC_INFO, + }, + { + .name ="STANDARD_INFO", + .level = RAW_FILEINFO_STANDARD_INFO, + }, + { + .name ="EA_INFO", + .level = RAW_FILEINFO_EA_INFO, + }, + { + .name ="NAME_INFO", + .level = RAW_FILEINFO_NAME_INFO, + }, + { + .name ="ALL_INFO", + .level = RAW_FILEINFO_ALL_INFO, + }, + { + .name ="ALT_NAME_INFO", + .level = RAW_FILEINFO_ALT_NAME_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="STREAM_INFO", + .level = RAW_FILEINFO_STREAM_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="COMPRESSION_INFO", + .level = RAW_FILEINFO_COMPRESSION_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="UNIX_BASIC_INFO", + .level = RAW_FILEINFO_UNIX_BASIC, + .only_paths = 0, + .only_handles = 0, + .capability_mask = CAP_UNIX, + }, + { + .name ="UNIX_LINK_INFO", + .level = RAW_FILEINFO_UNIX_LINK, + .only_paths = 0, + .only_handles = 0, + .capability_mask = CAP_UNIX, + }, + { + .name ="BASIC_INFORMATION", + .level = RAW_FILEINFO_BASIC_INFORMATION, + }, + { + .name ="STANDARD_INFORMATION", + .level = RAW_FILEINFO_STANDARD_INFORMATION, + }, + { + .name ="INTERNAL_INFORMATION", + .level = RAW_FILEINFO_INTERNAL_INFORMATION, + }, + { + .name ="EA_INFORMATION", + .level = RAW_FILEINFO_EA_INFORMATION, + }, + { + .name = "ACCESS_INFORMATION", + .level = RAW_FILEINFO_ACCESS_INFORMATION, + }, + { + .name = "NAME_INFORMATION", + .level = RAW_FILEINFO_NAME_INFORMATION, + }, + { + .name ="POSITION_INFORMATION", + .level = RAW_FILEINFO_POSITION_INFORMATION, + }, + { + .name ="MODE_INFORMATION", + .level = RAW_FILEINFO_MODE_INFORMATION, + }, + { + .name ="ALIGNMENT_INFORMATION", + .level = RAW_FILEINFO_ALIGNMENT_INFORMATION, + }, + { + .name ="ALL_INFORMATION", + .level = RAW_FILEINFO_ALL_INFORMATION, + }, + { + .name ="ALT_NAME_INFORMATION", + .level = RAW_FILEINFO_ALT_NAME_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="STREAM_INFORMATION", + .level = RAW_FILEINFO_STREAM_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name = "COMPRESSION_INFORMATION", + .level = RAW_FILEINFO_COMPRESSION_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="NETWORK_OPEN_INFORMATION", + .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name = "ATTRIBUTE_TAG_INFORMATION", + .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { .name = NULL, }, +}; + +/* + compare a dos time (2 second resolution) to a nt time +*/ +static int dos_nt_time_cmp(time_t t, NTTIME nt) +{ + time_t t2 = nt_time_to_unix(nt); + if (labs(t2 - t) <= 2) return 0; + return t2 > t ? 1 : -1; +} + + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fnum_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fnum_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_paths) { + return &levels[i].fnum_finfo; + } + } + return NULL; +} + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fname_find(bool is_ipc, const char *name) +{ + int i; + if (is_ipc) { + return NULL; + } + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fname_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_handles) { + return &levels[i].fname_finfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (unsigned int)s1->n1.out.v1, \ + #n2, #v2, (unsigned int)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \ + s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \ + printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \ + #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#if 0 /* unused */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (unsigned int)s1->n1.out.v1, \ + (unsigned int)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) +#endif + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +static bool torture_raw_qfileinfo_internals(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smbcli_tree *tree, + int fnum, const char *fname, + bool is_ipc) +{ + size_t i; + bool ret = true; + size_t count; + union smb_fileinfo *s1, *s2; + NTTIME correct_time; + uint64_t correct_size; + uint32_t correct_attrib; + const char *correct_name; + bool skip_streams = false; + + /* scan all the fileinfo and pathinfo levels */ + for (i=0; levels[i].name; i++) { + if (!levels[i].only_paths) { + levels[i].fnum_finfo.generic.level = levels[i].level; + levels[i].fnum_finfo.generic.in.file.fnum = fnum; + levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx, + &levels[i].fnum_finfo); + } + + if (!levels[i].only_handles) { + levels[i].fname_finfo.generic.level = levels[i].level; + levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname); + levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx, + &levels[i].fname_finfo); + } + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32_t cap = tree->session->transport->negotiate.capabilities; + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + if (is_ipc) { + if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) { + } else if (!levels[i].only_handles && + NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_SUPPORTED)) { + torture_warning(torture, "fname level %s %s", + levels[i].name, + nt_errstr(levels[i].fname_status)); + continue; + } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) { + printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + if (!levels[i].only_paths && + (NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fnum level %s %s", + levels[i].name, + nt_errstr(levels[i].fnum_status)); + continue; + } + if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) { + printf("ERROR: fnum level %s failed, expected %s - %s\n", + levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status), + nt_errstr(levels[i].fnum_status)); + count++; + } + } else { + if (!levels[i].only_paths && + (NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fnum level %s %s", + levels[i].name, + nt_errstr(levels[i].fnum_status)); + continue; + } + + if (!levels[i].only_handles && + (NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fname level %s %s", + levels[i].name, + nt_errstr(levels[i].fname_status)); + continue; + } + + if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) { + printf("ERROR: fnum level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fnum_status)); + count++; + } + if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) { + printf("ERROR: fname level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + } + + } + + if (count != 0) { + ret = false; + printf("%zu levels failed\n", count); + if (count > 35) { + torture_fail(torture, "too many level failures - giving up"); + } + } + + /* see if we can do streams */ + s1 = fnum_find("STREAM_INFO"); + if (!s1 || s1->stream_info.out.num_streams == 0) { + if (!is_ipc) { + printf("STREAM_INFO broken (%d) - skipping streams checks\n", + s1 ? s1->stream_info.out.num_streams : -1); + } + skip_streams = true; + } + + + /* this code is incredibly repititive but doesn't lend itself to loops, so + we use lots of macros to make it less painful */ + + /* first off we check the levels that are supposed to be aliases. It will be quite rare for + this code to fail, but we need to check it for completeness */ + + + +#define ALIAS_CHECK(sname1, sname2) \ + do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2) { INFO_CHECK } \ + } while (0) + +#define INFO_CHECK \ + STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \ + STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \ + STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \ + STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \ + VAL_EQUAL (basic_info, attrib, basic_info, attrib); + + ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \ + VAL_EQUAL(standard_info, size, standard_info, size); \ + VAL_EQUAL(standard_info, nlink, standard_info, nlink); \ + VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \ + VAL_EQUAL(standard_info, directory, standard_info, directory); + + ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(ea_info, ea_size, ea_info, ea_size); + + ALIAS_CHECK("EA_INFO", "EA_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(name_info, fname, name_info, fname); + + ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STRUCT_EQUAL(all_info, create_time, all_info, create_time); \ + STRUCT_EQUAL(all_info, access_time, all_info, access_time); \ + STRUCT_EQUAL(all_info, write_time, all_info, write_time); \ + STRUCT_EQUAL(all_info, change_time, all_info, change_time); \ + VAL_EQUAL(all_info, attrib, all_info, attrib); \ + VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \ + VAL_EQUAL(all_info, size, all_info, size); \ + VAL_EQUAL(all_info, nlink, all_info, nlink); \ + VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \ + VAL_EQUAL(all_info, directory, all_info, directory); \ + VAL_EQUAL(all_info, ea_size, all_info, ea_size); \ + STR_EQUAL(all_info, fname, all_info, fname); + + ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \ + VAL_EQUAL(compression_info, format, compression_info, format); \ + VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \ + VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \ + VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift); + + ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION"); + + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(alt_name_info, fname, alt_name_info, fname); + + ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION"); + + +#define TIME_CHECK_NT(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) + +#define TIME_CHECK_DOS(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) + +#if 0 /* unused */ +#define TIME_CHECK_UNX(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) +#endif + + /* now check that all the times that are supposed to be equal are correct */ + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.create_time; + torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time); + TIME_CHECK_DOS("GETATTRE", getattre, create_time); + TIME_CHECK_DOS("STANDARD", standard, create_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, create_time); + TIME_CHECK_NT ("ALL_INFO", all_info, create_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.access_time; + torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time); + TIME_CHECK_DOS("GETATTRE", getattre, access_time); + TIME_CHECK_DOS("STANDARD", standard, access_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, access_time); + TIME_CHECK_NT ("ALL_INFO", all_info, access_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.write_time; + torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time); + TIME_CHECK_DOS("GETATTR", getattr, write_time); + TIME_CHECK_DOS("GETATTRE", getattre, write_time); + TIME_CHECK_DOS("STANDARD", standard, write_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, write_time); + TIME_CHECK_NT ("ALL_INFO", all_info, write_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.change_time; + torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time); + TIME_CHECK_NT ("ALL_INFO", all_info, change_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time); + + +#define SIZE_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_size); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_size); \ + ret = false; \ + }} while (0) + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.size; + torture_comment(torture, "size: %u\n", (unsigned int)correct_size); + + SIZE_CHECK("GETATTR", getattr, size); + SIZE_CHECK("GETATTRE", getattre, size); + SIZE_CHECK("STANDARD", standard, size); + SIZE_CHECK("EA_SIZE", ea_size, size); + SIZE_CHECK("STANDARD_INFO", standard_info, size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, size); + SIZE_CHECK("ALL_INFO", all_info, size); + SIZE_CHECK("ALL_INFORMATION", all_info, size); + SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size); + SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size); + } + + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.alloc_size; + torture_comment(torture, "alloc_size: %u\n", (unsigned int)correct_size); + + SIZE_CHECK("GETATTRE", getattre, alloc_size); + SIZE_CHECK("STANDARD", standard, alloc_size); + SIZE_CHECK("EA_SIZE", ea_size, alloc_size); + SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size); + SIZE_CHECK("ALL_INFO", all_info, alloc_size); + SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size); + } + +#define ATTRIB_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_attrib); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_attrib); \ + ret = false; \ + }} while (0) + + s1 = fnum_find("BASIC_INFO"); + correct_attrib = s1->basic_info.out.attrib; + torture_comment(torture, "attrib: 0x%x\n", (unsigned int)correct_attrib); + + ATTRIB_CHECK("GETATTR", getattr, attrib); + if (!is_ipc) { + ATTRIB_CHECK("GETATTRE", getattre, attrib); + ATTRIB_CHECK("STANDARD", standard, attrib); + ATTRIB_CHECK("EA_SIZE", ea_size, attrib); + } + ATTRIB_CHECK("BASIC_INFO", basic_info, attrib); + ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib); + ATTRIB_CHECK("ALL_INFO", all_info, attrib); + ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib); + ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib); + ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + + correct_name = fname; + torture_comment(torture, "name: %s\n", correct_name); + +#define NAME_CHECK(sname, stype, tfield, flags) do { \ + s1 = fnum_find(sname); \ + if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \ + printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \ + printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = false; \ + }} while (0) + + NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE); + NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE); + + /* the ALL_INFO file name is the full path on the filesystem */ + s1 = fnum_find("ALL_INFO"); + if (s1 && !s1->all_info.out.fname.s) { + torture_fail(torture, "ALL_INFO didn't give a filename"); + } + if (s1 && s1->all_info.out.fname.s) { + char *p = strrchr(s1->all_info.out.fname.s, '\\'); + if (!p) { + printf("Not a full path in all_info/fname? - '%s'\n", + s1->all_info.out.fname.s); + ret = false; + } else { + if (strcmp_safe(correct_name, p) != 0) { + printf("incorrect basename in all_info/fname - '%s'\n", + s1->all_info.out.fname.s); + ret = false; + } + } + if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) { + printf("Should not null terminate all_info/fname\n"); + ret = false; + } + } + + s1 = fnum_find("ALT_NAME_INFO"); + if (s1) { + correct_name = s1->alt_name_info.out.fname.s; + } + + if (!correct_name) { + torture_comment(torture, "no alternate name information\n"); + } else { + torture_comment(torture, "alt_name: %s\n", correct_name); + + NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE); + NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE); + + /* and make sure we can open by alternate name */ + smbcli_close(tree, fnum); + fnum = smbcli_nt_create_full(tree, correct_name, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + if (fnum == -1) { + printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree)); + ret = false; + } + + if (!skip_streams) { + correct_name = "::$DATA"; + torture_comment(torture, "stream_name: %s\n", correct_name); + + NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE); + NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE); + } + } + + /* make sure the EAs look right */ + s1 = fnum_find("ALL_EAS"); + s2 = fnum_find("ALL_INFO"); + if (s1) { + for (i=0;iall_eas.out.num_eas;i++) { + printf(" flags=%d %s=%*.*s\n", + s1->all_eas.out.eas[i].flags, + s1->all_eas.out.eas[i].name.s, + (int)s1->all_eas.out.eas[i].value.length, + (int)s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.data); + } + } + if (s1 && s2) { + if (s1->all_eas.out.num_eas == 0) { + if (s2->all_info.out.ea_size != 0) { + printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n", + s2->all_info.out.ea_size); + } + } else { + if (s2->all_info.out.ea_size != + ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) { + printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n", + (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas), + (int)s2->all_info.out.ea_size); + } + } + } + s2 = fname_find(is_ipc, "ALL_EAS"); + if (s2) { + VAL_EQUAL(all_eas, num_eas, all_eas, num_eas); + for (i=0;iall_eas.out.num_eas;i++) { + VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags); + STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name); + VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length); + } + } + +#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + }} while (0) + + VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, + "ALL_INFO", all_info, delete_pending); + VAL_CHECK("STANDARD_INFO", standard_info, directory, + "ALL_INFO", all_info, directory); + VAL_CHECK("STANDARD_INFO", standard_info, nlink, + "ALL_INFO", all_info, nlink); + s1 = fnum_find("BASIC_INFO"); + if (s1 && is_ipc) { + if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) { + printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, (int)FILE_ATTRIBUTE_NORMAL); + ret = false; + } + } + s1 = fnum_find("STANDARD_INFO"); + if (s1 && is_ipc) { + if (s1->standard_info.out.nlink != 1) { + printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink); + ret = false; + } + if (s1->standard_info.out.delete_pending != 1) { + printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending); + ret = false; + } + } + VAL_CHECK("EA_INFO", ea_info, ea_size, + "ALL_INFO", all_info, ea_size); + if (!is_ipc) { + VAL_CHECK("EA_SIZE", ea_size, ea_size, + "ALL_INFO", all_info, ea_size); + } + +#define NAME_PATH_CHECK(sname, stype, field) do { \ + s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \ + if (s1 && s2) { \ + VAL_EQUAL(stype, field, stype, field); \ + } \ +} while (0) + + + s1 = fnum_find("INTERNAL_INFORMATION"); + if (s1) { + torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id); + } + + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id); + NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position); + if (s1 && s2) { + printf("fnum pos = %.0f, fname pos = %.0f\n", + (double)s2->position_information.out.position, + (double)s1->position_information.out.position ); + } + NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode); + NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag); + +#if 0 + /* these are expected to differ */ + NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags); +#endif + +#if 0 /* unused */ +#define UNKNOWN_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield); \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield); \ + }} while (0) +#endif + /* now get a bit fancier .... */ + + /* when we set the delete disposition then the link count should drop + to 0 and delete_pending should be 1 */ + + return ret; +} + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +bool torture_raw_qfileinfo(struct torture_context *torture, + struct smbcli_state *cli) +{ + int fnum; + bool ret; + const char *fname = "\\torture_qfileinfo.txt"; + + fnum = create_complex_file(cli, torture, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + + ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + return ret; +} + +bool torture_raw_qfileinfo_pipe(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + int fnum; + const char *fname = "\\lsass"; + union smb_open op; + NTSTATUS status; + + op.ntcreatex.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_WRITE_EA | + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + op.ntcreatex.in.file_attr = 0; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.impersonation = + NTCREATEX_IMPERSONATION_IMPERSONATION; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, torture, &op); + torture_assert_ntstatus_ok(torture, status, "smb_raw_open failed"); + + fnum = op.ntcreatex.out.file.fnum; + + ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, + fnum, fname, + true /* is_ipc */); + + smbcli_close(cli->tree, fnum); + return ret; +} diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c new file mode 100644 index 0000000..6be6c42 --- /dev/null +++ b/source4/torture/raw/qfsinfo.c @@ -0,0 +1,340 @@ +/* + Unix SMB/CIFS implementation. + RAW_QFS_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/basic/proto.h" +#include "torture/raw/proto.h" + + +static struct { + const char *name; + enum smb_fsinfo_level level; + uint32_t capability_mask; + NTSTATUS status; + union smb_fsinfo fsinfo; +} levels[] = { + { + .name = "DSKATTR", + .level = RAW_QFS_DSKATTR, + }, + { + .name = "ALLOCATION", + .level = RAW_QFS_ALLOCATION, + }, + { + .name = "VOLUME", + .level = RAW_QFS_VOLUME, + }, + { + .name = "VOLUME_INFO", + .level = RAW_QFS_VOLUME_INFO, + }, + { + .name = "SIZE_INFO", + .level = RAW_QFS_SIZE_INFO, + }, + { + .name = "DEVICE_INFO", + .level = RAW_QFS_DEVICE_INFO, + }, + { + .name = "ATTRIBUTE_INFO", + .level = RAW_QFS_ATTRIBUTE_INFO, + }, + { + .name = "UNIX_INFO", + .level = RAW_QFS_UNIX_INFO, + .capability_mask = CAP_UNIX, + }, + { + .name = "VOLUME_INFORMATION", + .level = RAW_QFS_VOLUME_INFORMATION, + }, + { + .name = "SIZE_INFORMATION", + .level = RAW_QFS_SIZE_INFORMATION, + }, + { + .name = "DEVICE_INFORMATION", + .level = RAW_QFS_DEVICE_INFORMATION, + }, + { + .name = "ATTRIBUTE_INFORMATION", + .level = RAW_QFS_ATTRIBUTE_INFORMATION, + }, + { + .name = "QUOTA_INFORMATION", + .level = RAW_QFS_QUOTA_INFORMATION, + }, + { + .name = "FULL_SIZE_INFORMATION", + .level = RAW_QFS_FULL_SIZE_INFORMATION, + }, +#if 0 + /* w2k3 seems to no longer support this */ + {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, }, +#endif + { .name = NULL, }, +}; + + +/* + find a level in the levels[] table +*/ +static union smb_fsinfo *find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (strcmp(name, levels[i].name) == 0 && + NT_STATUS_IS_OK(levels[i].status)) { + return &levels[i].fsinfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (unsigned int)s1->n1.out.v1, \ + #n2, #v2, (unsigned int)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define VAL_APPROX_EQUAL(n1, v1, n2, v2) do {if (abs((int)(s1->n1.out.v1) - (int)(s2->n2.out.v2)) > 0.1*s1->n1.out.v1) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (unsigned int)s1->n1.out.v1, \ + #n2, #v2, (unsigned int)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do { \ + if (strcmp_safe(s1->n1.out.v1, s2->n2.out.v2)) { \ + printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1, \ + #n2, #v2, s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (unsigned int)s1->n1.out.v1, \ + (unsigned int)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +/* basic testing of all RAW_QFS_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. + + Some of the consistency tests assume that the target filesystem is + quiescent, which is sometimes hard to achieve +*/ +bool torture_raw_qfsinfo(struct torture_context *torture, + struct smbcli_state *cli) +{ + size_t i; + bool ret = true; + size_t count; + union smb_fsinfo *s1, *s2; + + /* scan all the levels, pulling the results */ + for (i=0; levels[i].name; i++) { + torture_comment(torture, "Running level %s\n", levels[i].name); + levels[i].fsinfo.generic.level = levels[i].level; + levels[i].status = smb_raw_fsinfo(cli->tree, torture, &levels[i].fsinfo); + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32_t cap = cli->transport->negotiate.capabilities; + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + if (!NT_STATUS_IS_OK(levels[i].status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].status)); + count++; + } + } + + if (count != 0) { + torture_comment(torture, "%zu levels failed\n", count); + torture_assert(torture, count > 13, "too many level failures - giving up"); + } + + torture_comment(torture, "check for correct aliases\n"); + s1 = find("SIZE_INFO"); + s2 = find("SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units); + VAL_APPROX_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, size_info, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, size_info, bytes_per_sector); + } + + s1 = find("DEVICE_INFO"); + s2 = find("DEVICE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(device_info, device_type, device_info, device_type); + VAL_EQUAL(device_info, characteristics, device_info, characteristics); + } + + s1 = find("VOLUME_INFO"); + s2 = find("VOLUME_INFORMATION"); + if (s1 && s2) { + STRUCT_EQUAL(volume_info, create_time, volume_info, create_time); + VAL_EQUAL (volume_info, serial_number, volume_info, serial_number); + STR_EQUAL (volume_info, volume_name.s, volume_info, volume_name.s); + torture_comment(torture, "volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s); + } + + s1 = find("ATTRIBUTE_INFO"); + s2 = find("ATTRIBUTE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(attribute_info, fs_attr, + attribute_info, fs_attr); + VAL_EQUAL(attribute_info, max_file_component_length, + attribute_info, max_file_component_length); + STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s); + torture_comment(torture, "attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s); + } + + torture_comment(torture, "check for consistent disk sizes\n"); + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_total * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.total_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (fabs(size1 - size2) > 1) { + printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = false; + } + torture_comment(torture, "total disk = %.0f MB\n", size1*scale/1.0e6); + } + + torture_comment(torture, "check consistent free disk space\n"); + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_free * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.avail_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (fabs(size1 - size2) > 1) { + printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = false; + } + torture_comment(torture, "free disk = %.0f MB\n", size1*scale/1.0e6); + } + + torture_comment(torture, "volume info consistency\n"); + s1 = find("VOLUME"); + s2 = find("VOLUME_INFO"); + if (s1 && s2) { + VAL_EQUAL(volume, serial_number, volume_info, serial_number); + STR_EQUAL(volume, volume_name.s, volume_info, volume_name.s); + } + + /* disk size consistency - notice that 'avail_alloc_units' maps to the caller + available allocation units, not the total */ + s1 = find("SIZE_INFO"); + s2 = find("FULL_SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units); + VAL_APPROX_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, full_size_information, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, full_size_information, bytes_per_sector); + } + + printf("check for non-zero unknown fields\n"); + s1 = find("QUOTA_INFORMATION"); + if (s1) { + VAL_UNKNOWN(quota_information, unknown[0]); + VAL_UNKNOWN(quota_information, unknown[1]); + VAL_UNKNOWN(quota_information, unknown[2]); + } + + s1 = find("OBJECTID_INFORMATION"); + if (s1) { + VAL_UNKNOWN(objectid_information, unknown[0]); + VAL_UNKNOWN(objectid_information, unknown[1]); + VAL_UNKNOWN(objectid_information, unknown[2]); + VAL_UNKNOWN(objectid_information, unknown[3]); + VAL_UNKNOWN(objectid_information, unknown[4]); + VAL_UNKNOWN(objectid_information, unknown[5]); + } + + +#define STR_CHECK(sname, stype, field, flags) do { \ + s1 = find(sname); \ + if (s1) { \ + if (s1->stype.out.field.s && wire_bad_flags(&s1->stype.out.field, flags, cli->transport)) { \ + printf("(%d) incorrect string termination in %s/%s\n", \ + __LINE__, #stype, #field); \ + ret = false; \ + } \ + }} while (0) + + torture_comment(torture, "check for correct termination\n"); + + STR_CHECK("VOLUME", volume, volume_name, 0); + STR_CHECK("VOLUME_INFO", volume_info, volume_name, STR_UNICODE); + STR_CHECK("VOLUME_INFORMATION", volume_info, volume_name, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFO", attribute_info, fs_type, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE); + + return ret; +} diff --git a/source4/torture/raw/raw.c b/source4/torture/raw/raw.c new file mode 100644 index 0000000..b3716b6 --- /dev/null +++ b/source4/torture/raw/raw.c @@ -0,0 +1,87 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "torture/util.h" +#include "torture/smbtorture.h" +#include "torture/raw/proto.h" + +NTSTATUS torture_raw_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "raw"); + /* RAW smb tests */ + torture_suite_add_simple_test(suite, "bench-oplock", torture_bench_oplock); + torture_suite_add_simple_test(suite, "ping-pong", torture_ping_pong); + torture_suite_add_simple_test(suite, "bench-lock", torture_bench_lock); + torture_suite_add_simple_test(suite, "bench-open", torture_bench_open); + torture_suite_add_simple_test(suite, "bench-lookup", + torture_bench_lookup); + torture_suite_add_simple_test(suite, "bench-tcon", + torture_bench_treeconnect); + torture_suite_add_simple_test(suite, "offline", torture_test_offline); + torture_suite_add_1smb_test(suite, "qfsinfo", torture_raw_qfsinfo); + torture_suite_add_1smb_test(suite, "qfileinfo", torture_raw_qfileinfo); + torture_suite_add_1smb_test(suite, "qfileinfo.ipc", torture_raw_qfileinfo_pipe); + torture_suite_add_suite(suite, torture_raw_sfileinfo(suite)); + torture_suite_add_suite(suite, torture_raw_search(suite)); + torture_suite_add_1smb_test(suite, "close", torture_raw_close); + torture_suite_add_suite(suite, torture_raw_open(suite)); + torture_suite_add_1smb_test(suite, "mkdir", torture_raw_mkdir); + torture_suite_add_suite(suite, torture_raw_oplock(suite)); + torture_suite_add_1smb_test(suite, "hold-oplock", torture_hold_oplock); + torture_suite_add_suite(suite, torture_raw_notify(suite)); + torture_suite_add_1smb_test(suite, "mux", torture_raw_mux); + torture_suite_add_1smb_test(suite, "ioctl", torture_raw_ioctl); + torture_suite_add_1smb_test(suite, "chkpath", torture_raw_chkpath); + torture_suite_add_suite(suite, torture_raw_unlink(suite)); + torture_suite_add_suite(suite, torture_raw_read(suite)); + torture_suite_add_suite(suite, torture_raw_write(suite)); + torture_suite_add_suite(suite, torture_raw_lock(suite)); + torture_suite_add_suite(suite, torture_raw_context(suite)); + torture_suite_add_suite(suite, torture_raw_session(suite)); + torture_suite_add_suite(suite, torture_raw_rename(suite)); + torture_suite_add_1smb_test(suite, "seek", torture_raw_seek); + torture_suite_add_1smb_test(suite, "eas", torture_raw_eas); + torture_suite_add_suite(suite, torture_raw_streams(suite)); + torture_suite_add_suite(suite, torture_raw_acls(suite)); + torture_suite_add_suite(suite, torture_raw_composite(suite)); + torture_suite_add_1smb_test(suite, "samba3hide", torture_samba3_hide); + torture_suite_add_1smb_test(suite, "samba3closeerr", torture_samba3_closeerr); + torture_suite_add_1smb_test(suite, "samba3rootdirfid", + torture_samba3_rootdirfid); + torture_suite_add_1smb_test(suite, "samba3rootdirfid2", + torture_samba3_rootdirfid2); + torture_suite_add_1smb_test(suite, "samba3checkfsp", torture_samba3_checkfsp); + torture_suite_add_1smb_test(suite, "samba3oplocklogoff", torture_samba3_oplock_logoff); + torture_suite_add_1smb_test(suite, "samba3badnameblob", torture_samba3_check_openX_badname); + torture_suite_add_simple_test(suite, "samba3badpath", torture_samba3_badpath); + torture_suite_add_1smb_test(suite, "samba3caseinsensitive", + torture_samba3_caseinsensitive); + torture_suite_add_1smb_test(suite, "samba3posixtimedlock", + torture_samba3_posixtimedlock); + torture_suite_add_simple_test(suite, "scan-eamax", torture_max_eas); + + suite->description = talloc_strdup(suite, "Tests for the raw SMB interface"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c new file mode 100644 index 0000000..6160e3e --- /dev/null +++ b/source4/torture/raw/read.c @@ -0,0 +1,1039 @@ +/* + Unix SMB/CIFS implementation. + test suite for various read operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define CHECK_STATUS(status, correct) do { \ + torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, \ + done, "incorrect status"); \ + } while (0) + +#define CHECK_VALUE(v, correct) do { \ + torture_assert_int_equal_goto(tctx, (v), (correct), ret, done, \ + "Incorrect value"); \ + } while (0) + +#define CHECK_BUFFER(buf, seed, len) do { \ + if (!check_buffer(tctx, buf, seed, len, __LINE__)) { \ + ret = false; \ + torture_fail_goto(tctx, done, "buffer check failed\n"); \ + }} while (0) + +#define CHECK_READX_ALIGN(io) do { \ + if ((io.readx.out.flags2 & FLAGS2_UNICODE_STRINGS) && \ + (io.readx.out.data_offset % 2 != 0)) { \ + ret = false; \ + torture_fail_goto(tctx, done, "data not 16 bit aligned\n"); \ + }} while (0) + +#define BASEDIR "\\testread" + + +/* + setup a random buffer based on a seed +*/ +static void setup_buffer(uint8_t *buf, unsigned int seed, int len) +{ + int i; + srandom(seed); + for (i=0;itree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying empty file read\n"); + io.read.in.file.fnum = fnum; + io.read.in.count = 1; + io.read.in.offset = 0; + io.read.in.remaining = 0; + io.read.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + + printf("Trying zero file read\n"); + io.read.in.count = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + + printf("Trying bad fnum\n"); + io.read.in.file.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.read.in.file.fnum = fnum; + + smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.read.in.file.fnum = fnum; + io.read.in.offset = 0; + io.read.in.remaining = 0; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.read.in.offset = 1; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { + printf("Trying max offset\n"); + io.read.in.offset = ~0; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + } + + setup_buffer(buf, seed, maxsize); + smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.read.in.offset = 0; + io.read.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BUFFER(buf, seed, io.read.out.nread); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { + printf("Failed to lock file at %d\n", __LINE__); + ret = false; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.read.in.offset = 0; + io.read.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test lockread ops +*/ +static bool test_lockread(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_read io; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned int seed = time(NULL); + + if (!cli->transport->negotiate.lockread_supported) { + printf("Server does not support lockread - skipping\n"); + return true; + } + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing RAW_READ_LOCKREAD\n"); + io.generic.level = RAW_READ_LOCKREAD; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying empty file read\n"); + io.lockread.in.file.fnum = fnum; + io.lockread.in.count = 1; + io.lockread.in.offset = 1; + io.lockread.in.remaining = 0; + io.lockread.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, 0); + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + printf("Trying zero file read\n"); + io.lockread.in.count = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_unlock(cli->tree, fnum, 1, 1); + + printf("Trying bad fnum\n"); + io.lockread.in.file.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.lockread.in.file.fnum = fnum; + + smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.lockread.in.file.fnum = fnum; + io.lockread.in.offset = 0; + io.lockread.in.remaining = 0; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + smbcli_unlock(cli->tree, fnum, 1, 0); + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.lockread.in.offset = 1; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + smbcli_unlock(cli->tree, fnum, 0, strlen(test_data)); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VALUE(io.lockread.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { + printf("Trying max offset\n"); + io.lockread.in.offset = ~0; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, 0); + } + + setup_buffer(buf, seed, maxsize); + smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.lockread.in.offset = 0; + io.lockread.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + smbcli_unlock(cli->tree, fnum, 1, strlen(test_data)); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BUFFER(buf, seed, io.lockread.out.nread); + smbcli_unlock(cli->tree, fnum, 0, 0xFFFF); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { + printf("Failed to lock file at %d\n", __LINE__); + ret = false; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.lockread.in.offset = 0; + io.lockread.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test readx ops +*/ +static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_read io; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned int seed = time(NULL); + struct smbcli_request *smbreq = NULL; + unsigned int i; + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing RAW_READ_READX\n"); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READX; + io.readx.in.file.fnum = fnum; + io.readx.in.mincnt = 1; + io.readx.in.maxcnt = 1; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + io.readx.in.read_for_execute = false; + io.readx.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_READX_ALIGN(io); + + printf("Trying zero file read\n"); + io.readx.in.mincnt = 0; + io.readx.in.maxcnt = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_READX_ALIGN(io); + + printf("Trying bad fnum\n"); + io.readx.in.file.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.readx.in.file.fnum = fnum; + + smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Checking reserved fields are [0]\n"); + io.readx.in.file.fnum = fnum; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + io.readx.in.read_for_execute = false; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + smbreq = smb_raw_read_send(cli->tree, &io); + if (smbreq == NULL) { + ret = false; + torture_fail_goto(tctx, done, "smb_raw_read_send failed\n"); + } + if (!smbcli_request_receive(smbreq) || + smbcli_request_is_error(smbreq)) { + status = smbcli_request_destroy(smbreq); + torture_fail_goto(tctx, done, "receive failed\n"); + } + + if (smbreq->in.wct != 12) { + ret = false; + printf("Incorrect wct %u (should be 12)\n", + (unsigned int)smbreq->in.wct); + status = smbcli_request_destroy(smbreq); + torture_fail_goto(tctx, done, "bad wct\n"); + } + + /* Ensure VWV8 - WVW11 are zero. */ + for (i = 8; i < 12; i++) { + uint16_t br = SVAL(smbreq->in.vwv, VWV(i)); + if (br != 0) { + status = smbcli_request_destroy(smbreq); + ret = false; + printf("reserved field %u is %u not zero\n", + i, + (unsigned int)br); + torture_fail_goto(tctx, done, "bad reserved field\n"); + } + } + + smbcli_request_destroy(smbreq); + + printf("Trying small read\n"); + io.readx.in.file.fnum = fnum; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + io.readx.in.read_for_execute = false; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, strlen(test_data)); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_READX_ALIGN(io); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.readx.in.offset = 1; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, strlen(test_data)-1); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_READX_ALIGN(io); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { + printf("Trying max offset\n"); + io.readx.in.offset = 0xffffffff; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_READX_ALIGN(io); + } + + printf("Trying mincnt past EOF\n"); + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 100; + io.readx.in.maxcnt = 110; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, strlen(test_data)); + CHECK_READX_ALIGN(io); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + + setup_buffer(buf, seed, maxsize); + smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying page sized read\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = 0x1000; + io.readx.in.maxcnt = 0x1000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + printf("Trying page + 1 sized read (check alignment)\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = 0x1001; + io.readx.in.maxcnt = 0x1001; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + printf("Trying large read (UINT16_MAX)\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = 0xFFFF; + io.readx.in.maxcnt = 0xFFFF; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + printf("Trying extra large read\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = 100; + io.readx.in.maxcnt = 80000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + if (io.readx.out.nread == io.readx.in.maxcnt) { + printf("SAMBA: large read extension\n"); + CHECK_VALUE(io.readx.out.nread, 80000); + } else { + CHECK_VALUE(io.readx.out.nread, 0x10000); + } + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + printf("Trying mincnt > maxcnt\n"); + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 30000; + io.readx.in.maxcnt = 20000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + printf("Trying mincnt < maxcnt\n"); + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 20000; + io.readx.in.maxcnt = 30000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + CHECK_READX_ALIGN(io); + + if (cli->transport->negotiate.capabilities & CAP_LARGE_READX) { + printf("Trying large readx\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = 0; + io.readx.in.maxcnt = 0x10000 - 1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0xFFFF); + CHECK_READX_ALIGN(io); + + io.readx.in.maxcnt = 0x10000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0x10000); + CHECK_READX_ALIGN(io); + + io.readx.in.maxcnt = 0x10001; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + if (io.readx.out.nread == io.readx.in.maxcnt) { + printf("SAMBA: large read extension\n"); + CHECK_VALUE(io.readx.out.nread, 0x10001); + } else { + CHECK_VALUE(io.readx.out.nread, 0x10000); + } + } else { + printf("Server does not support the CAP_LARGE_READX extension\n"); + } + + printf("Trying locked region\n"); + cli->session->pid++; + if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { + printf("Failed to lock file at %d\n", __LINE__); + ret = false; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 100; + io.readx.in.maxcnt = 200; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + printf("skipping large file tests - CAP_LARGE_FILES not set\n"); + goto done; + } + + printf("Trying large offset read\n"); + io.readx.in.offset = ((uint64_t)0x2) << 32; + io.readx.in.mincnt = 10; + io.readx.in.maxcnt = 10; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_READX_ALIGN(io); + + if (NT_STATUS_IS_ERR(smbcli_lock64(cli->tree, fnum, io.readx.in.offset, 1, 0, WRITE_LOCK))) { + printf("Failed to lock file at %d\n", __LINE__); + ret = false; + goto done; + } + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_READX_ALIGN(io); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test readbraw ops +*/ +static bool test_readbraw(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_read io; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned int seed = time(NULL); + + if (!cli->transport->negotiate.readbraw_supported) { + printf("Server does not support readbraw - skipping\n"); + return true; + } + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing RAW_READ_READBRAW\n"); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READBRAW; + io.readbraw.in.file.fnum = fnum; + io.readbraw.in.mincnt = 1; + io.readbraw.in.maxcnt = 1; + io.readbraw.in.offset = 0; + io.readbraw.in.timeout = 0; + io.readbraw.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying zero file read\n"); + io.readbraw.in.mincnt = 0; + io.readbraw.in.maxcnt = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying bad fnum\n"); + io.readbraw.in.file.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + io.readbraw.in.file.fnum = fnum; + + smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.readbraw.in.file.fnum = fnum; + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.readbraw.in.offset = 1; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = false; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { + printf("Trying max offset\n"); + io.readbraw.in.offset = ~0; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + } + + setup_buffer(buf, seed, maxsize); + smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = ~0; + io.readbraw.in.maxcnt = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0xFFFF); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying mincnt > maxcnt\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 30000; + io.readbraw.in.maxcnt = 20000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying mincnt < maxcnt\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 20000; + io.readbraw.in.maxcnt = 30000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying locked region\n"); + cli->session->pid++; + if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { + printf("Failed to lock file at %d\n", __LINE__); + ret = false; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 100; + io.readbraw.in.maxcnt = 200; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying locked region with timeout\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 100; + io.readbraw.in.maxcnt = 200; + io.readbraw.in.timeout = 10000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { + printf("Trying large offset read\n"); + io.readbraw.in.offset = ((uint64_t)0x2) << 32; + io.readbraw.in.mincnt = 10; + io.readbraw.in.maxcnt = 10; + io.readbraw.in.timeout = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + } + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test read for execute +*/ +static bool test_read_for_execute(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_open op; + union smb_write wr; + union smb_read rd; + NTSTATUS status; + bool ret = true; + int fnum=0; + uint8_t *buf; + const int maxsize = 900; + const char *fname = BASEDIR "\\test.txt"; + const uint8_t data[] = "TEST DATA"; + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing RAW_READ_READX with read_for_execute\n"); + + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.file.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = ARRAY_SIZE(data); + wr.writex.in.data = data; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, ARRAY_SIZE(data)); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("open file with SEC_FILE_EXECUTE\n"); + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_FILE_EXECUTE; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + printf("read with FLAGS2_READ_PERMIT_EXECUTE\n"); + rd.generic.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = maxsize; + rd.readx.in.offset = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = true; + rd.readx.out.data = buf; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data)); + CHECK_VALUE(rd.readx.out.remaining, 0xFFFF); + CHECK_VALUE(rd.readx.out.compaction_mode, 0); + + printf("read without FLAGS2_READ_PERMIT_EXECUTE (should fail)\n"); + rd.generic.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = maxsize; + rd.readx.in.offset = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = buf; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + status = smbcli_close(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("open file with SEC_FILE_READ_DATA\n"); + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + printf("read with FLAGS2_READ_PERMIT_EXECUTE\n"); + rd.generic.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = maxsize; + rd.readx.in.offset = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = true; + rd.readx.out.data = buf; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data)); + CHECK_VALUE(rd.readx.out.remaining, 0xFFFF); + CHECK_VALUE(rd.readx.out.compaction_mode, 0); + + printf("read without FLAGS2_READ_PERMIT_EXECUTE\n"); + rd.generic.level = RAW_READ_READX; + rd.readx.in.file.fnum = fnum; + rd.readx.in.mincnt = 0; + rd.readx.in.maxcnt = maxsize; + rd.readx.in.offset = 0; + rd.readx.in.remaining = 0; + rd.readx.in.read_for_execute = false; + rd.readx.out.data = buf; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data)); + CHECK_VALUE(rd.readx.out.remaining, 0xFFFF); + CHECK_VALUE(rd.readx.out.compaction_mode, 0); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + basic testing of read calls +*/ +struct torture_suite *torture_raw_read(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "read"); + + torture_suite_add_1smb_test(suite, "read", test_read); + torture_suite_add_1smb_test(suite, "readx", test_readx); + torture_suite_add_1smb_test(suite, "lockread", test_lockread); + torture_suite_add_1smb_test(suite, "readbraw", test_readbraw); + torture_suite_add_1smb_test(suite, "read for execute", + test_read_for_execute); + + return suite; +} diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c new file mode 100644 index 0000000..5f48c05 --- /dev/null +++ b/source4/torture/raw/rename.c @@ -0,0 +1,692 @@ +/* + Unix SMB/CIFS implementation. + rename test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect %s %d - should be %d\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define BASEDIR "\\testrename" + +/* + test SMBmv ops +*/ +static bool test_mv(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_rename io; + NTSTATUS status; + bool ret = true; + int fnum = -1; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + const char *Fname1 = BASEDIR "\\Test1.txt"; + union smb_fileinfo finfo; + union smb_open op; + + torture_comment(tctx, "Testing SMBmv\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Trying simple rename\n"); + + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname1; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + io.generic.level = RAW_RENAME_RENAME; + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = fname2; + io.rename.in.attrib = 0; + + torture_comment(tctx, "trying rename while first file open\n"); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + smbcli_close(cli->tree, fnum); + + op.ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + torture_comment(tctx, "trying rename while first file open with SHARE_ACCESS_DELETE\n"); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.rename.in.pattern1 = fname2; + io.rename.in.pattern2 = fname1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Trying case-changing rename\n"); + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = Fname1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (strcmp(finfo.all_info.out.fname.s, Fname1) != 0) { + torture_warning(tctx, "(%s) Incorrect filename [%s] after case-changing " + "rename, should be [%s]\n", __location__, + finfo.all_info.out.fname.s, Fname1); + } + + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = fname2; + + torture_comment(tctx, "trying rename while not open\n"); + smb_raw_exit(cli->session); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Trying self rename\n"); + io.rename.in.pattern1 = fname2; + io.rename.in.pattern2 = fname2; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = fname1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +static bool test_osxrename(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_rename io; + union smb_unlink io_un; + NTSTATUS status; + bool ret = true; + int fnum = -1; + const char *fname1 = BASEDIR "\\test1"; + const char *FNAME1 = BASEDIR "\\TEST1"; + union smb_fileinfo finfo; + union smb_open op; + + torture_comment(tctx, "\nTesting OSX Rename\n"); + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname1; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + io.generic.level = RAW_RENAME_RENAME; + io.rename.in.attrib = 0; + + smbcli_close(cli->tree, fnum); + + /* Rename by changing case. First check for the + * existence of the file with the "newname". + * If we find one and both the output and input are same case, + * delete it. */ + + torture_comment(tctx, "Checking os X rename (case changing)\n"); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.path = FNAME1; + torture_comment(tctx, "Looking for file %s \n",FNAME1); + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Name of the file found %s \n", finfo.all_info.out.fname.s); + if (strcmp(finfo.all_info.out.fname.s, finfo.all_info.in.file.path) == 0) { + /* If file is found with the same case delete it */ + torture_comment(tctx, "Deleting File %s \n", finfo.all_info.out.fname.s); + io_un.unlink.in.pattern = finfo.all_info.out.fname.s; + io_un.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io_un); + CHECK_STATUS(status, NT_STATUS_OK); + } + } + + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = FNAME1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "File name after rename %s \n",finfo.all_info.out.fname.s); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test SMBntrename ops +*/ +static bool test_ntrename(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_rename io; + NTSTATUS status; + bool ret = true; + int fnum, i; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + union smb_fileinfo finfo; + + torture_comment(tctx, "Testing SMBntrename\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Trying simple rename\n"); + + fnum = create_complex_file(cli, tctx, fname1); + + io.generic.level = RAW_RENAME_NTRENAME; + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname2; + io.ntrename.in.attrib = 0; + io.ntrename.in.cluster_size = 0; + io.ntrename.in.flags = RENAME_FLAG_RENAME; + + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + smbcli_close(cli->tree, fnum); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Trying self rename\n"); + io.ntrename.in.old_name = fname2; + io.ntrename.in.new_name = fname2; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_comment(tctx, "trying wildcard rename\n"); + io.ntrename.in.old_name = BASEDIR "\\*.txt"; + io.ntrename.in.new_name = fname1; + + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + torture_comment(tctx, "Checking attrib handling\n"); + torture_set_file_attribute(cli->tree, fname2, FILE_ATTRIBUTE_HIDDEN); + io.ntrename.in.old_name = fname2; + io.ntrename.in.new_name = fname1; + io.ntrename.in.attrib = 0; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.ntrename.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL); + + torture_comment(tctx, "Checking hard link\n"); + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname2; + io.ntrename.in.attrib = 0; + io.ntrename.in.flags = RENAME_FLAG_HARD_LINK; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_SYSTEM); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname2; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 2); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM); + + finfo.generic.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 2); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM); + + torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL); + + smbcli_unlink(cli->tree, fname2); + + finfo.generic.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL); + + torture_comment(tctx, "Checking copy\n"); + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname2; + io.ntrename.in.attrib = 0; + io.ntrename.in.flags = RENAME_FLAG_COPY; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname2; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL); + + torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_SYSTEM); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname2; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL); + + finfo.generic.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM); + + torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL); + + smbcli_unlink(cli->tree, fname2); + + finfo.generic.in.file.path = fname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.nlink, 1); + + torture_comment(tctx, "Checking invalid flags\n"); + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname2; + io.ntrename.in.attrib = 0; + io.ntrename.in.flags = 0; + status = smb_raw_rename(cli->tree, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.ntrename.in.flags = 300; + status = smb_raw_rename(cli->tree, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.ntrename.in.flags = 0x106; + status = smb_raw_rename(cli->tree, &io); + if (TARGET_IS_WIN7(tctx)) { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "Checking unknown field\n"); + io.ntrename.in.old_name = fname1; + io.ntrename.in.new_name = fname2; + io.ntrename.in.attrib = 0; + io.ntrename.in.flags = RENAME_FLAG_RENAME; + io.ntrename.in.cluster_size = 0xff; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "Trying RENAME_FLAG_MOVE_CLUSTER_INFORMATION\n"); + + io.ntrename.in.old_name = fname2; + io.ntrename.in.new_name = fname1; + io.ntrename.in.attrib = 0; + io.ntrename.in.flags = RENAME_FLAG_MOVE_CLUSTER_INFORMATION; + io.ntrename.in.cluster_size = 1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + io.ntrename.in.flags = RENAME_FLAG_COPY; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +#if 0 + { + char buf[16384]; + fnum = smbcli_open(cli->tree, fname1, O_RDWR, DENY_NONE); + memset(buf, 1, sizeof(buf)); + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + smbcli_close(cli->tree, fnum); + + fnum = smbcli_open(cli->tree, fname2, O_RDWR, DENY_NONE); + memset(buf, 1, sizeof(buf)); + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)-1); + smbcli_close(cli->tree, fnum); + + torture_all_info(cli->tree, fname1); + torture_all_info(cli->tree, fname2); + } + + + io.ntrename.in.flags = RENAME_FLAG_MOVE_CLUSTER_INFORMATION; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + for (i=0;i<20000;i++) { + io.ntrename.in.cluster_size = i; + status = smb_raw_rename(cli->tree, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + torture_warning(tctx, "i=%d status=%s\n", i, nt_errstr(status)); + } + } +#endif + + torture_comment(tctx, "Checking other flags\n"); + + for (i=0;i<0xFFF;i++) { + if (i == RENAME_FLAG_RENAME || + i == RENAME_FLAG_HARD_LINK || + i == RENAME_FLAG_COPY) { + continue; + } + + io.ntrename.in.old_name = fname2; + io.ntrename.in.new_name = fname1; + io.ntrename.in.flags = i; + io.ntrename.in.attrib = 0; + io.ntrename.in.cluster_size = 0; + status = smb_raw_rename(cli->tree, &io); + if (TARGET_IS_WIN7(tctx)){ + if (!NT_STATUS_EQUAL(status, + NT_STATUS_INVALID_PARAMETER)) { + torture_warning(tctx, "flags=0x%x status=%s\n", + i, nt_errstr(status)); + } + } else { + if (!NT_STATUS_EQUAL(status, + NT_STATUS_ACCESS_DENIED)) { + torture_warning(tctx, "flags=0x%x status=%s\n", + i, nt_errstr(status)); + } + } + } + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test dir rename. +*/ +static bool test_dir_rename(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + union smb_rename ren_io; + NTSTATUS status; + const char *dname1 = BASEDIR "\\dir_for_rename"; + const char *dname2 = BASEDIR "\\renamed_dir"; + const char *dname1_long = BASEDIR "\\dir_for_rename_long"; + const char *fname = BASEDIR "\\dir_for_rename\\file.txt"; + const char *sname = BASEDIR "\\renamed_dir:a stream:$DATA"; + bool ret = true; + int fnum = -1; + + torture_comment(tctx, "Checking rename on a directory containing an open file.\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* create a directory */ + smbcli_rmdir(cli->tree, dname1); + smbcli_rmdir(cli->tree, dname2); + smbcli_rmdir(cli->tree, dname1_long); + smbcli_unlink(cli->tree, dname1); + smbcli_unlink(cli->tree, dname2); + smbcli_unlink(cli->tree, dname1_long); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.fname = dname1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + fnum = io.ntcreatex.out.file.fnum; + smbcli_close(cli->tree, fnum); + + /* create the longname directory */ + io.ntcreatex.in.fname = dname1_long; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + fnum = io.ntcreatex.out.file.fnum; + smbcli_close(cli->tree, fnum); + + /* Now create and hold open a file. */ + ZERO_STRUCT(io); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* Create the file. */ + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* Now try and rename the directory. */ + + ZERO_STRUCT(ren_io); + ren_io.generic.level = RAW_RENAME_RENAME; + ren_io.rename.in.pattern1 = dname1; + ren_io.rename.in.pattern2 = dname2; + ren_io.rename.in.attrib = 0; + + status = smb_raw_rename(cli->tree, &ren_io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + /* Close the file and try the rename. */ + smbcli_close(cli->tree, fnum); + + status = smb_raw_rename(cli->tree, &ren_io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Now try just holding a second handle on the directory and holding + * it open across a rename. This should be allowed. + */ + io.ntcreatex.in.fname = dname2; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_READ_EA | SEC_FILE_READ_DATA; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ren_io.generic.level = RAW_RENAME_RENAME; + ren_io.rename.in.pattern1 = dname2; + ren_io.rename.in.pattern2 = dname1; + ren_io.rename.in.attrib = 0; + + status = smb_raw_rename(cli->tree, &ren_io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* close our handle to the directory. */ + smbcli_close(cli->tree, fnum); + + /* Open a handle on the long name, and then + * try a rename. This would catch a regression + * in bug #6781. + */ + io.ntcreatex.in.fname = dname1_long; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + + io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_READ_EA | SEC_FILE_READ_DATA; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ren_io.generic.level = RAW_RENAME_RENAME; + ren_io.rename.in.pattern1 = dname1; + ren_io.rename.in.pattern2 = dname2; + ren_io.rename.in.attrib = 0; + + status = smb_raw_rename(cli->tree, &ren_io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* close our handle to the longname directory. */ + smbcli_close(cli->tree, fnum); + + /* + * Now try opening a stream on the directory and holding it open + * across a rename. This should be allowed. + */ + io.ntcreatex.in.fname = sname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ren_io.generic.level = RAW_RENAME_RENAME; + ren_io.rename.in.pattern1 = dname2; + ren_io.rename.in.pattern2 = dname1; + ren_io.rename.in.attrib = 0; + + status = smb_raw_rename(cli->tree, &ren_io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +extern bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2); +extern bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1); + +/* + basic testing of rename calls +*/ +struct torture_suite *torture_raw_rename(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "rename"); + + torture_suite_add_1smb_test(suite, "mv", test_mv); + /* test_trans2rename and test_nttransrename are actually in torture/raw/oplock.c to + use the handlers and macros there. */ + torture_suite_add_2smb_test(suite, "trans2rename", test_trans2rename); + torture_suite_add_1smb_test(suite, "nttransrename", test_nttransrename); + torture_suite_add_1smb_test(suite, "ntrename", test_ntrename); + torture_suite_add_1smb_test(suite, "osxrename", test_osxrename); + torture_suite_add_1smb_test(suite, "directory rename", test_dir_rename); + + return suite; +} diff --git a/source4/torture/raw/samba3hide.c b/source4/torture/raw/samba3hide.c new file mode 100644 index 0000000..d28f91e --- /dev/null +++ b/source4/torture/raw/samba3hide.c @@ -0,0 +1,326 @@ +/* + Unix SMB/CIFS implementation. + Test samba3 hide unreadable/unwriteable + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +static void init_unixinfo_nochange(union smb_setfileinfo *info) +{ + ZERO_STRUCTP(info); + info->unix_basic.level = RAW_SFILEINFO_UNIX_BASIC; + info->unix_basic.in.mode = SMB_MODE_NO_CHANGE; + + info->unix_basic.in.end_of_file = SMB_SIZE_NO_CHANGE_HI; + info->unix_basic.in.end_of_file <<= 32; + info->unix_basic.in.end_of_file |= SMB_SIZE_NO_CHANGE_LO; + + info->unix_basic.in.num_bytes = SMB_SIZE_NO_CHANGE_HI; + info->unix_basic.in.num_bytes <<= 32; + info->unix_basic.in.num_bytes |= SMB_SIZE_NO_CHANGE_LO; + + info->unix_basic.in.status_change_time = SMB_TIME_NO_CHANGE_HI; + info->unix_basic.in.status_change_time <<= 32; + info->unix_basic.in.status_change_time |= SMB_TIME_NO_CHANGE_LO; + + info->unix_basic.in.access_time = SMB_TIME_NO_CHANGE_HI; + info->unix_basic.in.access_time <<= 32; + info->unix_basic.in.access_time |= SMB_TIME_NO_CHANGE_LO; + + info->unix_basic.in.change_time = SMB_TIME_NO_CHANGE_HI; + info->unix_basic.in.change_time <<= 32; + info->unix_basic.in.change_time |= SMB_TIME_NO_CHANGE_LO; + + info->unix_basic.in.uid = SMB_UID_NO_CHANGE; + info->unix_basic.in.gid = SMB_GID_NO_CHANGE; +} + +struct list_state { + const char *fname; + bool visible; +}; + +static void set_visible(struct clilist_file_info *i, const char *mask, + void *priv) +{ + struct list_state *state = (struct list_state *)priv; + + if (strcasecmp_m(state->fname, i->name) == 0) + state->visible = true; +} + +static bool is_visible(struct smbcli_tree *tree, const char *fname) +{ + struct list_state state; + + state.visible = false; + state.fname = fname; + + if (smbcli_list(tree, "*.*", 0, set_visible, &state) < 0) { + return false; + } + return state.visible; +} + +static bool is_readable(struct smbcli_tree *tree, const char *fname) +{ + int fnum; + fnum = smbcli_open(tree, fname, O_RDONLY, DENY_NONE); + if (fnum < 0) { + return false; + } + smbcli_close(tree, fnum); + return true; +} + +static bool is_writeable(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree, + const char *fname) +{ + int fnum; + fnum = smbcli_open(tree, fname, O_WRONLY, DENY_NONE); + if (fnum < 0) { + return false; + } + smbcli_close(tree, fnum); + return true; +} + +/* + * This is not an exact method because there's a ton of reasons why a getatr + * might fail. But for our purposes it's sufficient. + */ + +static bool smbcli_file_exists(struct smbcli_tree *tree, const char *fname) +{ + return NT_STATUS_IS_OK(smbcli_getatr(tree, fname, NULL, NULL, NULL)); +} + +static NTSTATUS smbcli_setup_unix(struct smbcli_tree *tree) +{ + union smb_fsinfo fsinfo; + union smb_setfsinfo set_fsinfo; + NTSTATUS status; + + ZERO_STRUCT(fsinfo); + ZERO_STRUCT(set_fsinfo); + + fsinfo.generic.level = RAW_QFS_UNIX_INFO; + status = smb_raw_fsinfo(tree, NULL, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("smb_raw_fsinfo failed %s\n", + nt_errstr(status)); + return status; + } + + set_fsinfo.generic.level = RAW_SETFS_UNIX_INFO; + set_fsinfo.unix_info.in.major_version = fsinfo.unix_info.out.major_version; + set_fsinfo.unix_info.in.minor_version = fsinfo.unix_info.out.minor_version; + set_fsinfo.unix_info.in.capability = fsinfo.unix_info.out.capability; + + status = smb_raw_setfsinfo(tree, NULL, &set_fsinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("smb_raw_setfsinfo failed %s\n", + nt_errstr(status)); + } + return status; +} + +static NTSTATUS smbcli_chmod(struct smbcli_tree *tree, const char *fname, + uint64_t permissions) +{ + union smb_setfileinfo sfinfo; + init_unixinfo_nochange(&sfinfo); + sfinfo.unix_basic.in.file.path = fname; + sfinfo.unix_basic.in.permissions = permissions; + return smb_raw_setpathinfo(tree, &sfinfo); +} + +bool torture_samba3_hide(struct torture_context *torture, struct smbcli_state *cli) +{ + const char *fname = "torture_samba3_hide.txt"; + int fnum; + NTSTATUS status; + struct smbcli_tree *hideunread; + struct smbcli_tree *hideunwrite; + + status = smbcli_setup_unix(cli->tree); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(torture, + talloc_asprintf(torture, "smbcli_setup_unix failed %s\n", + nt_errstr(status))); + } + + status = torture_second_tcon(torture, cli->session, "hideunread", + &hideunread); + torture_assert_ntstatus_ok(torture, status, "second_tcon(hideunread) failed\n"); + + status = torture_second_tcon(torture, cli->session, "hideunwrite", + &hideunwrite); + torture_assert_ntstatus_ok(torture, status, "second_tcon(hideunwrite) failed\n"); + + status = smbcli_unlink(cli->tree, fname); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + smbcli_setatr(cli->tree, fname, 0, -1); + smbcli_unlink(cli->tree, fname); + } + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_fail(torture, + talloc_asprintf(torture, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree))); + } + + smbcli_close(cli->tree, fnum); + + if (!smbcli_file_exists(cli->tree, fname)) { + torture_fail(torture, talloc_asprintf(torture, "%s does not exist\n", fname)); + } + + /* R/W file should be visible everywhere */ + + status = smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR); + torture_assert_ntstatus_ok(torture, status, "smbcli_chmod failed\n"); + + if (!is_writeable(torture, cli->tree, fname)) { + torture_fail(torture, "File not writable\n"); + } + if (!is_readable(cli->tree, fname)) { + torture_fail(torture, "File not readable\n"); + } + if (!is_visible(cli->tree, fname)) { + torture_fail(torture, "r/w file not visible via normal share\n"); + } + if (!is_visible(hideunread, fname)) { + torture_fail(torture, "r/w file not visible via hide unreadable\n"); + } + if (!is_visible(hideunwrite, fname)) { + torture_fail(torture, "r/w file not visible via hide unwriteable\n"); + } + + /* R/O file should not be visible via hide unwriteable files */ + + status = smbcli_chmod(cli->tree, fname, UNIX_R_USR); + torture_assert_ntstatus_ok(torture, status, "smbcli_chmod failed\n"); + + if (is_writeable(torture, cli->tree, fname)) { + torture_fail(torture, "r/o is writable\n"); + } + if (!is_readable(cli->tree, fname)) { + torture_fail(torture, "r/o not readable\n"); + } + if (!is_visible(cli->tree, fname)) { + torture_fail(torture, "r/o file not visible via normal share\n"); + } + if (!is_visible(hideunread, fname)) { + torture_fail(torture, "r/o file not visible via hide unreadable\n"); + } + if (is_visible(hideunwrite, fname)) { + torture_fail(torture, "r/o file visible via hide unwriteable\n"); + } + + /* inaccessible file should be only visible on normal share */ + + status = smbcli_chmod(cli->tree, fname, 0); + torture_assert_ntstatus_ok(torture, status, "smbcli_chmod failed\n"); + + if (is_writeable(torture, cli->tree, fname)) { + torture_fail(torture, "inaccessible file is writable\n"); + } + if (is_readable(cli->tree, fname)) { + torture_fail(torture, "inaccessible file is readable\n"); + } + if (!is_visible(cli->tree, fname)) { + torture_fail(torture, "inaccessible file not visible via normal share\n"); + } + if (is_visible(hideunread, fname)) { + torture_fail(torture, "inaccessible file visible via hide unreadable\n"); + } + if (is_visible(hideunwrite, fname)) { + torture_fail(torture, "inaccessible file visible via hide unwriteable\n"); + } + + smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR); + smbcli_unlink(cli->tree, fname); + + return true; +} + +/* + * Try to force smb_close to return an error. The only way I can think of is + * to open a file with delete on close, chmod the parent dir to 000 and then + * close. smb_close should return NT_STATUS_ACCESS_DENIED. + */ + +bool torture_samba3_closeerr(struct torture_context *tctx, struct smbcli_state *cli) +{ + bool result = false; + NTSTATUS status; + const char *dname = "closeerr.dir"; + const char *fname = "closeerr.dir\\closerr.txt"; + int fnum; + + smbcli_deltree(cli->tree, dname); + + torture_assert_ntstatus_ok( + tctx, smbcli_mkdir(cli->tree, dname), + talloc_asprintf(tctx, "smbcli_mdir failed: (%s)\n", + smbcli_errstr(cli->tree))); + + fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, + DENY_NONE); + torture_assert(tctx, fnum != -1, + talloc_asprintf(tctx, "smbcli_open failed: %s\n", + smbcli_errstr(cli->tree))); + smbcli_close(cli->tree, fnum); + + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_OPEN, 0, 0); + + torture_assert(tctx, fnum != -1, + talloc_asprintf(tctx, "smbcli_open failed: %s\n", + smbcli_errstr(cli->tree))); + + status = smbcli_nt_delete_on_close(cli->tree, fnum, true); + + torture_assert_ntstatus_ok(tctx, status, + "setting delete_on_close on file failed !"); + + status = smbcli_chmod(cli->tree, dname, 0); + + torture_assert_ntstatus_ok(tctx, status, + "smbcli_chmod on file failed !"); + + status = smbcli_close(cli->tree, fnum); + + smbcli_chmod(cli->tree, dname, UNIX_R_USR|UNIX_W_USR|UNIX_X_USR); + smbcli_deltree(cli->tree, dname); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_ACCESS_DENIED, + "smbcli_close"); + + result = true; + + return result; +} diff --git a/source4/torture/raw/samba3misc.c b/source4/torture/raw/samba3misc.c new file mode 100644 index 0000000..35271aa --- /dev/null +++ b/source4/torture/raw/samba3misc.c @@ -0,0 +1,1137 @@ +/* + Unix SMB/CIFS implementation. + Test some misc Samba3 code paths + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "param/param.h" +#include "torture/raw/proto.h" + +/* + The next 2 functions are stolen from source4/libcli/raw/rawfile.c + but allow us to send a raw data blob instead of an OpenX name. +*/ + +#define SETUP_REQUEST(cmd, wct, buflen) do { \ + req = smbcli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +static struct smbcli_request *smb_raw_openX_name_blob_send(struct smbcli_tree *tree, + union smb_open *parms, + const DATA_BLOB *pname_blob) +{ + struct smbcli_request *req = NULL; + + if (parms->generic.level != RAW_OPEN_OPENX) { + return NULL; + } + + SETUP_REQUEST(SMBopenX, 15, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->openx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->openx.in.open_mode); + SSVAL(req->out.vwv, VWV(4), parms->openx.in.search_attrs); + SSVAL(req->out.vwv, VWV(5), parms->openx.in.file_attrs); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(6), parms->openx.in.write_time); + SSVAL(req->out.vwv, VWV(8), parms->openx.in.open_func); + SIVAL(req->out.vwv, VWV(9), parms->openx.in.size); + SIVAL(req->out.vwv, VWV(11),parms->openx.in.timeout); + SIVAL(req->out.vwv, VWV(13),0); /* reserved */ + smbcli_req_append_blob(req, pname_blob); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +static NTSTATUS smb_raw_openX_name_blob(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_open *parms, + const DATA_BLOB *pname_blob) +{ + struct smbcli_request *req = smb_raw_openX_name_blob_send(tree, parms, pname_blob); + return smb_raw_open_recv(req, mem_ctx, parms); +} + +static NTSTATUS raw_smbcli_openX_name_blob(struct smbcli_tree *tree, + const DATA_BLOB *pname_blob, + int flags, + int share_mode, + int *fnum) +{ + union smb_open open_parms; + unsigned int openfn=0; + unsigned int accessmode=0; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_openX_name_blob"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + if (flags & O_CREAT) { + openfn |= OPENX_OPEN_FUNC_CREATE; + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) { + openfn |= OPENX_OPEN_FUNC_TRUNC; + } else { + openfn |= OPENX_OPEN_FUNC_OPEN; + } + } + + accessmode = (share_mode<session, + torture_setting_string(torture, "share", NULL), + &tree2), + NT_STATUS_OK, + "creating second tcon"); + + /* Try a read on an invalid FID */ + + nread = smbcli_read(cli->tree, 4711, buf, 0, sizeof(buf)); + CHECK_STATUS(torture, smbcli_nt_error(cli->tree), NT_STATUS_INVALID_HANDLE); + + /* Try a read on a directory handle */ + + torture_assert(torture, torture_setup_dir(cli, dirname), "creating test directory"); + + /* Open the directory */ + { + union smb_open io; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = dirname; + status = smb_raw_open(cli->tree, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_result(torture, TORTURE_FAIL, "smb_open on the directory failed: %s\n", + nt_errstr(status)); + ret = false; + goto done; + } + fnum = io.ntcreatex.out.file.fnum; + } + + /* Try a read on the directory */ + + nread = smbcli_read(cli->tree, fnum, buf, 0, sizeof(buf)); + if (nread >= 0) { + torture_result(torture, TORTURE_FAIL, "smbcli_read on a directory succeeded, expected " + "failure\n"); + ret = false; + } + + CHECK_STATUS(torture, smbcli_nt_error(cli->tree), + NT_STATUS_INVALID_DEVICE_REQUEST); + + /* Same test on the second tcon */ + + nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf)); + if (nread >= 0) { + torture_result(torture, TORTURE_FAIL, "smbcli_read on a directory succeeded, expected " + "failure\n"); + ret = false; + } + + CHECK_STATUS(torture, smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE); + + smbcli_close(cli->tree, fnum); + + /* Try a normal file read on a second tcon */ + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_result(torture, TORTURE_FAIL, "Failed to create %s - %s\n", fname, + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf)); + CHECK_STATUS(torture, smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE); + + smbcli_close(cli->tree, fnum); + + done: + smbcli_deltree(cli->tree, dirname); + talloc_free(mem_ctx); + + return ret; +} + +static NTSTATUS raw_smbcli_open(struct smbcli_tree *tree, const char *fname, int flags, int share_mode, int *fnum) +{ + union smb_open open_parms; + unsigned int openfn=0; + unsigned int accessmode=0; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + if (flags & O_CREAT) { + openfn |= OPENX_OPEN_FUNC_CREATE; + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) { + openfn |= OPENX_OPEN_FUNC_TRUNC; + } else { + openfn |= OPENX_OPEN_FUNC_OPEN; + } + } + + accessmode = (share_mode<lp_ctx); + client_ntlmv2_auth = lpcfg_client_ntlmv2_auth(torture->lp_ctx); + + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "nt status support", "yes"), ret, fail, "Could not set 'nt status support = yes'\n"); + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "client ntlmv2 auth", "yes"), ret, fail, "Could not set 'client ntlmv2 auth = yes'\n"); + + torture_assert_goto(torture, torture_open_connection(&cli_nt, torture, 0), ret, fail, "Could not open NTSTATUS connection\n"); + + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "nt status support", "no"), ret, fail, "Could not set 'nt status support = no'\n"); + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "client ntlmv2 auth", "no"), ret, fail, "Could not set 'client ntlmv2 auth = no'\n"); + + torture_assert_goto(torture, torture_open_connection(&cli_dos, torture, 1), ret, fail, "Could not open DOS connection\n"); + + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "nt status support", + nt_status_support ? "yes":"no"), + ret, fail, "Could not set 'nt status support' back to where it was\n"); + torture_assert_goto(torture, lpcfg_set_cmdline(torture->lp_ctx, "client ntlmv2 auth", + client_ntlmv2_auth ? "yes":"no"), + ret, fail, "Could not set 'client ntlmv2 auth' back to where it was\n"); + + torture_assert(torture, torture_setup_dir(cli_nt, dirname), "creating test directory"); + + status = smbcli_chkpath(cli_nt->tree, dirname); + CHECK_STATUS(torture, status, NT_STATUS_OK); + + status = smbcli_chkpath(cli_nt->tree, + talloc_asprintf(mem_ctx, "%s\\bla", dirname)); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + status = smbcli_chkpath(cli_dos->tree, + talloc_asprintf(mem_ctx, "%s\\bla", dirname)); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, + talloc_asprintf(mem_ctx, "%s\\bla\\blub", + dirname)); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_PATH_NOT_FOUND); + status = smbcli_chkpath(cli_dos->tree, + talloc_asprintf(mem_ctx, "%s\\bla\\blub", + dirname)); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + torture_assert_goto(torture, fpath = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname), + ret, fail, "Could not allocate fpath\n"); + + fnum = smbcli_open(cli_nt->tree, fpath, O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_result(torture, TORTURE_FAIL, "Could not create file %s: %s\n", fpath, + smbcli_errstr(cli_nt->tree)); + goto fail; + } + smbcli_close(cli_nt->tree, fnum); + + if (!(fpath1 = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname1))) { + goto fail; + } + fnum = smbcli_open(cli_nt->tree, fpath1, O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_result(torture, TORTURE_FAIL, "Could not create file %s: %s\n", fpath1, + smbcli_errstr(cli_nt->tree)); + goto fail; + } + smbcli_close(cli_nt->tree, fnum); + + /* + * Do a whole bunch of error code checks on chkpath + */ + + status = smbcli_chkpath(cli_nt->tree, fpath); + CHECK_STATUS(torture, status, NT_STATUS_NOT_A_DIRECTORY); + status = smbcli_chkpath(cli_dos->tree, fpath); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, ".."); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + status = smbcli_chkpath(cli_dos->tree, ".."); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath)); + + status = smbcli_chkpath(cli_nt->tree, "."); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_chkpath(cli_dos->tree, "."); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, "\t"); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_chkpath(cli_dos->tree, "\t"); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, "\t\\bla"); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_chkpath(cli_dos->tree, "\t\\bla"); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, "<"); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_chkpath(cli_dos->tree, "<"); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + status = smbcli_chkpath(cli_nt->tree, "<\\bla"); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_chkpath(cli_dos->tree, "<\\bla"); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRbadpath)); + + /* + * .... And the same gang against getatr. Note that the DOS error codes + * differ.... + */ + + status = smbcli_getatr(cli_nt->tree, fpath, NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OK); + status = smbcli_getatr(cli_dos->tree, fpath, NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OK); + + status = smbcli_getatr(cli_nt->tree, "..", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + status = smbcli_getatr(cli_dos->tree, "..", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath)); + + status = smbcli_getatr(cli_nt->tree, ".", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_getatr(cli_dos->tree, ".", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = smbcli_getatr(cli_nt->tree, "\t", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_getatr(cli_dos->tree, "\t", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = smbcli_getatr(cli_nt->tree, "\t\\bla", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_getatr(cli_dos->tree, "\t\\bla", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = smbcli_getatr(cli_nt->tree, "<", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_getatr(cli_dos->tree, "<", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = smbcli_getatr(cli_nt->tree, "<\\bla", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = smbcli_getatr(cli_dos->tree, "<\\bla", NULL, NULL, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + /* Try the same set with openX. */ + + status = raw_smbcli_open(cli_nt->tree, "..", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + status = raw_smbcli_open(cli_dos->tree, "..", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath)); + + status = raw_smbcli_open(cli_nt->tree, ".", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = raw_smbcli_open(cli_dos->tree, ".", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = raw_smbcli_open(cli_nt->tree, "\t", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = raw_smbcli_open(cli_dos->tree, "\t", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = raw_smbcli_open(cli_nt->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = raw_smbcli_open(cli_dos->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = raw_smbcli_open(cli_nt->tree, "<", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = raw_smbcli_open(cli_dos->tree, "<", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + status = raw_smbcli_open(cli_nt->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_INVALID); + status = raw_smbcli_open(cli_dos->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS, ERRinvalidname)); + + /* Let's test EEXIST error code mapping. */ + status = raw_smbcli_open(cli_nt->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_COLLISION); + status = raw_smbcli_open(cli_dos->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS,ERRfilexists)); + + status = raw_smbcli_t2open(cli_nt->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL); + if (!NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED) + || !torture_setting_bool(torture, "samba3", false)) { + /* Against samba3, treat EAS_NOT_SUPPORTED as acceptable */ + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_COLLISION); + } + status = raw_smbcli_t2open(cli_dos->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL); + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS,ERReasnotsupported)) + || !torture_setting_bool(torture, "samba3", false)) { + /* Against samba3, treat EAS_NOT_SUPPORTED as acceptable */ + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS,ERRfilexists)); + } + + status = raw_smbcli_ntcreate(cli_nt->tree, fpath, NULL); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_COLLISION); + status = raw_smbcli_ntcreate(cli_dos->tree, fpath, NULL); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS,ERRfilexists)); + + /* Try the rename test. */ + { + union smb_rename io; + memset(&io, '\0', sizeof(io)); + io.rename.in.pattern1 = fpath1; + io.rename.in.pattern2 = fpath; + + /* Try with SMBmv rename. */ + status = smb_raw_rename(cli_nt->tree, &io); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_COLLISION); + status = smb_raw_rename(cli_dos->tree, &io); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS,ERRrename)); + + /* Try with NT rename. */ + io.generic.level = RAW_RENAME_NTRENAME; + io.ntrename.in.old_name = fpath1; + io.ntrename.in.new_name = fpath; + io.ntrename.in.attrib = 0; + io.ntrename.in.cluster_size = 0; + io.ntrename.in.flags = RENAME_FLAG_RENAME; + + status = smb_raw_rename(cli_nt->tree, &io); + CHECK_STATUS(torture, status, NT_STATUS_OBJECT_NAME_COLLISION); + status = smb_raw_rename(cli_dos->tree, &io); + CHECK_STATUS(torture, status, NT_STATUS_DOS(ERRDOS,ERRrename)); + } + + goto done; + + fail: + ret = false; + + done: + if (cli_nt != NULL) { + smbcli_deltree(cli_nt->tree, dirname); + torture_close_connection(cli_nt); + } + if (cli_dos != NULL) { + torture_close_connection(cli_dos); + } + talloc_free(mem_ctx); + + return ret; +} + +static void count_fn(struct clilist_file_info *info, const char *name, + void *private_data) +{ + int *counter = (int *)private_data; + *counter += 1; +} + +bool torture_samba3_caseinsensitive(struct torture_context *torture, struct smbcli_state *cli) +{ + TALLOC_CTX *mem_ctx; + const char *dirname = "insensitive"; + const char *ucase_dirname = "InSeNsItIvE"; + const char *fname = "foo"; + char *fpath; + int fnum; + int counter = 0; + bool ret = false; + + if (!(mem_ctx = talloc_init("torture_samba3_caseinsensitive"))) { + torture_result(torture, TORTURE_FAIL, "talloc_init failed\n"); + return false; + } + + torture_assert(torture, torture_setup_dir(cli, dirname), "creating test directory"); + + if (!(fpath = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname))) { + goto done; + } + fnum = smbcli_open(cli->tree, fpath, O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_result(torture, TORTURE_FAIL, + "Could not create file %s: %s", fpath, + smbcli_errstr(cli->tree)); + goto done; + } + smbcli_close(cli->tree, fnum); + + smbcli_list(cli->tree, talloc_asprintf( + mem_ctx, "%s\\*", ucase_dirname), + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN + |FILE_ATTRIBUTE_SYSTEM, + count_fn, (void *)&counter); + + if (counter == 3) { + ret = true; + } + else { + torture_result(torture, TORTURE_FAIL, + "expected 3 entries, got %d", counter); + ret = false; + } + + done: + talloc_free(mem_ctx); + return ret; +} + +static void close_locked_file(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + int *pfd = (int *)private_data; + + TALLOC_FREE(te); + + if (*pfd != -1) { + close(*pfd); + *pfd = -1; + } +} + +struct lock_result_state { + NTSTATUS status; + bool done; +}; + +static void receive_lock_result(struct smbcli_request *req) +{ + struct lock_result_state *state = + (struct lock_result_state *)req->async.private_data; + + state->status = smbcli_request_simple_recv(req); + state->done = true; +} + +/* + * Check that Samba3 correctly deals with conflicting local posix byte range + * locks on an underlying file via "normal" SMB1 (without unix extensions). + * + * Note: This test depends on "posix locking = yes". + * Note: To run this test, use "--option=torture:localdir=" + */ + +bool torture_samba3_posixtimedlock(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + const char *dirname = "posixlock"; + const char *fname = "locked"; + const char *fpath; + const char *localdir; + const char *localname; + int fnum = -1; + + int fd = -1; + struct flock posix_lock; + + union smb_lock io; + struct smb_lock_entry lock_entry; + struct smbcli_request *req; + struct lock_result_state lock_result; + + struct tevent_timer *te; + + torture_assert(tctx, torture_setup_dir(cli, dirname), "creating test directory"); + + if (!(fpath = talloc_asprintf(tctx, "%s\\%s", dirname, fname))) { + torture_warning(tctx, "talloc failed\n"); + ret = false; + goto done; + } + fnum = smbcli_open(cli->tree, fpath, O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_warning(tctx, "Could not create file %s: %s\n", fpath, + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + if (!(localdir = torture_setting_string(tctx, "localdir", NULL))) { + torture_warning(tctx, "Need 'localdir' setting\n"); + ret = false; + goto done; + } + + if (!(localname = talloc_asprintf(tctx, "%s/%s/%s", localdir, dirname, + fname))) { + torture_warning(tctx, "talloc failed\n"); + ret = false; + goto done; + } + + /* + * Lock a byte range from posix + */ + + fd = open(localname, O_RDWR); + if (fd == -1) { + torture_warning(tctx, "open(%s) failed: %s\n", + localname, strerror(errno)); + goto done; + } + + posix_lock.l_type = F_WRLCK; + posix_lock.l_whence = SEEK_SET; + posix_lock.l_start = 0; + posix_lock.l_len = 1; + + if (fcntl(fd, F_SETLK, &posix_lock) == -1) { + torture_warning(tctx, "fcntl failed: %s\n", strerror(errno)); + ret = false; + goto done; + } + + /* + * Try a cifs brlock without timeout to see if posix locking = yes + */ + + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + + lock_entry.count = 1; + lock_entry.offset = 0; + lock_entry.pid = cli->tree->session->pid; + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.locks = &lock_entry; + io.lockx.in.file.fnum = fnum; + + status = smb_raw_lock(cli->tree, &io); + + ret = true; + CHECK_STATUS(tctx, status, NT_STATUS_LOCK_NOT_GRANTED); + + if (!ret) { + goto done; + } + + /* + * Now fire off a timed brlock, unlock the posix lock and see if the + * timed lock gets through. + */ + + io.lockx.in.timeout = 5000; + + req = smb_raw_lock_send(cli->tree, &io); + if (req == NULL) { + torture_warning(tctx, "smb_raw_lock_send failed\n"); + ret = false; + goto done; + } + + lock_result.done = false; + req->async.fn = receive_lock_result; + req->async.private_data = &lock_result; + + te = tevent_add_timer(tctx->ev, + tctx, timeval_current_ofs(1, 0), + close_locked_file, &fd); + if (te == NULL) { + torture_warning(tctx, "tevent_add_timer failed\n"); + ret = false; + goto done; + } + + while ((fd != -1) || (!lock_result.done)) { + if (tevent_loop_once(tctx->ev) == -1) { + torture_warning(tctx, "tevent_loop_once failed: %s\n", + strerror(errno)); + ret = false; + goto done; + } + } + + CHECK_STATUS(tctx, lock_result.status, NT_STATUS_OK); + + done: + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + if (fd != -1) { + close(fd); + } + smbcli_deltree(cli->tree, dirname); + return ret; +} + +bool torture_samba3_rootdirfid(struct torture_context *tctx, struct smbcli_state *cli) +{ + uint16_t dnum; + union smb_open io; + const char *fname = "testfile"; + bool ret = false; + + smbcli_unlink(cli->tree, fname); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_SYNCHRONIZE | SEC_FILE_EXECUTE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ + | NTCREATEX_SHARE_ACCESS_READ; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = "\\"; + torture_assert_ntstatus_equal_goto(tctx, smb_raw_open(cli->tree, tctx, &io), + NT_STATUS_OK, + ret, done, "smb_open on the directory failed: %s\n"); + + dnum = io.ntcreatex.out.file.fnum; + + io.ntcreatex.in.flags = + NTCREATEX_FLAGS_REQUEST_OPLOCK + | NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.root_fid.fnum = dnum; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = fname; + + torture_assert_ntstatus_equal_goto(tctx, smb_raw_open(cli->tree, tctx, &io), + NT_STATUS_OK, + ret, done, "smb_open on the file failed"); + + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_close(cli->tree, dnum); + smbcli_unlink(cli->tree, fname); + + ret = true; + done: + return ret; +} + +bool torture_samba3_rootdirfid2(struct torture_context *tctx, struct smbcli_state *cli) +{ + int fnum; + uint16_t dnum; + union smb_open io; + const char *dirname1 = "dir1"; + const char *dirname2 = "dir1/dir2"; + const char *path = "dir1/dir2/testfile"; + const char *relname = "dir2/testfile"; + bool ret = false; + + smbcli_deltree(cli->tree, dirname1); + + torture_assert(tctx, torture_setup_dir(cli, dirname1), "creating test directory"); + torture_assert(tctx, torture_setup_dir(cli, dirname2), "creating test directory"); + + fnum = smbcli_open(cli->tree, path, O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_result(tctx, TORTURE_FAIL, + "Could not create file: %s", + smbcli_errstr(cli->tree)); + goto done; + } + smbcli_close(cli->tree, fnum); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_SYNCHRONIZE | SEC_FILE_EXECUTE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ + | NTCREATEX_SHARE_ACCESS_READ; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = dirname1; + torture_assert_ntstatus_equal_goto(tctx, smb_raw_open(cli->tree, tctx, &io), + NT_STATUS_OK, + ret, done, "smb_open on the directory failed: %s\n"); + + dnum = io.ntcreatex.out.file.fnum; + + io.ntcreatex.in.flags = + NTCREATEX_FLAGS_REQUEST_OPLOCK + | NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.root_fid.fnum = dnum; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = relname; + + torture_assert_ntstatus_equal_goto(tctx, smb_raw_open(cli->tree, tctx, &io), + NT_STATUS_OK, + ret, done, "smb_open on the file failed"); + + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_close(cli->tree, dnum); + + ret = true; +done: + smbcli_deltree(cli->tree, dirname1); + return ret; +} + +bool torture_samba3_oplock_logoff(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_open io; + const char *fname = "testfile"; + bool ret = false; + struct smbcli_request *req; + struct smb_echo echo_req; + + smbcli_unlink(cli->tree, fname); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.access_mask = + SEC_STD_SYNCHRONIZE | SEC_FILE_EXECUTE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.fname = "testfile"; + torture_assert_ntstatus_equal_goto(tctx, smb_raw_open(cli->tree, tctx, &io), + NT_STATUS_OK, + ret, done, "first smb_open on the file failed"); + + /* + * Create a conflicting open, causing the one-second delay + */ + + torture_assert_goto(tctx, req = smb_raw_open_send(cli->tree, &io), + ret, done, "smb_raw_open_send on the file failed"); + + /* + * Pull the VUID from under that request. As of Nov 3, 2008 all Samba3 + * versions (3.0, 3.2 and master) would spin sending ERRinvuid errors + * as long as the client is still connected. + */ + + torture_assert_ntstatus_equal_goto(tctx, smb_raw_ulogoff(cli->session), + NT_STATUS_OK, + ret, done, "ulogoff failed failed"); + + echo_req.in.repeat_count = 1; + echo_req.in.size = 1; + echo_req.in.data = discard_const_p(uint8_t, ""); + + torture_assert_ntstatus_equal_goto(tctx, smb_raw_echo(cli->session->transport, &echo_req), + NT_STATUS_OK, + ret, done, "smb_raw_echo failed"); + + ret = true; + done: + return ret; +} + +bool torture_samba3_check_openX_badname(struct torture_context *tctx, struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = false; + int fnum = -1; + DATA_BLOB name_blob = data_blob_talloc(cli->tree, NULL, 65535); + + if (name_blob.data == NULL) { + return false; + } + memset(name_blob.data, 0xcc, 65535); + status = raw_smbcli_openX_name_blob(cli->tree, &name_blob, O_RDWR, DENY_NONE, &fnum); + CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_INVALID); + ret = true; + + return ret; +} diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c new file mode 100644 index 0000000..575bbd0 --- /dev/null +++ b/source4/torture/raw/search.c @@ -0,0 +1,1653 @@ +/* + Unix SMB/CIFS implementation. + RAW_SEARCH_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/util/tsort.h" +#include "torture/raw/proto.h" + +#undef strncasecmp + +#define BASEDIR "\\testsearch" + +#define CHECK_STATUS_LEVEL(__tctx, __status, __level, __supp) \ + do { \ + if (NT_STATUS_EQUAL(__status, \ + NT_STATUS_NOT_SUPPORTED) || \ + NT_STATUS_EQUAL(__status, \ + NT_STATUS_NOT_IMPLEMENTED)) { \ + __supp = false; \ + } else { \ + __supp = true; \ + } \ + if (__supp) { \ + torture_assert_ntstatus_ok_goto(__tctx, \ + __status, ret, done, #__level" failed"); \ + } else { \ + torture_warning(__tctx, "(%s) Info " \ + "level "#__level" is %s", \ + __location__, nt_errstr(__status)); \ + } \ + } while (0) + +/* + callback function for single_search +*/ +static bool single_search_callback(void *private_data, const union smb_search_data *file) +{ + union smb_search_data *data = (union smb_search_data *)private_data; + + *data = *file; + + return true; +} + +/* + do a single file (non-wildcard) search +*/ +NTSTATUS torture_single_search(struct smbcli_state *cli, + TALLOC_CTX *tctx, + const char *pattern, + enum smb_search_level level, + enum smb_search_data_level data_level, + uint16_t attrib, + union smb_search_data *data) +{ + union smb_search_first io; + union smb_search_close c; + NTSTATUS status; + + switch (level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_FFIRST: + case RAW_SEARCH_FUNIQUE: + io.search_first.level = level; + io.search_first.data_level = RAW_SEARCH_DATA_SEARCH; + io.search_first.in.max_count = 1; + io.search_first.in.search_attrib = attrib; + io.search_first.in.pattern = pattern; + break; + + case RAW_SEARCH_TRANS2: + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = data_level; + io.t2ffirst.in.search_attrib = attrib; + io.t2ffirst.in.max_count = 1; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + break; + + case RAW_SEARCH_SMB2: + return NT_STATUS_INVALID_LEVEL; + } + + status = smb_raw_search_first(cli->tree, tctx, + &io, (void *)data, single_search_callback); + + if (NT_STATUS_IS_OK(status) && level == RAW_SEARCH_FFIRST) { + c.fclose.level = RAW_FINDCLOSE_FCLOSE; + c.fclose.in.max_count = 1; + c.fclose.in.search_attrib = 0; + c.fclose.in.id = data->search.id; + status = smb_raw_search_close(cli->tree, &c); + } + + return status; +} + + +static struct { + const char *name; + enum smb_search_level level; + enum smb_search_data_level data_level; + int name_offset; + int resume_key_offset; + uint32_t capability_mask; + NTSTATUS status; + union smb_search_data data; +} levels[] = { + { + .name = "FFIRST", + .level = RAW_SEARCH_FFIRST, + .data_level = RAW_SEARCH_DATA_SEARCH, + .name_offset = offsetof(union smb_search_data, + search.name), + .resume_key_offset = -1, + }, + { + .name = "FUNIQUE", + .level = RAW_SEARCH_FUNIQUE, + .data_level = RAW_SEARCH_DATA_SEARCH, + .name_offset = offsetof(union smb_search_data, + search.name), + .resume_key_offset = -1, + }, + { + .name = "SEARCH", + .level = RAW_SEARCH_SEARCH, + .data_level = RAW_SEARCH_DATA_SEARCH, + .name_offset = offsetof(union smb_search_data, + search.name), + .resume_key_offset = -1, + }, + { + .name = "STANDARD", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_STANDARD, + .name_offset = offsetof(union smb_search_data, + standard.name.s), + .resume_key_offset = offsetof(union smb_search_data, + standard.resume_key), + }, + { + .name = "EA_SIZE", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_EA_SIZE, + .name_offset = offsetof(union smb_search_data, + ea_size.name.s), + .resume_key_offset = offsetof(union smb_search_data, + ea_size.resume_key), + }, + { + .name = "DIRECTORY_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + directory_info.file_index), + }, + { + .name = "FULL_DIRECTORY_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + full_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + full_directory_info.file_index), + }, + { + .name = "NAME_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_NAME_INFO, + .name_offset = offsetof(union smb_search_data, + name_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + name_info.file_index), + }, + { + .name = "BOTH_DIRECTORY_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + both_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + both_directory_info.file_index), + }, + { + .name = "ID_FULL_DIRECTORY_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + id_full_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + id_full_directory_info.file_index), + }, + { + .name = "ID_BOTH_DIRECTORY_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + id_both_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + id_both_directory_info.file_index), + }, + { + .name = "UNIX_INFO", + .level = RAW_SEARCH_TRANS2, + .data_level = RAW_SEARCH_DATA_UNIX_INFO, + .name_offset = offsetof(union smb_search_data, + unix_info.name), + .resume_key_offset = offsetof(union smb_search_data, + unix_info.file_index), + .capability_mask = CAP_UNIX + }, +}; + + +/* + return level name +*/ +static const char *level_name(enum smb_search_level level, + enum smb_search_data_level data_level) +{ + int i; + for (i=0;itransport->negotiate.capabilities; + + if ((cap & CAP_UNIX) == 0) { + /* + * Server doesn't support SMB1+POSIX. + * The caller will skip the UNIX info + * level anyway. + */ + torture_comment(tctx, + "Server doesn't support SMB1+POSIX\n"); + return NT_STATUS_OK; + } + + /* Setup POSIX on this connection. */ + SSVAL(data, 0, CIFS_UNIX_MAJOR_VERSION); + SSVAL(data, 2, CIFS_UNIX_MINOR_VERSION); + SBVAL(data,4,((uint64_t)( + CIFS_UNIX_POSIX_ACLS_CAP| + CIFS_UNIX_POSIX_PATHNAMES_CAP| + CIFS_UNIX_FCNTL_LOCKS_CAP| + CIFS_UNIX_EXTATTR_CAP| + CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP))); + setup = TRANSACT2_SETFSINFO; + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 0; + tp.in.max_data = 0; + tp.in.setup = &setup; + tp.in.trans_name = NULL; + SSVAL(params, 0, 0); + SSVAL(params, 2, SMB_SET_CIFS_UNIX_INFO); + tp.in.params = data_blob_talloc(tctx, params, 4); + tp.in.data = data_blob_talloc(tctx, data, 12); + return smb_raw_trans2(cli_unix->tree, tctx, &tp); +} + +/* + basic testing of all RAW_SEARCH_* calls using a single file +*/ +static bool test_one_file(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli_unix) +{ + bool ret = true; + int fnum; + const char *fname = "torture_search.txt"; + const char *fname2 = "torture_search-NOTEXIST.txt"; + NTSTATUS status; + int i; + union smb_fileinfo all_info, alt_info, name_info, internal_info; + bool all_info_supported, alt_info_supported, name_info_supported, + internal_info_supported; + union smb_search_data *s; + + status = setup_smb1_posix(tctx, cli_unix); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, + TORTURE_FAIL, + __location__"setup_smb1_posix() failed (%s)\n", + nt_errstr(status)); + ret = false; + goto done; + } + + fnum = create_complex_file(cli, tctx, fname); + if (fnum == -1) { + torture_result(tctx, + TORTURE_FAIL, + __location__"ERROR: open of %s failed (%s)\n", + fname, + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + /* call all the levels */ + for (i=0;itransport->negotiate.capabilities; + struct smbcli_state *cli_search = cli; + + torture_comment(tctx, "Testing %s\n", levels[i].name); + + if (levels[i].data_level == RAW_SEARCH_DATA_UNIX_INFO) { + /* + * For an SMB1+POSIX info level, use the cli_unix + * connection. + */ + cli_search = cli_unix; + } + + levels[i].status = torture_single_search(cli_search, tctx, fname, + levels[i].level, + levels[i].data_level, + 0, + &levels[i].data); + + /* see if this server claims to support this level */ + if (((cap & levels[i].capability_mask) != levels[i].capability_mask) + || NT_STATUS_EQUAL(levels[i].status, NT_STATUS_NOT_SUPPORTED)) { + printf("search level %s(%d) not supported by server\n", + levels[i].name, (int)levels[i].level); + continue; + } + + if (!NT_STATUS_IS_OK(levels[i].status)) { + torture_result(tctx, + TORTURE_FAIL, + __location__"search level %s(%d) failed - %s\n", + levels[i].name, (int)levels[i].level, + nt_errstr(levels[i].status)); + ret = false; + continue; + } + + status = torture_single_search(cli_search, tctx, fname2, + levels[i].level, + levels[i].data_level, + 0, + &levels[i].data); + + expected_status = NT_STATUS_NO_SUCH_FILE; + if (levels[i].level == RAW_SEARCH_SEARCH || + levels[i].level == RAW_SEARCH_FFIRST || + levels[i].level == RAW_SEARCH_FUNIQUE) { + expected_status = STATUS_NO_MORE_FILES; + } + if (!NT_STATUS_EQUAL(status, expected_status)) { + torture_result(tctx, + TORTURE_FAIL, + __location__"search level %s(%d) should fail with %s - %s\n", + levels[i].name, (int)levels[i].level, + nt_errstr(expected_status), + nt_errstr(status)); + ret = false; + } + } + + /* get the all_info file into to check against */ + all_info.generic.level = RAW_FILEINFO_ALL_INFO; + all_info.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &all_info); + CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_ALL_INFO", + all_info_supported); + + alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO; + alt_info.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &alt_info); + CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_ALT_NAME_INFO", + alt_info_supported); + + internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION; + internal_info.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &internal_info); + CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_INTERNAL_INFORMATION", + internal_info_supported); + + name_info.generic.level = RAW_FILEINFO_NAME_INFO; + name_info.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &name_info); + CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_NAME_INFO", + name_info_supported); + +#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if ((s->sname1.field1) != (v.sname2.out.field2)) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \ + __location__, \ + #sname1, #field1, (int)s->sname1.field1, \ + #sname2, #field2, (int)v.sname2.out.field2); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != (~1 & nt_time_to_unix(v.sname2.out.field2))) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, timestring(tctx, s->sname1.field1), \ + #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != v.sname2.out.field2) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, nt_time_string(tctx, s->sname1.field1), \ + #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \ + wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1.s, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, fname) || \ + wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1.s, \ + fname); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || \ + strcmp(s->sname1.field1, fname)) { \ + torture_result(tctx,\ + TORTURE_FAIL,\ + "(%s) %s/%s [%s] != %s\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1, \ + fname); \ + ret = false; \ + } \ + }} while (0) + + /* check that all the results are as expected */ + CHECK_VAL("SEARCH", search, attrib, all_info, all_info, attrib&0xFFF); + CHECK_VAL("STANDARD", standard, attrib, all_info, all_info, attrib&0xFFF); + CHECK_VAL("EA_SIZE", ea_size, attrib, all_info, all_info, attrib&0xFFF); + CHECK_VAL("DIRECTORY_INFO", directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info, all_info, attrib); + + CHECK_TIME("SEARCH", search, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, write_time, all_info, all_info, write_time); + CHECK_TIME("EA_SIZE", ea_size, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, create_time, all_info, all_info, create_time); + CHECK_TIME("EA_SIZE", ea_size, create_time, all_info, all_info, create_time); + CHECK_TIME("STANDARD", standard, access_time, all_info, all_info, access_time); + CHECK_TIME("EA_SIZE", ea_size, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info, all_info, write_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info, all_info, create_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, change_time, all_info, all_info, change_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info, all_info, change_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info, all_info, change_time); + CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info, all_info, change_time); + CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info, all_info, change_time); + + CHECK_VAL("SEARCH", search, size, all_info, all_info, size); + CHECK_VAL("STANDARD", standard, size, all_info, all_info, size); + CHECK_VAL("EA_SIZE", ea_size, size, all_info, all_info, size); + CHECK_VAL("DIRECTORY_INFO", directory_info, size, all_info, all_info, size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size); + CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info, all_info, size); + CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info, all_info, size); + CHECK_VAL("UNIX_INFO", unix_info, size, all_info, all_info, size); + + CHECK_VAL("STANDARD", standard, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("EA_SIZE", ea_size, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("DIRECTORY_INFO", directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("UNIX_INFO", unix_info, alloc_size, all_info, all_info, alloc_size); + + CHECK_VAL("EA_SIZE", ea_size, ea_size, all_info, all_info, ea_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info, all_info, ea_size); + + if (alt_info_supported) { + CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, + fname); + CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, + short_name, alt_info, alt_name_info, fname, STR_UNICODE); + } + + CHECK_NAME("STANDARD", standard, name, fname, 0); + CHECK_NAME("EA_SIZE", ea_size, name, fname, 0); + CHECK_NAME("DIRECTORY_INFO", directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname, STR_TERMINATE_ASCII); + + if (name_info_supported) { + CHECK_NAME("NAME_INFO", name_info, name, fname, + STR_TERMINATE_ASCII); + } + + CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_UNIX_NAME("UNIX_INFO", unix_info, name, fname, STR_TERMINATE_ASCII); + + if (internal_info_supported) { + CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, + file_id, internal_info, internal_information, file_id); + CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, + file_id, internal_info, internal_information, file_id); + } + +done: + smb_raw_exit(cli->session); + smbcli_unlink(cli->tree, fname); + + return ret; +} + + +struct multiple_result { + TALLOC_CTX *tctx; + int count; + union smb_search_data *list; +}; + +/* + callback function for multiple_search +*/ +static bool multiple_search_callback(void *private_data, const union smb_search_data *file) +{ + struct multiple_result *data = (struct multiple_result *)private_data; + + + data->count++; + data->list = talloc_realloc(data->tctx, + data->list, + union smb_search_data, + data->count); + + data->list[data->count-1] = *file; + + return true; +} + +enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY}; + +/* + do a single file (non-wildcard) search +*/ +static NTSTATUS multiple_search(struct smbcli_state *cli, + TALLOC_CTX *tctx, + const char *pattern, + enum smb_search_data_level data_level, + enum continue_type cont_type, + void *data) +{ + union smb_search_first io; + union smb_search_next io2; + NTSTATUS status; + const int per_search = 100; + struct multiple_result *result = (struct multiple_result *)data; + + if (data_level == RAW_SEARCH_DATA_SEARCH) { + io.search_first.level = RAW_SEARCH_SEARCH; + io.search_first.data_level = RAW_SEARCH_DATA_SEARCH; + io.search_first.in.max_count = per_search; + io.search_first.in.search_attrib = 0; + io.search_first.in.pattern = pattern; + } else { + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = data_level; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = per_search; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + if (cont_type == CONT_RESUME_KEY) { + io.t2ffirst.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME | + FLAG_TRANS2_FIND_BACKUP_INTENT; + } + } + + status = smb_raw_search_first(cli->tree, tctx, + &io, data, multiple_search_callback); + + + while (NT_STATUS_IS_OK(status)) { + if (data_level == RAW_SEARCH_DATA_SEARCH) { + io2.search_next.level = RAW_SEARCH_SEARCH; + io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH; + io2.search_next.in.max_count = per_search; + io2.search_next.in.search_attrib = 0; + io2.search_next.in.id = result->list[result->count-1].search.id; + } else { + io2.t2fnext.level = RAW_SEARCH_TRANS2; + io2.t2fnext.data_level = data_level; + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = per_search; + io2.t2fnext.in.resume_key = 0; + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; + io2.t2fnext.in.last_name = ""; + switch (cont_type) { + case CONT_RESUME_KEY: + io2.t2fnext.in.resume_key = extract_resume_key(&result->list[result->count-1], + io2.t2fnext.level, io2.t2fnext.data_level); + if (io2.t2fnext.in.resume_key == 0) { + printf("Server does not support resume by key for level %s\n", + level_name(io2.t2fnext.level, io2.t2fnext.data_level)); + return NT_STATUS_NOT_SUPPORTED; + } + io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME | + FLAG_TRANS2_FIND_BACKUP_INTENT; + break; + case CONT_NAME: + io2.t2fnext.in.last_name = extract_name(&result->list[result->count-1], + io2.t2fnext.level, io2.t2fnext.data_level); + break; + case CONT_FLAGS: + io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_CONTINUE; + break; + } + } + + status = smb_raw_search_next(cli->tree, tctx, + &io2, data, multiple_search_callback); + if (!NT_STATUS_IS_OK(status)) { + break; + } + if (data_level == RAW_SEARCH_DATA_SEARCH) { + if (io2.search_next.out.count == 0) { + break; + } + } else if (io2.t2fnext.out.count == 0 || + io2.t2fnext.out.end_of_search) { + break; + } + } + + return status; +} + +#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status") + +#define CHECK_VALUE(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value"); + +#define CHECK_STRING(v, correct) torture_assert_casestr_equal(tctx, v, correct, "incorrect value"); + + +static enum smb_search_data_level compare_data_level; + +static int search_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + const char *s1, *s2; + enum smb_search_level level; + + if (compare_data_level == RAW_SEARCH_DATA_SEARCH) { + level = RAW_SEARCH_SEARCH; + } else { + level = RAW_SEARCH_TRANS2; + } + + s1 = extract_name(d1, level, compare_data_level); + s2 = extract_name(d2, level, compare_data_level); + return strcmp_safe(s1, s2); +} + + + +/* + basic testing of search calls using many files +*/ +static bool test_many_files(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const int num_files = 700; + int i, fnum, t; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + struct { + const char *name; + const char *cont_name; + enum smb_search_data_level data_level; + enum continue_type cont_type; + } search_types[] = { + {"SEARCH", "ID", RAW_SEARCH_DATA_SEARCH, CONT_RESUME_KEY}, + {"BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_NAME}, + {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_FLAGS}, + {"BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"STANDARD", "FLAGS", RAW_SEARCH_DATA_STANDARD, CONT_FLAGS}, + {"STANDARD", "KEY", RAW_SEARCH_DATA_STANDARD, CONT_RESUME_KEY}, + {"STANDARD", "NAME", RAW_SEARCH_DATA_STANDARD, CONT_NAME}, + {"EA_SIZE", "FLAGS", RAW_SEARCH_DATA_EA_SIZE, CONT_FLAGS}, + {"EA_SIZE", "KEY", RAW_SEARCH_DATA_EA_SIZE, CONT_RESUME_KEY}, + {"EA_SIZE", "NAME", RAW_SEARCH_DATA_EA_SIZE, CONT_NAME}, + {"DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_FLAGS}, + {"DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_NAME}, + {"FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_FLAGS}, + {"FULL_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"FULL_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_NAME}, + {"ID_FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_FLAGS}, + {"ID_FULL_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"ID_FULL_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_NAME}, + {"ID_BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_NAME}, + {"ID_BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_FLAGS}, + {"ID_BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY} + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Testing with %d files\n", num_files); + + for (i=0;itree, fname, O_CREAT|O_RDWR, DENY_NONE); + torture_assert(tctx, fnum != -1, "Failed to create"); + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + + for (t=0;tsession); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + check a individual file result +*/ +static bool check_result(struct multiple_result *result, const char *name, bool exist, uint32_t attrib) +{ + int i; + for (i=0;icount;i++) { + if (strcmp(name, result->list[i].both_directory_info.name.s) == 0) break; + } + if (i == result->count) { + if (exist) { + printf("failed: '%s' should exist with attribute %s\n", + name, attrib_string(result->list, attrib)); + return false; + } + return true; + } + + if (!exist) { + printf("failed: '%s' should NOT exist (has attribute %s)\n", + name, attrib_string(result->list, result->list[i].both_directory_info.attrib)); + return false; + } + + if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) { + printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n", + name, + attrib, result->list[i].both_directory_info.attrib); + return false; + } + return true; +} + +/* + test what happens when the directory is modified during a search +*/ +static bool test_modify_search(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const int num_files = 20; + int i, fnum; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + union smb_search_first io; + union smb_search_next io2; + union smb_setfileinfo sfinfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Creating %d files\n", num_files); + + for (i=num_files-1;i>=0;i--) { + fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i); + fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + printf("pulling the first file\n"); + ZERO_STRUCT(result); + result.tctx = talloc_new(tctx); + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 0; + io.t2ffirst.in.flags = 0; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = BASEDIR "\\*.*"; + + status = smb_raw_search_first(cli->tree, tctx, + &io, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 1); + + printf("pulling the second file\n"); + io2.t2fnext.level = RAW_SEARCH_TRANS2; + io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = 1; + io2.t2fnext.in.resume_key = 0; + io2.t2fnext.in.flags = 0; + io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s; + + status = smb_raw_search_next(cli->tree, tctx, + &io2, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 2); + + result.count = 0; + + printf("Changing attributes and deleting\n"); + smbcli_open(cli->tree, BASEDIR "\\T003-03.txt.2", O_CREAT|O_RDWR, DENY_NONE); + smbcli_open(cli->tree, BASEDIR "\\T013-13.txt.2", O_CREAT|O_RDWR, DENY_NONE); + fnum = create_complex_file(cli, tctx, BASEDIR "\\T013-13.txt.3"); + smbcli_unlink(cli->tree, BASEDIR "\\T014-14.txt"); + torture_set_file_attribute(cli->tree, BASEDIR "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN); + torture_set_file_attribute(cli->tree, BASEDIR "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL); + torture_set_file_attribute(cli->tree, BASEDIR "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM); + torture_set_file_attribute(cli->tree, BASEDIR "\\T018-18.txt", 0); + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + io2.t2fnext.level = RAW_SEARCH_TRANS2; + io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = num_files + 3; + io2.t2fnext.in.resume_key = 0; + io2.t2fnext.in.flags = 0; + io2.t2fnext.in.last_name = "."; + + status = smb_raw_search_next(cli->tree, tctx, + &io2, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 20); + + ret &= check_result(&result, "t009-9.txt", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(&result, "t014-14.txt", false, 0); + ret &= check_result(&result, "t015-15.txt", false, 0); + ret &= check_result(&result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL); + ret &= check_result(&result, "t017-17.txt", false, 0); + ret &= check_result(&result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(&result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(&result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(&result, "T003-3.txt.2", false, 0); + ret &= check_result(&result, "T013-13.txt.3", true, FILE_ATTRIBUTE_ARCHIVE); + + if (!ret) { + for (i=0;isession); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + testing if directories always come back sorted +*/ +static bool test_sorted(struct torture_context *tctx, struct smbcli_state *cli) +{ + const int num_files = 700; + int i, fnum; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Creating %d files\n", num_files); + + for (i=0;itree, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + + ZERO_STRUCT(result); + result.tctx = tctx; + + status = multiple_search(cli, tctx, BASEDIR "\\*.*", + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, + CONT_NAME, &result); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, num_files); + + for (i=0;i 0) { + printf("non-alphabetical order at entry %d '%s' '%s'\n", + i, name1, name2); + printf("Server does not produce sorted directory listings (not an error)\n"); + goto done; + } + } + + talloc_free(result.list); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + + +/* + basic testing of many old style search calls using separate dirs +*/ +static bool test_many_dirs(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const int num_dirs = 20; + int i, fnum, n; + char *fname, *dname; + bool ret = true; + NTSTATUS status; + union smb_search_data *file, *file2, *file3; + + if (!torture_setting_bool(tctx, "raw_search_search", true)) { + torture_comment(tctx, "Skipping these tests as the server " + "doesn't support old style search calls\n"); + return true; + } + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Creating %d dirs\n", num_dirs); + + for (i=0;itree, dname); + if (!NT_STATUS_IS_OK(status)) { + printf("(%s) Failed to create %s - %s\n", + __location__, dname, nt_errstr(status)); + ret = false; + goto done; + } + + for (n=0;n<3;n++) { + fname = talloc_asprintf(cli, BASEDIR "\\d%d\\f%d-%d.txt", i, i, n); + fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + printf("(%s) Failed to create %s - %s\n", + __location__, fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + talloc_free(dname); + } + + file = talloc_zero_array(tctx, union smb_search_data, num_dirs); + file2 = talloc_zero_array(tctx, union smb_search_data, num_dirs); + file3 = talloc_zero_array(tctx, union smb_search_data, num_dirs); + + printf("Search first on %d dirs\n", num_dirs); + + for (i=0;itree, tctx, + &io, (void *)&file[i], single_search_callback); + if (io.search_first.out.count != 1) { + printf("(%s) search first gave %d entries for dir %d - %s\n", + __location__, io.search_first.out.count, i, nt_errstr(status)); + ret = false; + goto done; + } + CHECK_STATUS(status, NT_STATUS_OK); + if (strncasecmp(file[i].search.name, fname, strlen(fname)) != 0) { + printf("(%s) incorrect name '%s' expected '%s'[12].txt\n", + __location__, file[i].search.name, fname); + ret = false; + goto done; + } + + talloc_free(fname); + } + + printf("Search next on %d dirs\n", num_dirs); + + for (i=0;itree, tctx, + &io2, (void *)&file2[i], single_search_callback); + if (io2.search_next.out.count != 1) { + printf("(%s) search next gave %d entries for dir %d - %s\n", + __location__, io2.search_next.out.count, i, nt_errstr(status)); + ret = false; + goto done; + } + CHECK_STATUS(status, NT_STATUS_OK); + if (strncasecmp(file2[i].search.name, fname, strlen(fname)) != 0) { + printf("(%s) incorrect name '%s' expected '%s'[12].txt\n", + __location__, file2[i].search.name, fname); + ret = false; + goto done; + } + + talloc_free(fname); + } + + + printf("Search next (rewind) on %d dirs\n", num_dirs); + + for (i=0;itree, tctx, + &io2, (void *)&file3[i], single_search_callback); + if (io2.search_next.out.count != 1) { + printf("(%s) search next gave %d entries for dir %d - %s\n", + __location__, io2.search_next.out.count, i, nt_errstr(status)); + ret = false; + goto done; + } + CHECK_STATUS(status, NT_STATUS_OK); + + if (strncasecmp(file3[i].search.name, file2[i].search.name, 3) != 0) { + printf("(%s) incorrect name '%s' on rewind at dir %d\n", + __location__, file2[i].search.name, i); + ret = false; + goto done; + } + + if (torture_setting_bool(tctx, "rewind_support", true) && + strcmp(file3[i].search.name, file2[i].search.name) != 0) { + printf("(%s) server did not rewind - got '%s' expected '%s'\n", + __location__, file3[i].search.name, file2[i].search.name); + ret = false; + goto done; + } + + talloc_free(fname); + } + + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +/* + testing of OS/2 style delete +*/ +static bool test_os2_delete(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const int num_files = 700; + const int delete_count = 4; + int total_deleted = 0; + int i, fnum; + char *fname; + bool ret = true; + NTSTATUS status; + union smb_search_first io; + union smb_search_next io2; + struct multiple_result result; + + if (!torture_setting_bool(tctx, "search_ea_size", true)){ + torture_comment(tctx, + "Server does not support RAW_SEARCH_EA_SIZE " + "level. Skipping this test\n"); + return true; + } + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing OS/2 style delete on %d files\n", num_files); + + for (i=0;itree, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + + ZERO_STRUCT(result); + result.tctx = tctx; + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_SIZE; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 100; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = BASEDIR "\\*"; + + status = smb_raw_search_first(cli->tree, tctx, + &io, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + + for (i=0;itree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + total_deleted++; + talloc_free(fname); + } + + io2.t2fnext.level = RAW_SEARCH_TRANS2; + io2.t2fnext.data_level = RAW_SEARCH_DATA_EA_SIZE; + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = 100; + io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key; + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME; + io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s; + + do { + ZERO_STRUCT(result); + result.tctx = tctx; + + status = smb_raw_search_next(cli->tree, tctx, + &io2, &result, multiple_search_callback); + if (!NT_STATUS_IS_OK(status)) { + break; + } + + for (i=0;itree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + total_deleted++; + talloc_free(fname); + } + + if (i>0) { + io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key; + io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s; + } + } while (NT_STATUS_IS_OK(status) && result.count != 0); + + CHECK_STATUS(status, NT_STATUS_OK); + + if (total_deleted != num_files) { + printf("error: deleted %d - expected to delete %d\n", + total_deleted, num_files); + ret = false; + } + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + + +static int ealist_cmp(union smb_search_data *r1, union smb_search_data *r2) +{ + return strcmp(r1->ea_list.name.s, r2->ea_list.name.s); +} + +/* + testing of the rather strange ea_list level +*/ +static bool test_ea_list(struct torture_context *tctx, + struct smbcli_state *cli) +{ + int fnum; + bool ret = true; + NTSTATUS status; + union smb_search_first io; + union smb_search_next nxt; + struct multiple_result result; + union smb_setfileinfo setfile; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Testing RAW_SEARCH_EA_LIST level\n"); + + if (!torture_setting_bool(tctx, "search_ea_support", true) || + !torture_setting_bool(tctx, "ea_support", true)) { + printf("..skipped per target configuration.\n"); + return true; + } + + fnum = smbcli_open(cli->tree, BASEDIR "\\file1.txt", O_CREAT|O_RDWR, DENY_NONE); + smbcli_close(cli->tree, fnum); + + fnum = smbcli_open(cli->tree, BASEDIR "\\file2.txt", O_CREAT|O_RDWR, DENY_NONE); + smbcli_close(cli->tree, fnum); + + fnum = smbcli_open(cli->tree, BASEDIR "\\file3.txt", O_CREAT|O_RDWR, DENY_NONE); + smbcli_close(cli->tree, fnum); + + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.path = BASEDIR "\\file2.txt"; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EA ONE"; + setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE 1"); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECOND EA"; + setfile.ea_set.in.eas[1].value = data_blob_string_const("Value Two"); + + status = smb_raw_setpathinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + setfile.generic.in.file.path = BASEDIR "\\file3.txt"; + status = smb_raw_setpathinfo(cli->tree, &setfile); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(result); + result.tctx = tctx; + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_LIST; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 2; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = BASEDIR "\\*"; + io.t2ffirst.in.num_names = 2; + io.t2ffirst.in.ea_names = talloc_array(tctx, struct ea_name, 2); + io.t2ffirst.in.ea_names[0].name.s = "SECOND EA"; + io.t2ffirst.in.ea_names[1].name.s = "THIRD EA"; + + status = smb_raw_search_first(cli->tree, tctx, + &io, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 2); + + nxt.t2fnext.level = RAW_SEARCH_TRANS2; + nxt.t2fnext.data_level = RAW_SEARCH_DATA_EA_LIST; + nxt.t2fnext.in.handle = io.t2ffirst.out.handle; + nxt.t2fnext.in.max_count = 2; + nxt.t2fnext.in.resume_key = result.list[1].ea_list.resume_key; + nxt.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | FLAG_TRANS2_FIND_CONTINUE; + nxt.t2fnext.in.last_name = result.list[1].ea_list.name.s; + nxt.t2fnext.in.num_names = 2; + nxt.t2fnext.in.ea_names = talloc_array(tctx, struct ea_name, 2); + nxt.t2fnext.in.ea_names[0].name.s = "SECOND EA"; + nxt.t2fnext.in.ea_names[1].name.s = "THIRD EA"; + + status = smb_raw_search_next(cli->tree, tctx, + &nxt, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + + /* we have to sort the result as different servers can return directories + in different orders */ + TYPESAFE_QSORT(result.list, result.count, ealist_cmp); + + CHECK_VALUE(result.count, 3); + CHECK_VALUE(result.list[0].ea_list.eas.num_eas, 2); + CHECK_STRING(result.list[0].ea_list.name.s, "file1.txt"); + CHECK_STRING(result.list[0].ea_list.eas.eas[0].name.s, "SECOND EA"); + CHECK_VALUE(result.list[0].ea_list.eas.eas[0].value.length, 0); + CHECK_STRING(result.list[0].ea_list.eas.eas[1].name.s, "THIRD EA"); + CHECK_VALUE(result.list[0].ea_list.eas.eas[1].value.length, 0); + + CHECK_STRING(result.list[1].ea_list.name.s, "file2.txt"); + CHECK_STRING(result.list[1].ea_list.eas.eas[0].name.s, "SECOND EA"); + CHECK_VALUE(result.list[1].ea_list.eas.eas[0].value.length, 9); + CHECK_STRING((const char *)result.list[1].ea_list.eas.eas[0].value.data, "Value Two"); + CHECK_STRING(result.list[1].ea_list.eas.eas[1].name.s, "THIRD EA"); + CHECK_VALUE(result.list[1].ea_list.eas.eas[1].value.length, 0); + + CHECK_STRING(result.list[2].ea_list.name.s, "file3.txt"); + CHECK_STRING(result.list[2].ea_list.eas.eas[0].name.s, "SECOND EA"); + CHECK_VALUE(result.list[2].ea_list.eas.eas[0].value.length, 9); + CHECK_STRING((const char *)result.list[2].ea_list.eas.eas[0].value.data, "Value Two"); + CHECK_STRING(result.list[2].ea_list.eas.eas[1].name.s, "THIRD EA"); + CHECK_VALUE(result.list[2].ea_list.eas.eas[1].value.length, 0); + + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + Test the behavior of max count parameter in TRANS2_FIND_FIRST2 and + TRANS2_FIND_NEXT2 queries +*/ +static bool test_max_count(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const int num_files = 2; + int i, fnum; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + union smb_search_first io; + union smb_search_next io2; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Creating %d files\n", num_files); + + for (i=num_files-1;i>=0;i--) { + fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i); + fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + torture_comment(tctx, + "Failed to create %s - %s\n", + fname, smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + talloc_free(fname); + smbcli_close(cli->tree, fnum); + } + + torture_comment(tctx, "Set max_count parameter to 0. " + "This should return 1 entry\n"); + ZERO_STRUCT(result); + result.tctx = talloc_new(tctx); + + io.t2ffirst.level = RAW_SEARCH_TRANS2; + io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 0; + io.t2ffirst.in.flags = 0; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = BASEDIR "\\*.*"; + + status = smb_raw_search_first(cli->tree, tctx, + &io, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 1); + + torture_comment(tctx, "Set max_count to 1. This should also " + "return 1 entry\n"); + io2.t2fnext.level = RAW_SEARCH_TRANS2; + io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = 1; + io2.t2fnext.in.resume_key = 0; + io2.t2fnext.in.flags = 0; + io2.t2fnext.in.last_name = + result.list[result.count-1].both_directory_info.name.s; + + status = smb_raw_search_next(cli->tree, tctx, + &io2, &result, multiple_search_callback); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, 2); +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* + basic testing of all RAW_SEARCH_* calls using a single file +*/ +struct torture_suite *torture_raw_search(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "search"); + + torture_suite_add_2smb_test(suite, "one file search", test_one_file); + torture_suite_add_1smb_test(suite, "many files", test_many_files); + torture_suite_add_1smb_test(suite, "sorted", test_sorted); + torture_suite_add_1smb_test(suite, "modify search", test_modify_search); + torture_suite_add_1smb_test(suite, "many dirs", test_many_dirs); + torture_suite_add_1smb_test(suite, "os2 delete", test_os2_delete); + torture_suite_add_1smb_test(suite, "ea list", test_ea_list); + torture_suite_add_1smb_test(suite, "max count", test_max_count); + + return suite; +} diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c new file mode 100644 index 0000000..6bac828 --- /dev/null +++ b/source4/torture/raw/seek.c @@ -0,0 +1,242 @@ +/* + Unix SMB/CIFS implementation. + seek test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = false; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testseek" + +/* + test seek ops +*/ +static bool test_seek(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_seek io; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + NTSTATUS status; + bool ret = true; + int fnum, fnum2; + const char *fname = BASEDIR "\\test.txt"; + uint8_t c[2]; + + if (!torture_setup_dir(cli, BASEDIR)) { + return false; + } + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE); + if (fnum == -1) { + printf("Failed to open test.txt - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + + printf("Trying bad handle\n"); + io.lseek.in.file.fnum = fnum+1; + io.lseek.in.mode = SEEK_MODE_START; + io.lseek.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Trying simple seek\n"); + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_START; + io.lseek.in.offset = 17; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 17); + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + printf("Trying relative seek\n"); + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_CURRENT; + io.lseek.in.offset = -3; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 14); + + printf("Trying end seek\n"); + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_END; + io.lseek.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, finfo.all_info.out.size); + + printf("Trying max seek\n"); + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_START; + io.lseek.in.offset = -1; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 0xffffffff); + + printf("Testing position information change\n"); + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + printf("Trying max overflow\n"); + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_CURRENT; + io.lseek.in.offset = 1000; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 999); + + printf("Testing position information change\n"); + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + printf("trying write to update offset\n"); + ZERO_STRUCT(c); + if (smbcli_write(cli->tree, fnum, 0, c, 0, 2) != 2) { + printf("Write failed - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Testing position information change\n"); + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + io.lseek.in.file.fnum = fnum; + io.lseek.in.mode = SEEK_MODE_CURRENT; + io.lseek.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 2); + + if (smbcli_read(cli->tree, fnum, c, 0, 1) != 1) { + printf("Read failed - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + + printf("Testing position information change\n"); + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 1); + + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lseek.out.offset, 1); + + printf("Testing position information\n"); + fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + if (fnum2 == -1) { + printf("2nd open failed - %s\n", smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.position_information.in.file.fnum = fnum2; + sfinfo.position_information.in.position = 25; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 25); + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 1); + + printf("position_information via paths\n"); + + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.position_information.in.file.path = fname; + sfinfo.position_information.in.position = 32; + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.fnum = fnum2; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 25); + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + basic testing of seek calls +*/ +bool torture_raw_seek(struct torture_context *torture, struct smbcli_state *cli) +{ + bool ret = true; + + ret &= test_seek(cli, torture); + + return ret; +} diff --git a/source4/torture/raw/session.c b/source4/torture/raw/session.c new file mode 100644 index 0000000..76ae808 --- /dev/null +++ b/source4/torture/raw/session.c @@ -0,0 +1,446 @@ +/* + Unix SMB/CIFS implementation. + test suite for session setup operations + Copyright (C) Gregor Beck 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture.h" +#include "libcli/libcli.h" +#include "torture/raw/proto.h" +#include "smb_composite/smb_composite.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" +#include "torture/util.h" +#include "auth/credentials/credentials.h" +#include "libcli/resolve/resolve.h" + + +static bool test_session_reauth1(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + struct smb_composite_sesssetup io; + int fnum, num; + const int dlen = 255; + char *data; + char fname[256]; + char buf[dlen+1]; + bool ok = true; + uint16_t vuid1 = cli->session->vuid; + + data = generate_random_str(tctx, dlen); + torture_assert(tctx, (data != NULL), "memory allocation failed"); + snprintf(fname, sizeof(fname), "raw_session_reconnect_%.8s.dat", data); + + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, + 0); + torture_assert_ntstatus_ok_goto(tctx, smbcli_nt_error(cli->tree), ok, + done, "create file"); + torture_assert_goto(tctx, fnum > 0, ok, done, "create file"); + + num = smbcli_smbwrite(cli->tree, fnum, data, 0, dlen); + torture_assert_int_equal_goto(tctx, num, dlen, ok, done, "write file"); + + ZERO_STRUCT(io); + io.in.sesskey = cli->transport->negotiate.sesskey; + io.in.capabilities = cli->transport->negotiate.capabilities; + io.in.credentials = samba_cmdline_get_creds(); + io.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(cli->session, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "setup2"); + torture_assert_int_equal_goto(tctx, io.out.vuid, vuid1, ok, done, "setup2"); + + buf[dlen] = '\0'; + + num = smbcli_read(cli->tree, fnum, &buf, 0, dlen); + torture_assert_int_equal_goto(tctx, num, dlen, ok, done, "read file"); + torture_assert_str_equal_goto(tctx, buf, data, ok, done, "read file"); + +done: + talloc_free(data); + + if (fnum > 0) { + status = smbcli_close(cli->tree, fnum); + torture_assert_ntstatus_ok(tctx, status, "close"); + } + return ok; +} + +static bool test_session_reauth2_oplock_timeout( + struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, + uint8_t level, void *private_data) +{ + return true; +} + +static bool test_session_reauth2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + char *random_string; + char *fname; + union smb_open io_open; + struct smb_composite_sesssetup io_sesssetup; + union smb_fileinfo io_qsecdesc; + struct smbcli_request *req; + struct cli_credentials *anon_creds; + NTSTATUS status; + uint16_t fnum; + ssize_t nwritten; + uint16_t vuid1 = cli->session->vuid; + + random_string = generate_random_str(tctx, 8); + torture_assert(tctx, (random_string != NULL), + "memory allocation failed"); + fname = talloc_asprintf(tctx, "raw_session_reauth2_%s.dat", + random_string); + talloc_free(random_string); + torture_assert(tctx, (fname != NULL), "memory allocation failed"); + + smbcli_unlink(cli->tree, fname); + smbcli_oplock_handler(cli->transport, + test_session_reauth2_oplock_timeout, + cli->tree); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io_open); + io_open.generic.level = RAW_OPEN_NTCREATEX; + io_open.ntcreatex.in.root_fid.fnum = 0; + io_open.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE; + io_open.ntcreatex.in.alloc_size = 0; + io_open.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io_open.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io_open.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io_open.ntcreatex.in.create_options = 0; + io_open.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io_open.ntcreatex.in.security_flags = 0; + io_open.ntcreatex.in.fname = fname; + + torture_comment(tctx, "open with batch oplock\n"); + io_open.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli->tree, tctx, &io_open); + torture_assert_ntstatus_ok(tctx, status, "smb_raw_open failed"); + + fnum = io_open.ntcreatex.out.file.fnum; + torture_assert( + tctx, + (io_open.ntcreatex.out.oplock_level == BATCH_OPLOCK_RETURN), + "did not get batch oplock"); + + io_open.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + req = smb_raw_open_send(cli->tree, &io_open); + torture_assert(tctx, (req != NULL), "memory allocation failed"); + + /* + * Make sure the open went through + */ + status = smbcli_chkpath(cli->tree, "\\"); + torture_assert_ntstatus_ok(tctx, status, "smb_chkpath failed"); + + status = smbcli_nt_delete_on_close(cli->tree, fnum, true); + torture_assert_ntstatus_ok(tctx, status, "could not set delete on " + "close"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, (anon_creds != NULL), "memory allocation failed"); + + ZERO_STRUCT(io_sesssetup); + io_sesssetup.in.sesskey = cli->transport->negotiate.sesskey; + io_sesssetup.in.capabilities = cli->transport->negotiate.capabilities; + io_sesssetup.in.credentials = anon_creds; + io_sesssetup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io_sesssetup.in.gensec_settings = lpcfg_gensec_settings( + tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(cli->session, &io_sesssetup); + torture_assert_ntstatus_ok(tctx, status, "setup2 failed"); + torture_assert_int_equal(tctx, io_sesssetup.out.vuid, vuid1, "setup2"); + + status = smbcli_close(cli->tree, fnum); + torture_assert_ntstatus_ok(tctx, status, "close failed"); + + status = smb_raw_open_recv(req, tctx, &io_open); + torture_assert_ntstatus_ok(tctx, status, "2nd open failed"); + + fnum = io_open.ntcreatex.out.file.fnum; + + nwritten = smbcli_write(cli->tree, fnum, 0, fname, 0, strlen(fname)); + torture_assert(tctx, (nwritten == strlen(fname)), + "smbcli_write failed"); + + ZERO_STRUCT(io_qsecdesc); + io_qsecdesc.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + io_qsecdesc.query_secdesc.in.file.fnum = fnum; + io_qsecdesc.query_secdesc.in.secinfo_flags = SECINFO_OWNER; + status = smb_raw_fileinfo(cli->tree, tctx, &io_qsecdesc); + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_ACCESS_DENIED, + "anon qsecdesc did not return ACCESS_DENIED"); + + ZERO_STRUCT(io_sesssetup); + io_sesssetup.in.sesskey = cli->transport->negotiate.sesskey; + io_sesssetup.in.capabilities = cli->transport->negotiate.capabilities; + io_sesssetup.in.credentials = samba_cmdline_get_creds(); + io_sesssetup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io_sesssetup.in.gensec_settings = lpcfg_gensec_settings( + tctx, tctx->lp_ctx); + status = smb_composite_sesssetup(cli->session, &io_sesssetup); + torture_assert_ntstatus_ok(tctx, status, "setup3 failed"); + torture_assert_int_equal(tctx, io_sesssetup.out.vuid, vuid1, "setup2"); + + status = smb_raw_fileinfo(cli->tree, tctx, &io_qsecdesc); + torture_assert_ntstatus_ok(tctx, status, "2nd qsecdesc failed"); + + status = smbcli_nt_delete_on_close(cli->tree, fnum, true); + torture_assert_ntstatus_ok(tctx, status, "could not set delete on " + "close"); + + status = smbcli_close(cli->tree, fnum); + torture_assert_ntstatus_ok(tctx, status, "close failed"); + + return true; +} + +static bool test_session_expire1(struct torture_context *tctx) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + struct smbcli_session_options session_options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct smbcli_state *cli = NULL; + enum credentials_use_kerberos use_kerberos; + char fname[256]; + union smb_fileinfo qfinfo; + uint16_t vuid; + uint16_t fnum = 0; + struct smb_composite_sesssetup io_sesssetup; + size_t i; + + use_kerberos = cli_credentials_get_kerberos_state( + samba_cmdline_get_creds()); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + torture_assert_int_equal(tctx, + use_kerberos, + CRED_USE_KERBEROS_REQUIRED, + "please use --use-kerberos=required"); + + lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=4"); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + status = smbcli_full_connection(tctx, &cli, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, NULL, + lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smbcli_full_connection failed"); + + vuid = cli->session->vuid; + + /* Add some random component to the file name. */ + snprintf(fname, 256, "session_expire1_%s.dat", + generate_random_str(tctx, 8)); + + smbcli_unlink(cli->tree, fname); + + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, + 0); + torture_assert_ntstatus_ok_goto(tctx, smbcli_nt_error(cli->tree), ret, + done, "create file"); + torture_assert_goto(tctx, fnum > 0, ret, done, "create file"); + + /* get the access information */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.fnum = fnum; + + for (i=0; i < 2; i++) { + torture_comment(tctx, "query info => OK\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "raw_fileinfo failed"); + + torture_comment(tctx, "sleep 10 seconds\n"); + smb_msleep(10*1000); + } + + /* + * the krb5 library may not handle expired creds + * well, lets start with an empty ccache. + */ + cli_credentials_invalidate_ccache(samba_cmdline_get_creds(), + CRED_SPECIFIED); + + /* + * now with CAP_DYNAMIC_REAUTH + * + * This should trigger NT_STATUS_NETWORK_SESSION_EXPIRED + */ + ZERO_STRUCT(io_sesssetup); + io_sesssetup.in.sesskey = cli->transport->negotiate.sesskey; + io_sesssetup.in.capabilities = cli->transport->negotiate.capabilities; + io_sesssetup.in.capabilities |= CAP_DYNAMIC_REAUTH; + io_sesssetup.in.credentials = samba_cmdline_get_creds(); + io_sesssetup.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); + io_sesssetup.in.gensec_settings = lpcfg_gensec_settings(tctx, + tctx->lp_ctx); + + torture_comment(tctx, "reauth with CAP_DYNAMIC_REAUTH => OK\n"); + ZERO_STRUCT(io_sesssetup.out); + status = smb_composite_sesssetup(cli->session, &io_sesssetup); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "reauth failed"); + torture_assert_int_equal_goto(tctx, io_sesssetup.out.vuid, vuid, + ret, done, "reauth"); + + for (i=0; i < 2; i++) { + torture_comment(tctx, "query info => OK\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "raw_fileinfo failed"); + + torture_comment(tctx, "sleep 10 seconds\n"); + smb_msleep(10*1000); + + torture_comment(tctx, "query info => EXPIRED\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "raw_fileinfo expired"); + + /* + * the krb5 library may not handle expired creds + * well, lets start with an empty ccache. + */ + cli_credentials_invalidate_ccache( + samba_cmdline_get_creds(), CRED_SPECIFIED); + + torture_comment(tctx, "reauth with CAP_DYNAMIC_REAUTH => OK\n"); + ZERO_STRUCT(io_sesssetup.out); + status = smb_composite_sesssetup(cli->session, &io_sesssetup); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "reauth failed"); + torture_assert_int_equal_goto(tctx, io_sesssetup.out.vuid, vuid, + ret, done, "reauth"); + } + + torture_comment(tctx, "query info => OK\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "raw_fileinfo failed"); + + /* + * the krb5 library may not handle expired creds + * well, lets start with an empty ccache. + */ + cli_credentials_invalidate_ccache(samba_cmdline_get_creds(), + CRED_SPECIFIED); + + /* + * now without CAP_DYNAMIC_REAUTH + * + * This should not trigger NT_STATUS_NETWORK_SESSION_EXPIRED + */ + torture_comment(tctx, "reauth without CAP_DYNAMIC_REAUTH => OK\n"); + io_sesssetup.in.capabilities &= ~CAP_DYNAMIC_REAUTH; + + ZERO_STRUCT(io_sesssetup.out); + status = smb_composite_sesssetup(cli->session, &io_sesssetup); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "reauth failed"); + torture_assert_int_equal_goto(tctx, io_sesssetup.out.vuid, vuid, + ret, done, "reauth"); + + for (i=0; i < 2; i++) { + torture_comment(tctx, "query info => OK\n"); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "raw_fileinfo failed"); + + torture_comment(tctx, "sleep 5 seconds\n"); + smb_msleep(5*1000); + } + + torture_comment(tctx, "query info => OK\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb_raw_fileinfo(cli->tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "raw_fileinfo failed"); + + ret = true; +done: + if (fnum > 0) { + smbcli_close(cli->tree, fnum); + } + + talloc_free(cli); + lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=0"); + return ret; +} + +struct torture_suite *torture_raw_session(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "session"); + suite->description = talloc_strdup(suite, "RAW-SESSION tests"); + + torture_suite_add_1smb_test(suite, "reauth1", test_session_reauth1); + torture_suite_add_1smb_test(suite, "reauth2", test_session_reauth2); + torture_suite_add_simple_test(suite, "expire1", test_session_expire1); + + return suite; +} diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c new file mode 100644 index 0000000..45ff819 --- /dev/null +++ b/source4/torture/raw/setfileinfo.c @@ -0,0 +1,1152 @@ +/* + Unix SMB/CIFS implementation. + RAW_SFILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\testsfileinfo" + +/* basic testing of all RAW_SFILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +static bool +torture_raw_sfileinfo_base(struct torture_context *torture, struct smbcli_state *cli) +{ + bool ret = true; + int fnum = -1; + char *fnum_fname; + char *path_fname; + char *path_fname_new; + union smb_fileinfo finfo1, finfo2; + union smb_setfileinfo sfinfo; + NTSTATUS status, status2; + const char *call_name; + time_t basetime = (time(NULL) - 86400) & ~1; + bool check_fnum; + int n = time(NULL) % 100; + + path_fname = talloc_asprintf(torture, BASEDIR "\\fname_test_%d.txt", n); + path_fname_new = talloc_asprintf(torture, BASEDIR "\\fname_test_new_%d.txt", n); + fnum_fname = talloc_asprintf(torture, BASEDIR "\\fnum_test_%d.txt", n); + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + +#define RECREATE_FILE(fname) do { \ + if (fnum != -1) smbcli_close(cli->tree, fnum); \ + fnum = create_complex_file(cli, torture, fname); \ + if (fnum == -1) { \ + printf("(%s) ERROR: open of %s failed (%s)\n", \ + __location__, fname, smbcli_errstr(cli->tree)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define RECREATE_BOTH do { \ + RECREATE_FILE(path_fname); \ + smbcli_close(cli->tree, fnum); \ + RECREATE_FILE(fnum_fname); \ + } while (0) + + RECREATE_BOTH; + +#define CHECK_CALL_FNUM(call, rightstatus) do { \ + check_fnum = true; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.fnum = fnum; \ + status = smb_raw_setfileinfo(cli->tree, &sfinfo); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { \ + torture_warning(torture, \ + "(%s) %s - %s", __location__, #call, \ + nt_errstr(status)); \ + } else if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%s) %s - %s (should be %s)\n", __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.file.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, torture, &finfo1); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { \ + torture_warning(torture, \ + "(%s) %s - %s", __location__, #call, \ + nt_errstr(status)); \ + } else if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \ + ret = false; \ + }} while (0) + +#define CHECK_CALL_PATH(call, rightstatus) do { \ + check_fnum = false; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.path = path_fname; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + sfinfo.generic.in.file.path = path_fname_new; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + } \ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { \ + torture_warning(torture, \ + "(%s) %s - %s", __location__, #call, \ + nt_errstr(status)); \ + } else if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%s) %s - %s (should be %s)\n", __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.file.path = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, torture, &finfo1); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo1.generic.in.file.path = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, torture, &finfo1); \ + } \ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { \ + torture_warning(torture, \ + "(%s) %s - %s", __location__, #call, \ + nt_errstr(status)); \ + } else if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status2)); \ + ret = false; \ + }} while (0) + +#define CHECK1(call) \ + do { if (NT_STATUS_IS_OK(status)) { \ + finfo2.generic.level = RAW_FILEINFO_ ## call; \ + if (check_fnum) { \ + finfo2.generic.in.file.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, torture, &finfo2); \ + } else { \ + finfo2.generic.in.file.path = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, torture, &finfo2); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo2.generic.in.file.path = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, torture, &finfo2); \ + } \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("%s - %s\n", #call, nt_errstr(status2)); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_VALUE(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && finfo2.stype.out.field != value) { \ + printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \ + call_name, #stype, #field, \ + (unsigned int)value, (unsigned int)finfo2.stype.out.field); \ + dump_all_info(torture, &finfo1); \ + ret = false; \ + }} while (0) + +#define CHECK_TIME(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && nt_time_to_unix(finfo2.stype.out.field) != value) { \ + printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \ + call_name, #stype, #field, \ + (unsigned int)value, \ + (unsigned int)nt_time_to_unix(finfo2.stype.out.field)); \ + printf("\t%s", timestring(torture, value)); \ + printf("\t%s\n", nt_time_string(torture, finfo2.stype.out.field)); \ + dump_all_info(torture, &finfo1); \ + ret = false; \ + }} while (0) + +#define CHECK_STR(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && strcmp(finfo2.stype.out.field, value) != 0) { \ + printf("(%s) %s - %s/%s should be '%s' - '%s'\n", __location__, \ + call_name, #stype, #field, \ + value, \ + finfo2.stype.out.field); \ + dump_all_info(torture, &finfo1); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + + printf("Test setattr\n"); + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY; + sfinfo.setattr.in.write_time = basetime; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE (ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("setting to NORMAL doesn't do anything\n"); + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_NORMAL; + sfinfo.setattr.in.write_time = 0; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("a zero write_time means don't change\n"); + sfinfo.setattr.in.attrib = 0; + sfinfo.setattr.in.write_time = 0; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("Test setattre\n"); + sfinfo.setattre.in.create_time = basetime + 20; + sfinfo.setattre.in.access_time = basetime + 30; + sfinfo.setattre.in.write_time = basetime + 40; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + sfinfo.setattre.in.create_time = 0; + sfinfo.setattre.in.access_time = 0; + sfinfo.setattre.in.write_time = 0; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + printf("Test standard level\n"); + sfinfo.standard.in.create_time = basetime + 100; + sfinfo.standard.in.access_time = basetime + 200; + sfinfo.standard.in.write_time = basetime + 300; + CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + + printf("Test basic_info level\n"); + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + printf("a zero time means don't change\n"); + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = 0; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + printf("Test basic_information level\n"); + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + torture_comment(torture, "try to change a file to a directory\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_INVALID_PARAMETER); + + printf("a zero time means don't change\n"); + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + + /* interesting - w2k3 leaves change_time as current time for 0 change time + in setpathinfo + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + */ + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + printf("Test disposition_info level\n"); + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + printf("Test disposition_information level\n"); + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + /* this would delete the file! */ + /* + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + */ + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + printf("Test allocation_info level\n"); + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + RECREATE_BOTH; + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + /* setting the allocation size up via setpathinfo seems + to be broken in w2k3 */ + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + printf("Test end_of_file_info level\n"); + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + printf("Test position_information level\n"); + sfinfo.position_information.in.position = 123456; + CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456); + + CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0); + + printf("Test mode_information level\n"); + sfinfo.mode_information.in.mode = 2; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + sfinfo.mode_information.in.mode = 1; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + + sfinfo.mode_information.in.mode = 0; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + +#if 0 + printf("Test unix_basic level\n"); + CHECK_CALL_FNUM(UNIX_BASIC, NT_STATUS_OK); + CHECK_CALL_PATH(UNIX_BASIC, NT_STATUS_OK); + + printf("Test unix_link level\n"); + CHECK_CALL_FNUM(UNIX_LINK, NT_STATUS_OK); + CHECK_CALL_PATH(UNIX_LINK, NT_STATUS_OK); +#endif + +done: + smb_raw_exit(cli->session); + smbcli_close(cli->tree, fnum); + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fnum_fname))) { + printf("Failed to delete %s - %s\n", fnum_fname, smbcli_errstr(cli->tree)); + } + if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, path_fname))) { + printf("Failed to delete %s - %s\n", path_fname, smbcli_errstr(cli->tree)); + } + + return ret; +} + +/* + * basic testing of all RAW_SFILEINFO_RENAME call + */ +static bool +torture_raw_sfileinfo_rename(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + int fnum_saved, d_fnum, fnum2, fnum = -1; + char *fnum_fname; + char *fnum_fname_new; + char *path_fname; + char *path_fname_new; + char *path_dname; + char *path_dname_new; + char *saved_name; + char *saved_name_new; + union smb_fileinfo finfo1, finfo2; + union smb_setfileinfo sfinfo; + NTSTATUS status, status2; + const char *call_name; + bool check_fnum; + int n = time(NULL) % 100; + + path_fname = talloc_asprintf(torture, BASEDIR "\\fname_test_%d.txt", n); + path_fname_new = talloc_asprintf(torture, BASEDIR "\\fname_test_new_%d.txt", n); + fnum_fname = talloc_asprintf(torture, BASEDIR "\\fnum_test_%d.txt", n); + fnum_fname_new = talloc_asprintf(torture, BASEDIR "\\fnum_test_new_%d.txt", n); + path_dname = talloc_asprintf(torture, BASEDIR "\\dname_test_%d", n); + path_dname_new = talloc_asprintf(torture, BASEDIR "\\dname_test_new_%d", n); + + torture_assert(torture, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + RECREATE_BOTH; + + ZERO_STRUCT(sfinfo); + + smbcli_close(cli->tree, create_complex_file(cli, torture, fnum_fname_new)); + smbcli_close(cli->tree, create_complex_file(cli, torture, path_fname_new)); + + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = fnum_fname_new; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_NOT_SUPPORTED); + + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new); + + printf("Trying rename with dest file open\n"); + fnum2 = create_complex_file(cli, torture, fnum_fname); + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_ACCESS_DENIED); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new); + + fnum_saved = fnum; + fnum = fnum2; + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + fnum = fnum_saved; + + printf("Trying rename with dest file open and delete_on_close\n"); + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_ACCESS_DENIED); + + smbcli_close(cli->tree, fnum2); + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + printf("Trying rename with source file open twice\n"); + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + fnum2 = create_complex_file(cli, torture, fnum_fname); + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new); + smbcli_close(cli->tree, fnum2); + + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new); + + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + sfinfo.rename_information.in.new_name = path_fname+strlen(BASEDIR)+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname); + + printf("Trying rename with a root fid\n"); + status = create_directory_handle(cli->tree, BASEDIR, &d_fnum); + CHECK_STATUS(status, NT_STATUS_OK); + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.root_fid = d_fnum; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_INVALID_PARAMETER); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + smbcli_close(cli->tree, d_fnum); + + printf("Trying rename directory\n"); + if (!torture_setup_dir(cli, path_dname)) { + ret = false; + goto done; + } + saved_name = path_fname; + saved_name_new = path_fname_new; + path_fname = path_dname; + path_fname_new = path_dname_new; + sfinfo.rename_information.in.new_name = path_dname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new); + path_fname = saved_name; + path_fname_new = saved_name_new; + + if (torture_setting_bool(torture, "samba3", false)) { + printf("SKIP: Trying rename directory with a handle\n"); + printf("SKIP: Trying rename by path while a handle is open\n"); + printf("SKIP: Trying rename directory by path while a handle is open\n"); + goto done; + } + + printf("Trying rename directory with a handle\n"); + status = create_directory_handle(cli->tree, path_dname_new, &d_fnum); + fnum_saved = fnum; + fnum = d_fnum; + saved_name = fnum_fname; + saved_name_new = fnum_fname_new; + fnum_fname = path_dname; + fnum_fname_new = path_dname_new; + sfinfo.rename_information.in.new_name = path_dname+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname); + smbcli_close(cli->tree, d_fnum); + fnum = fnum_saved; + fnum_fname = saved_name; + fnum_fname_new = saved_name_new; + + printf("Trying rename by path while a handle is open\n"); + fnum_saved = fnum; + fnum = create_complex_file(cli, torture, path_fname); + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new); + /* check that the handle returns the same name */ + check_fnum = true; + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new); + /* rename it back on the handle */ + sfinfo.rename_information.in.new_name = path_fname+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname); + check_fnum = false; + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname); + smbcli_close(cli->tree, fnum); + fnum = fnum_saved; + + printf("Trying rename directory by path while a handle is open\n"); + status = create_directory_handle(cli->tree, path_dname, &d_fnum); + fnum_saved = fnum; + fnum = d_fnum; + saved_name = path_fname; + saved_name_new = path_fname_new; + path_fname = path_dname; + path_fname_new = path_dname_new; + sfinfo.rename_information.in.new_name = path_dname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new); + path_fname = saved_name; + path_fname_new = saved_name_new; + saved_name = fnum_fname; + saved_name_new = fnum_fname_new; + fnum_fname = path_dname; + fnum_fname_new = path_dname_new; + /* check that the handle returns the same name */ + check_fnum = true; + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new); + /* rename it back on the handle */ + sfinfo.rename_information.in.new_name = path_dname+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname); + fnum_fname = saved_name; + fnum_fname_new = saved_name_new; + saved_name = path_fname; + saved_name_new = path_fname_new; + path_fname = path_dname; + path_fname_new = path_dname_new; + check_fnum = false; + CHECK_STR(NAME_INFO, name_info, fname.s, path_dname); + smbcli_close(cli->tree, d_fnum); + fnum = fnum_saved; + path_fname = saved_name; + path_fname_new = saved_name_new; + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + look for the w2k3 setpathinfo STANDARD bug +*/ +static bool torture_raw_sfileinfo_bug(struct torture_context *torture, + struct smbcli_state *cli) +{ + const char *fname = "\\bug3.txt"; + union smb_setfileinfo sfinfo; + NTSTATUS status; + int fnum; + + if (!torture_setting_bool(torture, "dangerous", false)) + torture_skip(torture, + "torture_raw_sfileinfo_bug disabled - enable dangerous tests to use\n"); + + fnum = create_complex_file(cli, torture, fname); + smbcli_close(cli->tree, fnum); + + sfinfo.generic.level = RAW_SFILEINFO_STANDARD; + sfinfo.generic.in.file.path = fname; + + sfinfo.standard.in.create_time = 0; + sfinfo.standard.in.access_time = 0; + sfinfo.standard.in.write_time = 0; + + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + printf("%s - %s\n", fname, nt_errstr(status)); + + printf("now try and delete %s\n", fname); + + return true; +} + +/** + * Test both the snia cifs RAW_SFILEINFO_END_OF_FILE_INFO and the undocumented + * pass-through RAW_SFILEINFO_END_OF_FILE_INFORMATION in the context of + * trans2setpathinfo. + */ +static bool +torture_raw_sfileinfo_eof(struct torture_context *tctx, + struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_sfileinfo_end_of_file.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + union smb_fileinfo qfi; + uint16_t fnum = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + + /* Open the file sharing none. */ + status = smb_raw_open(cli1->tree, tctx, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + fnum = io.ntcreatex.out.file.fnum; + + /* Try to sfileinfo to extend the file. */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + status = smb_raw_setpathinfo(cli2->tree, &sfi); + + /* There should be share mode contention in this case. */ + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_SHARING_VIOLATION, ret, done, "Status should be " + "SHARING_VIOLATION"); + + /* Make sure the size is still 0. */ + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_STANDARD_INFO; + qfi.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli2->tree, tctx, &qfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + + torture_assert_u64_equal(tctx, qfi.standard_info.out.size, 0, + "alloc_size should be 0 since the setpathinfo failed."); + + /* Try again with the pass through instead of documented version. */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + status = smb_raw_setpathinfo(cli2->tree, &sfi); + + /* + * Looks like a windows bug: + * http://lists.samba.org/archive/cifs-protocol/2009-November/001130.html + */ + if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) { + /* It succeeds! This is just weird! */ + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "Status should be OK"); + + /* Verify that the file was actually extended to 100. */ + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_STANDARD_INFO; + qfi.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli2->tree, tctx, &qfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "Status should be OK"); + + torture_assert_u64_equal(tctx, qfi.standard_info.out.size, 100, + "alloc_size should be 100 since the setpathinfo " + "succeeded."); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_SHARING_VIOLATION, ret, done, "Status should be " + "SHARING_VIOLATION"); + } + + /* close the first file. */ + smbcli_close(cli1->tree, fnum); + fnum = 0; + + /* Try to sfileinfo to extend the file again (non-pass-through). */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 200; + status = smb_raw_setpathinfo(cli2->tree, &sfi); + + /* This should cause the client to return invalid level. */ + if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) { + /* + * Windows sends back an invalid packet that smbclient sees + * and returns INTERNAL_ERROR. + */ + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_INTERNAL_ERROR, ret, done, "Status should be " + "INTERNAL_ERROR"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_INVALID_LEVEL, ret, done, "Status should be " + "INVALID_LEVEL"); + } + + /* Try to extend the file now with the passthrough level. */ + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + status = smb_raw_setpathinfo(cli2->tree, &sfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + + /* Verify that the file was actually extended to 200. */ + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_STANDARD_INFO; + qfi.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli2->tree, tctx, &qfi); + + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + torture_assert_u64_equal(tctx, qfi.standard_info.out.size, 200, + "alloc_size should be 200 since the setpathinfo succeeded."); + + /* Open the file so end of file can be set by handle. */ + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_WRITE; + status = smb_raw_open(cli1->tree, tctx, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + fnum = io.ntcreatex.out.file.fnum; + + /* Try sfileinfo to extend the file by handle (non-pass-through). */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sfi.generic.in.file.fnum = fnum; + sfi.end_of_file_info.in.size = 300; + status = smb_raw_setfileinfo(cli1->tree, &sfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + + /* Verify that the file was actually extended to 300. */ + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_STANDARD_INFO; + qfi.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli1->tree, tctx, &qfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + torture_assert_u64_equal(tctx, qfi.standard_info.out.size, 300, + "alloc_size should be 300 since the setpathinfo succeeded."); + + /* Try sfileinfo to extend the file by handle (pass-through). */ + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.fnum = fnum; + sfi.end_of_file_info.in.size = 400; + status = smb_raw_setfileinfo(cli1->tree, &sfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + + /* Verify that the file was actually extended to 300. */ + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_STANDARD_INFO; + qfi.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli1->tree, tctx, &qfi); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, "Status should be OK"); + torture_assert_u64_equal(tctx, qfi.standard_info.out.size, 400, + "alloc_size should be 400 since the setpathinfo succeeded."); + done: + if (fnum > 0) { + smbcli_close(cli1->tree, fnum); + fnum = 0; + } + + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool +torture_raw_sfileinfo_eof_access(struct torture_context *tctx, + struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_exclusive3.dat"; + NTSTATUS status, expected_status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + uint16_t fnum=0; + uint32_t access_mask = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + /* + * base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + + + for (access_mask = 1; access_mask <= 0x00001FF; access_mask++) { + io.ntcreatex.in.access_mask = access_mask; + + status = smb_raw_open(cli1->tree, tctx, &io); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + + fnum = io.ntcreatex.out.file.fnum; + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sfi.generic.in.file.fnum = fnum; + sfi.end_of_file_info.in.size = 100; + + status = smb_raw_setfileinfo(cli1->tree, &sfi); + + expected_status = (access_mask & SEC_FILE_WRITE_DATA) ? + NT_STATUS_OK : NT_STATUS_ACCESS_DENIED; + + if (!NT_STATUS_EQUAL(expected_status, status)) { + torture_comment(tctx, "0x%x wrong\n", access_mask); + } + + torture_assert_ntstatus_equal_goto(tctx, status, + expected_status, ret, done, "Status Wrong"); + + smbcli_close(cli1->tree, fnum); + } + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +static bool +torture_raw_sfileinfo_archive(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char *fname = BASEDIR "\\test_archive.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfinfo; + union smb_fileinfo finfo; + uint16_t fnum=0; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* cleanup */ + smbcli_unlink(cli->tree, fname); + + /* + * create a normal file, verify archive bit + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "open failed"); + fnum = io.ntcreatex.out.file.fnum; + + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_ARCHIVE, + "archive bit not set"); + + /* + * try to turn off archive bit + */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO; + sfinfo.generic.in.file.fnum = fnum; + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "setfileinfo failed"); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "fileinfo failed"); + + torture_assert_int_equal(tctx, + finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_NORMAL, + "archive bit set"); + + status = smbcli_close(cli->tree, fnum); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "close failed"); + + status = smbcli_unlink(cli->tree, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "unlink failed"); + + /* + * create a directory, verify no archive bit + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_DIR_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.flags = 0; + status = smb_raw_open(cli->tree, tctx, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "directory open failed"); + fnum = io.ntcreatex.out.file.fnum; + + torture_assert_int_equal(tctx, + io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_DIRECTORY, + "archive bit set"); + + /* + * verify you can turn on archive bit + */ + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO; + sfinfo.generic.in.file.fnum = fnum; + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "setfileinfo failed"); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "fileinfo failed"); + + torture_assert_int_equal(tctx, + finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE, + "archive bit not set"); + + /* + * and try to turn it back off + */ + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO; + sfinfo.generic.in.file.fnum = fnum; + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "setfileinfo failed"); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "fileinfo failed"); + + torture_assert_int_equal(tctx, + finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_DIRECTORY, + "archive bit set"); + + status = smbcli_close(cli->tree, fnum); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, + ret, done, "close failed"); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +struct torture_suite *torture_raw_sfileinfo(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "sfileinfo"); + + torture_suite_add_1smb_test(suite, "base", torture_raw_sfileinfo_base); + torture_suite_add_1smb_test(suite, "rename", torture_raw_sfileinfo_rename); + torture_suite_add_1smb_test(suite, "bug", torture_raw_sfileinfo_bug); + torture_suite_add_2smb_test(suite, "end-of-file", + torture_raw_sfileinfo_eof); + torture_suite_add_2smb_test(suite, "end-of-file-access", + torture_raw_sfileinfo_eof_access); + torture_suite_add_1smb_test(suite, "archive", + torture_raw_sfileinfo_archive); + + return suite; +} diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c new file mode 100644 index 0000000..3b16db3 --- /dev/null +++ b/source4/torture/raw/streams.c @@ -0,0 +1,2091 @@ +/* + Unix SMB/CIFS implementation. + + test alternate data streams + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/locale.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/security/dom_sid.h" +#include "libcli/security/security_descriptor.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "lib/util/tsort.h" +#include "torture/raw/proto.h" + +#define BASEDIR "\\teststreams" + +#define CHECK_STATUS(status, correct) \ + torture_assert_ntstatus_equal_goto(tctx,status,correct,ret,done,"CHECK_STATUS") + +#define CHECK_VALUE(v, correct) \ + torture_assert_int_equal(tctx,v,correct,"CHECK_VALUE") + +#define CHECK_NTTIME(v, correct) \ + torture_assert_u64_equal(tctx,v,correct,"CHECK_NTTIME") + +#define CHECK_STR(v, correct) do { \ + bool ok; \ + if ((v) && !(correct)) { \ + ok = false; \ + } else if (!(v) && (correct)) { \ + ok = false; \ + } else if (!(v) && !(correct)) { \ + ok = true; \ + } else if (strcmp((v), (correct)) == 0) { \ + ok = true; \ + } else { \ + ok = false; \ + } \ + torture_assert(tctx,ok,\ + talloc_asprintf(tctx, "got '%s', expected '%s'",\ + (v)?(v):"NULL", (correct)?(correct):"NULL")); \ +} while (0) + +/* + check that a stream has the right contents +*/ +static bool check_stream(struct smbcli_state *cli, const char *location, + TALLOC_CTX *mem_ctx, + const char *fname, const char *sname, + const char *value) +{ + int fnum; + const char *full_name; + uint8_t *buf; + ssize_t ret; + + full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname); + + fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE); + + if (value == NULL) { + if (fnum != -1) { + printf("(%s) should have failed stream open of %s\n", + location, full_name); + return false; + } + return true; + } + + if (fnum == -1) { + printf("(%s) Failed to open stream '%s' - %s\n", + location, full_name, smbcli_errstr(cli->tree)); + return false; + } + + buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11); + + ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11); + if (ret != strlen(value)) { + printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n", + location, (long)strlen(value), full_name, (int)ret); + return false; + } + + if (memcmp(buf, value, strlen(value)) != 0) { + printf("(%s) Bad data in stream\n", location); + return false; + } + + smbcli_close(cli->tree, fnum); + return true; +} + +static int qsort_string(char * const *s1, char * const *s2) +{ + return strcmp(*s1, *s2); +} + +static int qsort_stream(const struct stream_struct *s1, const struct stream_struct *s2) +{ + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + +static bool check_stream_list(struct torture_context *tctx, + struct smbcli_state *cli, const char *fname, + int num_exp, const char **exp) +{ + union smb_fileinfo finfo; + NTSTATUS status; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(cli); + char **exp_sort; + struct stream_struct *stream_sort; + bool ret = false; + int fail = -1; + + finfo.generic.level = RAW_FILEINFO_STREAM_INFO; + finfo.generic.in.file.path = fname; + + status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VALUE(finfo.stream_info.out.num_streams, num_exp); + + if (num_exp == 0) { + ret = true; + goto done; + } + + exp_sort = (char **)talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + + if (exp_sort == NULL) { + goto done; + } + + TYPESAFE_QSORT(exp_sort, num_exp, qsort_string); + + stream_sort = (struct stream_struct *)talloc_memdup(tmp_ctx, + finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + + if (stream_sort == NULL) { + goto done; + } + + TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream); + + for (i=0; itree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + printf("(%s) opening basedir stream\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = basedir_data; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + printf("(%s) opening basedir ::$DATA stream\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0x10; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = basedir_data; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + printf("(%s) list the streams on the basedir\n", __location__); + ret &= check_stream_list(tctx, cli, BASEDIR, 0, NULL); +done: + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test basic behavior of streams on directories +*/ +static bool test_stream_io(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *sname1, *sname2; + bool ret = true; + int fnum = -1; + ssize_t retsize; + + const char *one[] = { "::$DATA" }; + const char *two[] = { "::$DATA", ":Second Stream:$DATA" }; + const char *three[] = { "::$DATA", ":Stream One:$DATA", + ":Second Stream:$DATA" }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream"); + + printf("(%s) creating a stream on a non-existent file\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + ret &= check_stream(cli, __location__, tctx, fname, "Stream One", NULL); + + printf("(%s) check that open of base file is allowed\n", __location__); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + printf("(%s) writing to stream\n", __location__); + retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9); + CHECK_VALUE(retsize, 9); + + smbcli_close(cli->tree, fnum); + + ret &= check_stream(cli, __location__, tctx, fname, "Stream One", "test data"); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = sname1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + printf("(%s) modifying stream\n", __location__); + retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10); + CHECK_VALUE(retsize, 10); + + smbcli_close(cli->tree, fnum); + + ret &= check_stream(cli, __location__, tctx, fname, "Stream One:$FOO", NULL); + + printf("(%s) creating a stream2 on a existing file\n", __location__); + io.ntcreatex.in.fname = sname2; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + printf("(%s) modifying stream\n", __location__); + retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13); + CHECK_VALUE(retsize, 13); + + smbcli_close(cli->tree, fnum); + + ret &= check_stream(cli, __location__, tctx, fname, "Stream One", "test MORE DATA "); + ret &= check_stream(cli, __location__, tctx, fname, "Stream One:$DATA", "test MORE DATA "); + ret &= check_stream(cli, __location__, tctx, fname, "Stream One:", NULL); + ret &= check_stream(cli, __location__, tctx, fname, "Second Stream", "SECOND STREAM"); + ret &= check_stream(cli, __location__, tctx, fname, + "SECOND STREAM:$DATA", "SECOND STREAM"); + ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:$DATA", "SECOND STREAM"); + ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:", NULL); + ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:$FOO", NULL); + + check_stream_list(tctx, cli, fname, 3, three); + + printf("(%s) deleting stream\n", __location__); + status = smbcli_unlink(cli->tree, sname1); + CHECK_STATUS(status, NT_STATUS_OK); + + check_stream_list(tctx, cli, fname, 2, two); + + printf("(%s) delete a stream via delete-on-close\n", __location__); + io.ntcreatex.in.fname = sname2; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + smbcli_close(cli->tree, fnum); + status = smbcli_unlink(cli->tree, sname2); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + check_stream_list(tctx, cli, fname, 1, one); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.fname = sname1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + printf("(%s) deleting file\n", __location__); + status = smbcli_unlink(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smbcli_close(cli->tree, fnum); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test stream sharemodes +*/ +static bool test_stream_sharemodes(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *sname1, *sname2; + bool ret = true; + int fnum1 = -1; + int fnum2 = -1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream"); + + printf("(%s) testing stream share mode conflicts\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + /* + * A different stream does not give a sharing violation + */ + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + /* + * ... whereas the same stream does with unchanged access/share_access + * flags + */ + + io.ntcreatex.in.fname = sname1; + io.ntcreatex.in.open_disposition = 0; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + +done: + if (fnum1 != -1) smbcli_close(cli->tree, fnum1); + if (fnum2 != -1) smbcli_close(cli->tree, fnum2); + status = smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + * Test FILE_SHARE_DELETE on streams + * + * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened + * with SEC_STD_DELETE. + * + * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to + * be opened with SEC_STD_DELETE. + * + * A stream held open with FILE_SHARE_DELETE allows the file to be + * deleted. After the main file is deleted, access to the open file descriptor + * still works, but all name-based access to both the main file as well as the + * stream is denied with DELETE pending. + * + * This means, an open of the main file with SEC_STD_DELETE should walk all + * streams and also open them with SEC_STD_DELETE. If any of these opens gives + * SHARING_VIOLATION, the main open fails. + * + * Closing the main file after delete_on_close has been set does not really + * unlink it but leaves the corresponding share mode entry with + * delete_on_close being set around until all streams are closed. + * + * Opening a stream must also look at the main file's share mode entry, look + * at the delete_on_close bit and potentially return DELETE_PENDING. + */ + +static bool test_stream_delete(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *sname1; + bool ret = true; + int fnum = -1; + uint8_t buf[9]; + ssize_t retsize; + union smb_fileinfo finfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One"); + + printf("(%s) opening non-existent file stream\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9); + CHECK_VALUE(retsize, 9); + + /* + * One stream opened without FILE_SHARE_DELETE prevents the main file + * to be deleted or even opened with DELETE access + */ + + status = smbcli_unlink(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + smbcli_close(cli->tree, fnum); + + /* + * ... but unlink works if a stream is opened with FILE_SHARE_DELETE + */ + + io.ntcreatex.in.fname = sname1; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + status = smbcli_unlink(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * file access still works on the stream while the main file is closed + */ + + retsize = smbcli_read(cli->tree, fnum, buf, 0, 9); + CHECK_VALUE(retsize, 9); + + finfo.generic.level = RAW_FILEINFO_STANDARD; + finfo.generic.in.file.path = fname; + + /* + * name-based access to both the main file and the stream does not + * work anymore but gives DELETE_PENDING + */ + + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_DELETE_PENDING); + + /* + * older S3 doesn't do this + */ + finfo.generic.in.file.path = sname1; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_DELETE_PENDING); + + /* + * fd-based qfileinfo on the stream still works, the stream does not + * have the delete-on-close bit set. This could mean that open on the + * stream first opens the main file + */ + + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* w2k and w2k3 return 0 and w2k8 returns 1 */ + if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K3(tctx) || + TARGET_IS_SAMBA3(tctx)) { + CHECK_VALUE(finfo.all_info.out.delete_pending, 0); + } else { + CHECK_VALUE(finfo.all_info.out.delete_pending, 1); + } + + smbcli_close(cli->tree, fnum); + + /* + * After closing the stream the file is really gone. + */ + + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA + |SEC_STD_DELETE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); +done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test stream names +*/ +static bool test_stream_names(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + union smb_fileinfo info; + union smb_fileinfo finfo; + union smb_fileinfo stinfo; + union smb_setfileinfo sinfo; + const char *fname = BASEDIR "\\stream_names.txt"; + const char *sname1, *sname1b, *sname1c, *sname1d; + const char *sname2, *snamew, *snamew2; + const char *snamer1; + bool ret = true; + int fnum1 = -1; + int fnum2 = -1; + int fnum3 = -1; + int i; + const char *four[4] = { + "::$DATA", + ":\x05Stream\n One:$DATA", + ":MStream Two:$DATA", + ":?Stream*:$DATA" + }; + const char *five1[5] = { + "::$DATA", + ":\x05Stream\n One:$DATA", + ":BeforeRename:$DATA", + ":MStream Two:$DATA", + ":?Stream*:$DATA" + }; + const char *five2[5] = { + "::$DATA", + ":\x05Stream\n One:$DATA", + ":AfterRename:$DATA", + ":MStream Two:$DATA", + ":?Stream*:$DATA" + }; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "\x05Stream\n One"); + sname1b = talloc_asprintf(tctx, "%s:", sname1); + sname1c = talloc_asprintf(tctx, "%s:$FOO", sname1); + sname1d = talloc_asprintf(tctx, "%s:?D*a", sname1); + sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "MStream Two"); + snamew = talloc_asprintf(tctx, "%s:%s:$DATA", fname, "?Stream*"); + snamew2 = talloc_asprintf(tctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*"); + snamer1 = talloc_asprintf(tctx, "%s:%s:$DATA", fname, "BeforeRename"); + + printf("(%s) testing stream names\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "Adding two EAs to base file\n"); + ZERO_STRUCT(sinfo); + sinfo.generic.level = RAW_SFILEINFO_EA_SET; + sinfo.generic.in.file.fnum = fnum1; + sinfo.ea_set.in.num_eas = 2; + sinfo.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2); + sinfo.ea_set.in.eas[0].flags = 0; + sinfo.ea_set.in.eas[0].name.s = "EAONE"; + sinfo.ea_set.in.eas[0].value = data_blob_string_const("VALUE1"); + sinfo.ea_set.in.eas[1].flags = 0; + sinfo.ea_set.in.eas[1].name.s = "SECONDEA"; + sinfo.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo"); + + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Make sure the create time of the streams are different from the + * base file. + */ + sleep(2); + smbcli_close(cli->tree, fnum1); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + torture_comment(tctx, "Adding one EAs to first stream file\n"); + ZERO_STRUCT(sinfo); + sinfo.generic.level = RAW_SFILEINFO_EA_SET; + sinfo.generic.in.file.fnum = fnum1; + sinfo.ea_set.in.num_eas = 1; + sinfo.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 1); + sinfo.ea_set.in.eas[0].flags = 0; + sinfo.ea_set.in.eas[0].name.s = "STREAMEA"; + sinfo.ea_set.in.eas[0].value = data_blob_string_const("EA_VALUE1"); + + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + status = torture_check_ea(cli, sname1, "STREAMEA", "EA_VALUE1"); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + ZERO_STRUCT(info); + info.generic.level = RAW_FILEINFO_ALL_EAS; + info.all_eas.in.file.path = sname1; + + status = smb_raw_pathinfo(cli->tree, tctx, &info); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * A different stream does not give a sharing violation + */ + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + /* + * ... whereas the same stream does with unchanged access/share_access + * flags + */ + + io.ntcreatex.in.fname = sname1; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.fname = sname1b; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.ntcreatex.in.fname = sname1c; + status = smb_raw_open(cli->tree, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + /* w2k returns INVALID_PARAMETER */ + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } else { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + } + + io.ntcreatex.in.fname = sname1d; + status = smb_raw_open(cli->tree, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + /* w2k returns INVALID_PARAMETER */ + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } else { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + } + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.fname = snamew; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum3 = io.ntcreatex.out.file.fnum; + + io.ntcreatex.in.fname = snamew2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + ret &= check_stream_list(tctx, cli, fname, 4, four); + + smbcli_close(cli->tree, fnum1); + smbcli_close(cli->tree, fnum2); + smbcli_close(cli->tree, fnum3); + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_stream_list(tctx, cli, fname, 4, four); + + for (i=0; i < 4; i++) { + NTTIME write_time; + uint64_t stream_size; + char *path = talloc_asprintf(tctx, "%s%s", + fname, four[i]); + + char *rpath = talloc_strdup(path, path); + char *p = strrchr(rpath, ':'); + /* eat :$DATA */ + *p = 0; + p--; + if (*p == ':') { + /* eat ::$DATA */ + *p = 0; + } + printf("(%s): i[%u][%s]\n", __location__, i, path); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.fname = path; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + stinfo.generic.level = RAW_FILEINFO_ALL_INFO; + stinfo.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli->tree, tctx, &stinfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (!torture_setting_bool(tctx, "samba3", false)) { + CHECK_NTTIME(stinfo.all_info.out.create_time, + finfo.all_info.out.create_time); + CHECK_NTTIME(stinfo.all_info.out.access_time, + finfo.all_info.out.access_time); + CHECK_NTTIME(stinfo.all_info.out.write_time, + finfo.all_info.out.write_time); + CHECK_NTTIME(stinfo.all_info.out.change_time, + finfo.all_info.out.change_time); + } + CHECK_VALUE(stinfo.all_info.out.attrib, + finfo.all_info.out.attrib); + CHECK_VALUE(stinfo.all_info.out.size, + finfo.all_info.out.size); + CHECK_VALUE(stinfo.all_info.out.delete_pending, + finfo.all_info.out.delete_pending); + CHECK_VALUE(stinfo.all_info.out.directory, + finfo.all_info.out.directory); + CHECK_VALUE(stinfo.all_info.out.ea_size, + finfo.all_info.out.ea_size); + + stinfo.generic.level = RAW_FILEINFO_NAME_INFO; + stinfo.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli->tree, tctx, &stinfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (!torture_setting_bool(tctx, "samba3", false)) { + CHECK_STR(stinfo.name_info.out.fname.s, rpath); + } + + write_time = finfo.all_info.out.write_time; + write_time += i*1000000; + write_time /= 1000000; + write_time *= 1000000; + + ZERO_STRUCT(sinfo); + sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; + sinfo.basic_info.in.file.fnum = fnum1; + sinfo.basic_info.in.write_time = write_time; + sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + stream_size = i*8192; + + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO; + sinfo.end_of_file_info.in.file.fnum = fnum1; + sinfo.end_of_file_info.in.size = stream_size; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + stinfo.generic.level = RAW_FILEINFO_ALL_INFO; + stinfo.generic.in.file.fnum = fnum1; + status = smb_raw_fileinfo(cli->tree, tctx, &stinfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (!torture_setting_bool(tctx, "samba3", false)) { + CHECK_NTTIME(stinfo.all_info.out.write_time, + write_time); + CHECK_VALUE(stinfo.all_info.out.attrib, + finfo.all_info.out.attrib); + } + CHECK_VALUE(stinfo.all_info.out.size, + stream_size); + CHECK_VALUE(stinfo.all_info.out.delete_pending, + finfo.all_info.out.delete_pending); + CHECK_VALUE(stinfo.all_info.out.directory, + finfo.all_info.out.directory); + CHECK_VALUE(stinfo.all_info.out.ea_size, + finfo.all_info.out.ea_size); + + ret &= check_stream_list(tctx, cli, fname, 4, four); + + smbcli_close(cli->tree, fnum1); + talloc_free(path); + } + + printf("(%s): testing stream renames\n", __location__); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.fname = snamer1; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + ret &= check_stream_list(tctx, cli, fname, 5, five1); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.fnum = fnum1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = ":AfterRename:$DATA"; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + ret &= check_stream_list(tctx, cli, fname, 5, five2); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.fnum = fnum1; + sinfo.rename_information.in.overwrite = false; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = ":MStream Two:$DATA"; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + ret &= check_stream_list(tctx, cli, fname, 5, five2); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.fnum = fnum1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = ":MStream Two:$DATA"; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + if (torture_setting_bool(tctx, "samba4", false) || + torture_setting_bool(tctx, "samba3", false)) { + /* why should this rename be considered invalid?? */ + CHECK_STATUS(status, NT_STATUS_OK); + ret &= check_stream_list(tctx, cli, fname, 4, four); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + ret &= check_stream_list(tctx, cli, fname, 5, five2); + } + + + /* TODO: we need to test more rename combinations */ + +done: + if (fnum1 != -1) smbcli_close(cli->tree, fnum1); + if (fnum2 != -1) smbcli_close(cli->tree, fnum2); + if (fnum3 != -1) smbcli_close(cli->tree, fnum3); + status = smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test stream names +*/ +static bool test_stream_names2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream_names2.txt"; + bool ret = true; + int fnum1 = -1; + uint8_t i; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("(%s) testing stream names\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + for (i=0x01; i < 0x7F; i++) { + char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA", + fname, i, i); + NTSTATUS expected; + + switch (i) { + case '/':/*0x2F*/ + case ':':/*0x3A*/ + case '\\':/*0x5C*/ + expected = NT_STATUS_OBJECT_NAME_INVALID; + break; + default: + expected = NT_STATUS_OBJECT_NAME_NOT_FOUND; + break; + } + + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = path; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, expected)) { + printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n", + __location__, fname, isprint(i)?(char)i:' ', i, + isprint(i)?"":" (not printable)", + nt_errstr(expected)); + } + CHECK_STATUS(status, expected); + + talloc_free(path); + } + +done: + if (fnum1 != -1) smbcli_close(cli->tree, fnum1); + status = smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +#define CHECK_CALL_FNUM(call, rightstatus) do { \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.fnum = fnum; \ + status = smb_raw_setfileinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%s) %s - %s (should be %s)\n", __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.file.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \ + ret = false; \ + }} while (0) + +/* + test stream renames +*/ +static bool test_stream_rename(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status, status2; + union smb_open io; + const char *fname = BASEDIR "\\stream_rename.txt"; + const char *sname1, *sname2; + union smb_fileinfo finfo1; + union smb_setfileinfo sfinfo; + bool ret = true; + int fnum = -1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream"); + + printf("(%s) testing stream renames\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + /* Create two streams. */ + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + if (fnum != -1) smbcli_close(cli->tree, fnum); + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + if (fnum != -1) smbcli_close(cli->tree, fnum); + + /* + * Open the second stream. + */ + + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* + * Now rename the second stream onto the first. + */ + + ZERO_STRUCT(sfinfo); + + sfinfo.rename_information.in.overwrite = 1; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = ":Stream One"; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + +done: + if (fnum != -1) smbcli_close(cli->tree, fnum); + status = smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +static bool test_stream_rename2(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname1 = BASEDIR "\\stream.txt"; + const char *fname2 = BASEDIR "\\stream2.txt"; + const char *stream_name1 = ":Stream One:$DATA"; + const char *stream_name2 = ":Stream Two:$DATA"; + const char *stream_name_default = "::$DATA"; + const char *sname1; + const char *sname2; + bool ret = true; + int fnum = -1; + union smb_setfileinfo sinfo; + union smb_rename rio; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname1, "Stream One"); + sname2 = talloc_asprintf(tctx, "%s:%s", fname1, "Stream Two"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_STD_DELETE|SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = (NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE); + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + /* Open/create new stream. */ + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* + * Check raw rename with :. + */ + printf("(%s) Checking NTRENAME of a stream using :\n", + __location__); + rio.generic.level = RAW_RENAME_NTRENAME; + rio.ntrename.in.old_name = sname1; + rio.ntrename.in.new_name = sname2; + rio.ntrename.in.attrib = 0; + rio.ntrename.in.cluster_size = 0; + rio.ntrename.in.flags = RENAME_FLAG_RENAME; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Check raw rename to the default stream using :. + */ + printf("(%s) Checking NTRENAME to default stream using :\n", + __location__); + rio.ntrename.in.new_name = stream_name_default; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + /* + * Check raw rename using :. + */ + printf("(%s) Checking NTRENAME of a stream using :\n", + __location__); + rio.ntrename.in.new_name = stream_name2; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check raw rename of a stream to a file. + */ + printf("(%s) Checking NTRENAME of a stream to a file\n", + __location__); + rio.ntrename.in.old_name = sname2; + rio.ntrename.in.new_name = fname2; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Check raw rename of a file to a stream. + */ + printf("(%s) Checking NTRENAME of a file to a stream\n", + __location__); + + /* Create the file. */ + io.ntcreatex.in.fname = fname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Try the rename. */ + rio.ntrename.in.old_name = fname2; + rio.ntrename.in.new_name = sname1; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + /* + * Reopen the stream for trans2 renames. + */ + io.ntcreatex.in.fname = sname2; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* + * Check trans2 rename of a stream using :. + */ + printf("(%s) Checking trans2 rename of a stream using :\n", + __location__); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.fnum = fnum; + sinfo.rename_information.in.overwrite = 1; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = stream_name1; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check trans2 rename of an overwriting stream using :. + */ + printf("(%s) Checking trans2 rename of an overwriting stream using " + ":\n", __location__); + + /* Create second stream. */ + io.ntcreatex.in.fname = sname2; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + + /* Rename the first stream onto the second. */ + sinfo.rename_information.in.file.fnum = fnum; + sinfo.rename_information.in.new_name = stream_name2; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + /* + * Reopen the stream with the new name. + */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* + * Check trans2 rename of a stream using :. + */ + printf("(%s) Checking trans2 rename of a stream using " + ":\n", __location__); + sinfo.rename_information.in.file.fnum = fnum; + sinfo.rename_information.in.new_name = sname1; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_NOT_SUPPORTED); + + /* + * Samba3 doesn't currently support renaming a stream to the default + * stream. This test does pass on windows. + */ + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + goto done; + } + + /* + * Check trans2 rename to the default stream using :. + */ + printf("(%s) Checking trans2 rename to defaualt stream using " + ":\n", __location__); + sinfo.rename_information.in.file.fnum = fnum; + sinfo.rename_information.in.new_name = stream_name_default; + status = smb_raw_setfileinfo(cli->tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + done: + smbcli_close(cli->tree, fnum); + status = smbcli_unlink(cli->tree, fname1); + status = smbcli_unlink(cli->tree, fname2); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test stream renames +*/ +static bool test_stream_rename3(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status, status2; + union smb_open io; + const char *fname = BASEDIR "\\stream_rename.txt"; + const char *sname1, *sname2; + union smb_fileinfo finfo1; + union smb_setfileinfo sfinfo; + bool ret = true; + int fnum = -1; + int fnum2 = -1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + sname1 = talloc_asprintf(tctx, "%s:%s", fname, "MStream Two:$DATA"); + sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream"); + + printf("(%s) testing stream renames\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + /* Create two streams. */ + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + if (fnum != -1) smbcli_close(cli->tree, fnum); + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + if (fnum != -1) smbcli_close(cli->tree, fnum); + + /* open the second stream. */ + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + /* Keep a handle to the first stream open. */ + io.ntcreatex.in.fname = sname1; + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.in.overwrite = 1; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = ":MStream Two:$DATA"; + if (torture_setting_bool(tctx, "samba4", false) || + torture_setting_bool(tctx, "samba3", false)) { + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + } else { + CHECK_CALL_FNUM(RENAME_INFORMATION, + NT_STATUS_INVALID_PARAMETER); + } + + +done: + if (fnum != -1) smbcli_close(cli->tree, fnum); + if (fnum2 != -1) smbcli_close(cli->tree, fnum2); + status = smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +static bool create_file_with_stream(struct torture_context *tctx, + struct smbcli_state *cli, + const char *stream) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + + ZERO_STRUCT(io); + + /* Create a file with a stream */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = stream; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + return ret; +} + +/* Test how streams interact with create dispositions */ +static bool test_stream_create_disposition(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + const char *default_stream_name = "::$DATA"; + const char *stream_list[2]; + bool ret = false; + int fnum = -1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream); + + stream_list[0] = talloc_asprintf(tctx, ":%s", stream); + stream_list[1] = default_stream_name; + + if (!create_file_with_stream(tctx, cli, fname_stream)) { + goto done; + } + + /* Open the base file with OPEN */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* + * check ntcreatex open: sanity check + */ + printf("(%s) Checking ntcreatex disp: open\n", __location__); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 2, stream_list)) { + goto done; + } + + /* + * check ntcreatex overwrite + */ + printf("(%s) Checking ntcreatex disp: overwrite\n", __location__); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) { + goto done; + } + + /* + * check ntcreatex overwrite_if + */ + printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__); + smbcli_unlink(cli->tree, fname); + if (!create_file_with_stream(tctx, cli, fname_stream)) { + goto done; + } + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) { + goto done; + } + + /* + * check ntcreatex supersede + */ + printf("(%s) Checking ntcreatex disp: supersede\n", __location__); + smbcli_unlink(cli->tree, fname); + if (!create_file_with_stream(tctx, cli, fname_stream)) { + goto done; + } + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) { + goto done; + } + + /* + * check ntcreatex overwrite_if on a stream. + */ + printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n", + __location__); + smbcli_unlink(cli->tree, fname); + if (!create_file_with_stream(tctx, cli, fname_stream)) { + goto done; + } + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.ntcreatex.in.fname = fname_stream; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 2, stream_list)) { + goto done; + } + + /* + * check openx overwrite_if + */ + printf("(%s) Checking openx disp: overwrite_if\n", __location__); + smbcli_unlink(cli->tree, fname); + if (!create_file_with_stream(tctx, cli, fname_stream)) { + goto done; + } + + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPEN_FLAGS_DENY_NONE; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 1024*1024; + io.openx.in.timeout = 0; + io.openx.in.fname = fname; + + io.openx.in.open_func = OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE; + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.openx.out.file.fnum); + if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) { + goto done; + } + + ret = true; + + done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +#if 0 +/* Test streaminfo with enough streams on a file to fill up the buffer. */ +static bool test_stream_large_streaminfo(struct torture_context *tctx, + struct smbcli_state *cli) +{ +#define LONG_STREAM_SIZE 2 + char *lstream_name; + const char *fname = BASEDIR "\\stream.txt"; + const char *fname_stream; + NTSTATUS status; + bool ret = true; + int i; + union smb_fileinfo finfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + lstream_name = talloc_array(tctx, char, LONG_STREAM_SIZE); + + for (i = 0; i < LONG_STREAM_SIZE - 1; i++) { + lstream_name[i] = (char)('a' + i%26); + } + lstream_name[LONG_STREAM_SIZE - 1] = '\0'; + + torture_comment(tctx, "(%s) Creating a file with a lot of streams\n", __location__); + for (i = 0; i < 10000; i++) { + fname_stream = talloc_asprintf(tctx, "%s:%s%d", fname, + lstream_name, i); + ret = create_file_with_stream(tctx, cli, fname_stream); + if (!ret) { + goto done; + } + } + + finfo.generic.level = RAW_FILEINFO_STREAM_INFO; + finfo.generic.in.file.path = fname; + + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, STATUS_BUFFER_OVERFLOW); + + done: + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} +#endif + +/* Test the effect of setting attributes on a stream. */ +static bool test_stream_attributes(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream_attr.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + int fnum = -1; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + time_t basetime = (time(NULL) - 86400) & ~1; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "(%s) testing attribute setting on stream\n", __location__); + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream); + + /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */ + ret = create_file_with_stream(tctx, cli, fname_stream); + if (!ret) { + goto done; + } + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_assert_int_equal_goto(tctx, finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, FILE_ATTRIBUTE_ARCHIVE, ret, done, "attrib incorrect"); + + /* Now open the stream name. */ + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL|SEC_FILE_WRITE_ATTRIBUTE); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_stream; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + fnum = io.ntcreatex.out.file.fnum; + + /* Change the attributes + time on the stream fnum. */ + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime); + + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.fnum = fnum; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, done, "smb_raw_setfileinfo failed"); + + smbcli_close(cli->tree, fnum); + fnum = -1; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, done, "smb_raw_pathinfo failed"); + + torture_assert_int_equal_goto(tctx, finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, FILE_ATTRIBUTE_READONLY, ret, done, "attrib incorrect"); + + torture_assert_int_equal_goto(tctx, nt_time_to_unix(finfo.all_info.out.write_time), basetime, ret, done, "time incorrect"); + + done: + + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/** + * A rough approximation of how a windows client creates the streams for use + * in the summary tab. + */ +static bool test_stream_summary_tab(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream_summary.txt"; + const char *stream = ":\005SummaryInformation:$DATA"; + const char *fname_stream = NULL; + const char *tmp_stream = ":Updt_\005SummaryInformation:$DATA"; + const char *fname_tmp_stream = NULL; + int fnum = -1; + union smb_fileinfo finfo; + union smb_rename rio; + ssize_t retsize; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + fname_stream = talloc_asprintf(tctx, "%s%s", fname, stream); + fname_tmp_stream = talloc_asprintf(tctx, "%s%s", fname, + tmp_stream); + + /* Create summary info stream */ + ret = create_file_with_stream(tctx, cli, fname_stream); + if (!ret) { + goto done; + } + + /* Create summary info tmp update stream */ + ret = create_file_with_stream(tctx, cli, fname_tmp_stream); + if (!ret) { + goto done; + } + + /* Open tmp stream and write to it */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_tmp_stream; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9); + CHECK_VALUE(retsize, 9); + + /* close the tmp stream. */ + smbcli_close(cli->tree, fnum); + fnum = -1; + + /* Delete the current stream */ + smbcli_unlink(cli->tree, fname_stream); + + /* Do the rename. */ + rio.generic.level = RAW_RENAME_RENAME; + rio.rename.in.pattern1 = fname_tmp_stream; + rio.rename.in.pattern2 = stream; + rio.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_rename(cli->tree, &rio); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to open the tmp stream that we just renamed away. */ + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* Query the base file to make sure it's still there. */ + finfo.generic.level = RAW_FILEINFO_BASIC_INFO; + finfo.generic.in.file.path = fname; + + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + smbcli_unlink(cli->tree, fname); + + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* Test how streams interact with base file permissions */ +/* Regression test for bug: + https://bugzilla.samba.org/show_bug.cgi?id=10229 + bug #10229 - No access check verification on stream files. +*/ +static bool test_stream_permissions(struct torture_context *tctx, + struct smbcli_state *cli) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + const char *fname = BASEDIR "\\stream_permissions.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + int fnum = -1; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_ace ace = {}; + struct security_descriptor *sd; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), + "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "(%s) testing permissions on streams\n", __location__); + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream); + + /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */ + ret = create_file_with_stream(tctx, cli, fname_stream); + if (!ret) { + goto done; + } + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_assert_int_equal_goto(tctx, + finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, + FILE_ATTRIBUTE_ARCHIVE, ret, done, "attrib incorrect"); + + /* Change the attributes on the base file name. */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_SETATTR; + sfinfo.generic.in.file.path = fname; + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY; + + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try and open the stream name for WRITE_DATA. Should + fail with ACCESS_DENIED. */ + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_stream; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + /* Change the attributes on the base file back. */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_SETATTR; + sfinfo.generic.in.file.path = fname; + sfinfo.setattr.in.attrib = 0; + + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Re-open the file name. */ + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA| + SEC_STD_READ_CONTROL|SEC_STD_WRITE_DAC| + SEC_FILE_WRITE_ATTRIBUTE); + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + fnum = io.ntcreatex.out.file.fnum; + + /* Get the existing security descriptor. */ + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.fnum = fnum; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb_raw_fileinfo(cli->tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd = q.query_secdesc.out.sd; + + /* Now add a DENY WRITE security descriptor for Everyone. */ + torture_comment(tctx, "add a new ACE to the DACL\n"); + + ace.type = SEC_ACE_TYPE_ACCESS_DENIED; + ace.flags = 0; + ace.access_mask = SEC_FILE_WRITE_DATA; + ace.trustee = global_sid_World; + + status = security_descriptor_dacl_add(sd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + /* security_descriptor_dacl_add adds to the *end* of + the ace array, we need it at the start. Swap.. */ + ace = sd->dacl->aces[0]; + sd->dacl->aces[0] = sd->dacl->aces[sd->dacl->num_aces-1]; + sd->dacl->aces[sd->dacl->num_aces-1] = ace; + + ZERO_STRUCT(set); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.fnum = fnum; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb_raw_setfileinfo(cli->tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + fnum = -1; + + /* Try and open the stream name for WRITE_DATA. Should + fail with ACCESS_DENIED. */ + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = 0; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname_stream; + + status = smb_raw_open(cli->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + done: + + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + smbcli_unlink(cli->tree, fname); + + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + basic testing of streams calls +*/ +struct torture_suite *torture_raw_streams(TALLOC_CTX *tctx) +{ + struct torture_suite *suite = torture_suite_create(tctx, "streams"); + + torture_suite_add_1smb_test(suite, "dir", test_stream_dir); + torture_suite_add_1smb_test(suite, "io", test_stream_io); + torture_suite_add_1smb_test(suite, "sharemodes", test_stream_sharemodes); + torture_suite_add_1smb_test(suite, "delete", test_stream_delete); + torture_suite_add_1smb_test(suite, "names", test_stream_names); + torture_suite_add_1smb_test(suite, "names2", test_stream_names2); + torture_suite_add_1smb_test(suite, "rename", test_stream_rename); + torture_suite_add_1smb_test(suite, "rename2", test_stream_rename2); + torture_suite_add_1smb_test(suite, "rename3", test_stream_rename3); + torture_suite_add_1smb_test(suite, "createdisp", + test_stream_create_disposition); + torture_suite_add_1smb_test(suite, "attr", test_stream_attributes); + torture_suite_add_1smb_test(suite, "sumtab", test_stream_summary_tab); + torture_suite_add_1smb_test(suite, "perms", test_stream_permissions); + +#if 0 + torture_suite_add_1smb_test(suite, "LARGESTREAMINFO", + test_stream_large_streaminfo); +#endif + + return suite; +} diff --git a/source4/torture/raw/tconrate.c b/source4/torture/raw/tconrate.c new file mode 100644 index 0000000..e514e7a --- /dev/null +++ b/source4/torture/raw/tconrate.c @@ -0,0 +1,208 @@ +/* + SMB tree connection rate test + + Copyright (C) 2006-2007 James Peach + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/resolve/resolve.h" +#include "torture/smbtorture.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" + +#include "system/filesys.h" +#include "system/shmem.h" +#include "torture/raw/proto.h" + +#define TIME_LIMIT_SECS 30 +#define usec_to_sec(s) ((s) / 1000000) + +/* Map a shared memory buffer of at least nelem counters. */ +static void * map_count_buffer(unsigned nelem, size_t elemsz) +{ + void * buf; + size_t bufsz; + size_t pagesz = getpagesize(); + + bufsz = nelem * elemsz; + bufsz = (bufsz + pagesz) % pagesz; /* round up to pagesz */ + +#ifdef MAP_ANON + /* BSD */ + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, + -1 /* fd */, 0 /* offset */); +#else + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, + open("/dev/zero", O_RDWR), 0 /* offset */); +#endif + + if (buf == MAP_FAILED) { + printf("failed to map count buffer: %s\n", + strerror(errno)); + return NULL; + } + + return buf; + +} + +static int fork_tcon_client(struct torture_context *tctx, + int *tcon_count, unsigned tcon_timelimit, + const char *host, const char *share) +{ + pid_t child; + struct smbcli_state *cli; + struct timeval end; + struct timeval now; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + child = fork(); + if (child == -1) { + printf("failed to fork child: %s\n,", strerror(errno)); + return -1; + } else if (child != 0) { + /* Parent, just return. */ + return 0; + } + + /* Child. Just make as many connections as possible within the + * time limit. Don't bother synchronising the child start times + * because it's probably not work the effort, and a bit of startup + * jitter is probably a more realistic test. + */ + + + end = timeval_current(); + now = timeval_current(); + end.tv_sec += tcon_timelimit; + *tcon_count = 0; + + while (timeval_compare(&now, &end) == -1) { + NTSTATUS status; + + status = smbcli_full_connection(NULL, &cli, + host, lpcfg_smb_ports(tctx->lp_ctx), share, + NULL, lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + + if (!NT_STATUS_IS_OK(status)) { + printf("failed to connect to //%s/%s: %s\n", + host, share, nt_errstr(status)); + goto done; + } + + smbcli_tdis(cli); + talloc_free(cli); + + *tcon_count = *tcon_count + 1; + now = timeval_current(); + } + +done: + exit(0); +} + +static bool children_remain(void) +{ + bool res; + + /* Reap as many children as possible. */ + for (;;) { + pid_t ret = waitpid(-1, NULL, WNOHANG); + if (ret == 0) { + /* no children ready */ + res = true; + break; + } + if (ret == -1) { + /* no children left. maybe */ + res = errno != ECHILD; + break; + } + } + return res; +} + +static double rate_convert_secs(unsigned count, + const struct timeval *start, const struct timeval *end) +{ + return (double)count / + usec_to_sec((double)usec_time_diff(end, start)); +} + +/* Test the rate at which the server will accept connections. */ +bool torture_bench_treeconnect(struct torture_context *tctx) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + + int timelimit = torture_setting_int(tctx, "timelimit", + TIME_LIMIT_SECS); + int nprocs = torture_setting_int(tctx, "nprocs", 4); + + int *curr_counts = map_count_buffer(nprocs, sizeof(int)); + int *last_counts = talloc_zero_array(tctx, int, nprocs); + + struct timeval now, last, start; + int i, delta; + + torture_assert(tctx, nprocs > 0, "bad proc count"); + torture_assert(tctx, timelimit > 0, "bad timelimit"); + torture_assert(tctx, curr_counts, "allocation failure"); + torture_assert(tctx, last_counts, "allocation failure"); + + start = last = timeval_current(); + for (i = 0; i < nprocs; ++i) { + fork_tcon_client(tctx, &curr_counts[i], timelimit, host, share); + } + + while (children_remain()) { + + sleep(1); + now = timeval_current(); + + for (i = 0, delta = 0; i < nprocs; ++i) { + delta += curr_counts[i] - last_counts[i]; + } + + printf("%u connections/sec\n", + (unsigned)rate_convert_secs(delta, &last, &now)); + + memcpy(last_counts, curr_counts, nprocs * sizeof(int)); + last = timeval_current(); + } + + now = timeval_current(); + + for (i = 0, delta = 0; i < nprocs; ++i) { + delta += curr_counts[i]; + } + + printf("TOTAL: %u connections/sec over %u secs\n", + (unsigned)rate_convert_secs(delta, &start, &now), + timelimit); + return true; +} + +/* vim: set sts=8 sw=8 : */ diff --git a/source4/torture/raw/unlink.c b/source4/torture/raw/unlink.c new file mode 100644 index 0000000..53059aa --- /dev/null +++ b/source4/torture/raw/unlink.c @@ -0,0 +1,470 @@ +/* + Unix SMB/CIFS implementation. + unlink test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "system/filesys.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testunlink" + +/* + test unlink ops +*/ +static bool test_unlink(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_unlink io; + NTSTATUS status; + bool ret = true; + const char *fname = BASEDIR "\\test.txt"; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + printf("Trying non-existent file\n"); + io.unlink.in.pattern = fname; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE)); + + io.unlink.in.pattern = fname; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying a hidden file\n"); + smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE)); + torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN); + + io.unlink.in.pattern = fname; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.unlink.in.pattern = fname; + io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.unlink.in.pattern = fname; + io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + printf("Trying a directory\n"); + io.unlink.in.pattern = BASEDIR; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + io.unlink.in.pattern = BASEDIR; + io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + printf("Trying a bad path\n"); + io.unlink.in.pattern = ".."; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + io.unlink.in.pattern = "\\.."; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + io.unlink.in.pattern = BASEDIR "\\..\\.."; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + io.unlink.in.pattern = BASEDIR "\\.."; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test delete on close +*/ +static bool test_delete_on_close(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_open op; + union smb_unlink io; + struct smb_rmdir dio; + NTSTATUS status; + bool ret = true; + int fnum, fnum2; + const char *fname = BASEDIR "\\test.txt"; + const char *dname = BASEDIR "\\test.dir"; + const char *inside = BASEDIR "\\test.dir\\test.txt"; + union smb_setfileinfo sfinfo; + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + dio.in.path = dname; + + io.unlink.in.pattern = fname; + io.unlink.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + printf("Testing with delete_on_close 0\n"); + fnum = create_complex_file(cli, tctx, fname); + + sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO; + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 0; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing with delete_on_close 1\n"); + fnum = create_complex_file(cli, tctx, fname); + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + + printf("Testing with directory and delete_on_close 0\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO; + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 0; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing with directory delete_on_close 1\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + + if (!torture_setting_bool(tctx, "samba3", false)) { + + /* + * Known deficiency, also skipped in base-delete. + */ + + printf("Testing with non-empty directory delete_on_close\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + fnum2 = create_complex_file(cli, tctx, inside); + + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + + sfinfo.disposition_info.in.file.fnum = fnum2; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + sfinfo.disposition_info.in.file.fnum = fnum; + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + + smbcli_close(cli->tree, fnum2); + + status = smb_raw_setfileinfo(cli->tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } + + printf("Testing open dir with delete_on_close\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + fnum2 = create_complex_file(cli, tctx, inside); + smbcli_close(cli->tree, fnum2); + + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + smbcli_close(cli->tree, fnum); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + + smbcli_deltree(cli->tree, dname); + + printf("Testing double open dir with second delete_on_close\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, fnum); + + fnum2 = create_complex_file(cli, tctx, inside); + smbcli_close(cli->tree, fnum2); + + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = op.ntcreatex.out.file.fnum; + + smbcli_close(cli->tree, fnum2); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + + smbcli_deltree(cli->tree, dname); + + printf("Testing pre-existing open dir with second delete_on_close\n"); + status = create_directory_handle(cli->tree, dname, &fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + fnum = create_complex_file(cli, tctx, inside); + smbcli_close(cli->tree, fnum); + + /* we have a dir with a file in it, no handles open */ + + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = dname; + + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.file.fnum; + + /* open without delete on close */ + op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + status = smb_raw_open(cli->tree, tctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = op.ntcreatex.out.file.fnum; + + /* close 2nd file handle */ + smbcli_close(cli->tree, fnum2); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + + + smbcli_close(cli->tree, fnum); + + status = smb_raw_rmdir(cli->tree, &dio); + CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY); + +done: + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +struct unlink_defer_cli_state { + struct torture_context *tctx; + struct smbcli_state *cli1; +}; + +/* + * A handler function for oplock break requests. Ack it as a break to none + */ +static bool oplock_handler_ack_to_none(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, + uint8_t level, void *private_data) +{ + struct unlink_defer_cli_state *ud_cli_state = + (struct unlink_defer_cli_state *)private_data; + union smb_setfileinfo sfinfo; + bool ret; + struct smbcli_request *req = NULL; + + torture_comment(ud_cli_state->tctx, "delete the file before sending " + "the ack."); + + /* cli1: set delete on close */ + sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO; + sfinfo.disposition_info.in.file.fnum = fnum; + sfinfo.disposition_info.in.delete_on_close = 1; + req = smb_raw_setfileinfo_send(ud_cli_state->cli1->tree, &sfinfo); + if (!req) { + torture_comment(ud_cli_state->tctx, "smb_raw_setfileinfo_send " + "failed."); + } + + smbcli_close(ud_cli_state->cli1->tree, fnum); + + torture_comment(ud_cli_state->tctx, "Acking the oplock to NONE\n"); + + ret = smbcli_oplock_ack(ud_cli_state->cli1->tree, fnum, + OPLOCK_BREAK_TO_NONE); + + return ret; +} + +static bool test_unlink_defer(struct torture_context *tctx, + struct smbcli_state *cli1, + struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_unlink_defer.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + struct unlink_defer_cli_state ud_cli_state = {}; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + ud_cli_state.tctx = tctx; + ud_cli_state.cli1 = cli1; + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_none, + &ud_cli_state); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* cli1: open file with a batch oplock. */ + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cli2: Try to unlink it, but block on the oplock */ + torture_comment(tctx, "Try an unlink (should defer the open\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb_raw_unlink(cli2->tree, &unl); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + +/* + basic testing of unlink calls +*/ +struct torture_suite *torture_raw_unlink(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "unlink"); + + torture_suite_add_1smb_test(suite, "unlink", test_unlink); + torture_suite_add_1smb_test(suite, "delete_on_close", test_delete_on_close); + torture_suite_add_2smb_test(suite, "unlink-defer", test_unlink_defer); + + return suite; +} diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c new file mode 100644 index 0000000..e47225a --- /dev/null +++ b/source4/torture/raw/write.c @@ -0,0 +1,799 @@ +/* + Unix SMB/CIFS implementation. + test suite for various write operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/raw/proto.h" +#include "libcli/raw/raw_proto.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_fail(tctx, talloc_asprintf(tctx, "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct))); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_fail(tctx, talloc_asprintf(tctx, "(%s) Incorrect value %s=%d - should be %d\n", \ + __location__, #v, v, correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_BUFFER(buf, seed, len) do { \ + if (!check_buffer(tctx, buf, seed, len, __location__)) { \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.file.path = fname; \ + status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != finfo.all_info.out.field) { \ + torture_comment(tctx, "(%s) wrong value for field %s %.0f - %.0f\n", \ + __location__, #field, (double)v, (double)finfo.all_info.out.field); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + + +#define BASEDIR "\\testwrite" + + +/* + setup a random buffer based on a seed +*/ +static void setup_buffer(uint8_t *buf, unsigned int seed, int len) +{ + int i; + srandom(seed); + for (i=0;itree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + ret = false; + torture_fail_goto(tctx, done, + talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree))); + } + + torture_comment(tctx, "Trying zero write\n"); + io.write.in.file.fnum = fnum; + io.write.in.count = 0; + io.write.in.offset = 0; + io.write.in.remaining = 0; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying small write\n"); + io.write.in.count = 9; + io.write.in.offset = 4; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, io.write.in.count); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying large write\n"); + io.write.in.count = 4000; + io.write.in.offset = 0; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 4000); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + + torture_comment(tctx, "Trying bad fnum\n"); + io.write.in.file.fnum = fnum+1; + io.write.in.count = 4000; + io.write.in.offset = 0; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + torture_comment(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n"); + goto done; + } + + torture_comment(tctx, "Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.write.in.file.fnum = fnum; + io.write.in.count = 4000; + io.write.in.offset = 0xFFFFFFFF - 2000; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 4000); + CHECK_ALL_INFO(io.write.in.count + (uint64_t)io.write.in.offset, size); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, io.write.in.offset, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test writex ops +*/ +static bool test_writex(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_write io; + NTSTATUS status; + bool ret = true; + int fnum, i; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned int seed = time(NULL); + union smb_fileinfo finfo; + int max_bits=63; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + max_bits=33; + torture_comment(tctx, "dangerous not set - limiting range of test to 2^%d\n", max_bits); + } + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + if (!cli->transport->negotiate.lockread_supported) { + torture_comment(tctx, "Server does not support writeunlock - skipping\n"); + return true; + } + + if (!torture_setup_dir(cli, BASEDIR)) { + torture_fail(tctx, "failed to setup basedir"); + } + + torture_comment(tctx, "Testing RAW_WRITE_WRITEX\n"); + io.generic.level = RAW_WRITE_WRITEX; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree))); + } + + torture_comment(tctx, "Trying zero write\n"); + io.writex.in.file.fnum = fnum; + io.writex.in.offset = 0; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.count = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying small write\n"); + io.writex.in.count = 9; + io.writex.in.offset = 4; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying large write\n"); + io.writex.in.count = 4000; + io.writex.in.offset = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + + torture_comment(tctx, "Trying bad fnum\n"); + io.writex.in.file.fnum = fnum+1; + io.writex.in.count = 4000; + io.writex.in.offset = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "Testing wmode\n"); + io.writex.in.file.fnum = fnum; + io.writex.in.count = 1; + io.writex.in.offset = 0; + io.writex.in.wmode = 1; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + io.writex.in.wmode = 2; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + + torture_comment(tctx, "Trying locked region\n"); + cli->session->pid++; + if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 3, 1, 0, WRITE_LOCK))) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to lock file at %s\n", __location__)); + } + cli->session->pid--; + io.writex.in.wmode = 0; + io.writex.in.count = 4; + io.writex.in.offset = 0; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + torture_comment(tctx, "Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n"); + } + + torture_comment(tctx, "Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writex.in.file.fnum = fnum; + io.writex.in.count = 4000; + io.writex.in.offset = 0xFFFFFFFF - 2000; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + + for (i=33;itree, &io); + if (i>33 && + NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + break; + } + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed+1, 4000); + } + torture_comment(tctx, "limit is 2^%d\n", i); + + setup_buffer(buf, seed, maxsize); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test write unlock ops +*/ +static bool test_writeunlock(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_write io; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned int seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + if (!cli->transport->negotiate.lockread_supported) { + torture_skip(tctx, "Server does not support writeunlock - skipping\n"); + } + + if (!torture_setup_dir(cli, BASEDIR)) { + torture_fail(tctx, "failed to setup basedir"); + } + + torture_comment(tctx, "Testing RAW_WRITE_WRITEUNLOCK\n"); + io.generic.level = RAW_WRITE_WRITEUNLOCK; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree))); + } + + torture_comment(tctx, "Trying zero write\n"); + io.writeunlock.in.file.fnum = fnum; + io.writeunlock.in.count = 0; + io.writeunlock.in.offset = 0; + io.writeunlock.in.remaining = 0; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying small write\n"); + io.writeunlock.in.count = 9; + io.writeunlock.in.offset = 4; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying large write\n"); + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0; + io.writeunlock.in.data = buf; + smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, 4000); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + + torture_comment(tctx, "Trying bad fnum\n"); + io.writeunlock.in.file.fnum = fnum+1; + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n"); + } + + torture_comment(tctx, "Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeunlock.in.file.fnum = fnum; + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0xFFFFFFFF - 2000; + io.writeunlock.in.data = buf; + smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, 4000); + CHECK_ALL_INFO(io.writeunlock.in.count + (uint64_t)io.writeunlock.in.offset, size); + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + + +/* + test write close ops +*/ +static bool test_writeclose(struct torture_context *tctx, + struct smbcli_state *cli) +{ + union smb_write io; + NTSTATUS status; + bool ret = true; + int fnum; + uint8_t *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned int seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero_array(tctx, uint8_t, maxsize); + + if (!torture_setting_bool(tctx, "writeclose_support", true)) { + torture_skip(tctx, "Server does not support writeclose - skipping\n"); + } + + if (!torture_setup_dir(cli, BASEDIR)) { + torture_fail(tctx, "failed to setup basedir"); + } + + torture_comment(tctx, "Testing RAW_WRITE_WRITECLOSE\n"); + io.generic.level = RAW_WRITE_WRITECLOSE; + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree))); + } + + torture_comment(tctx, "Trying zero write\n"); + io.writeclose.in.file.fnum = fnum; + io.writeclose.in.count = 0; + io.writeclose.in.offset = 0; + io.writeclose.in.mtime = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying small write\n"); + io.writeclose.in.count = 9; + io.writeclose.in.offset = 4; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + io.writeclose.in.file.fnum = fnum; + + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + io.writeclose.in.file.fnum = fnum; + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + torture_comment(tctx, "Trying large write\n"); + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, 4000); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + io.writeclose.in.file.fnum = fnum; + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + + torture_comment(tctx, "Trying bad fnum\n"); + io.writeclose.in.file.fnum = fnum+1; + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + torture_comment(tctx, "Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n"); + } + + torture_comment(tctx, "Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeclose.in.file.fnum = fnum; + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0xFFFFFFFF - 2000; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, 4000); + CHECK_ALL_INFO(io.writeclose.in.count + (uint64_t)io.writeclose.in.offset, size); + + fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); + io.writeclose.in.file.fnum = fnum; + + memset(buf, 0, maxsize); + if (smbcli_read(cli->tree, fnum, buf, io.writeclose.in.offset, 4000) != 4000) { + ret = false; + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__)); + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smbcli_close(cli->tree, fnum); + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + test a deliberately bad SMB1 write. +*/ +static bool test_bad_write(struct torture_context *tctx, + struct smbcli_state *cli) +{ + bool ret = false; + int fnum = -1; + struct smbcli_request *req = NULL; + const char *fname = BASEDIR "\\badwrite.txt"; + bool ok = false; + + if (!torture_setup_dir(cli, BASEDIR)) { + torture_fail(tctx, "failed to setup basedir"); + } + + torture_comment(tctx, "Testing RAW_BAD_WRITE\n"); + + fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "Failed to create %s - %s\n", + fname, + smbcli_errstr(cli->tree))); + } + + req = smbcli_request_setup(cli->tree, + SMBwrite, + 5, + 0); + if (req == NULL) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, "talloc fail\n")); + } + + SSVAL(req->out.vwv, VWV(0), fnum); + SSVAL(req->out.vwv, VWV(1), 65535); /* bad write length. */ + SIVAL(req->out.vwv, VWV(2), 0); /* offset */ + SSVAL(req->out.vwv, VWV(4), 0); /* remaining. */ + + if (!smbcli_request_send(req)) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, "Send failed\n")); + } + + if (!smbcli_request_receive(req)) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, "Receive failed\n")); + } + + /* + * Check for expected error codes. + * ntvfs returns NT_STATUS_UNSUCCESSFUL. + */ + ok = (NT_STATUS_EQUAL(req->status, NT_STATUS_INVALID_PARAMETER) || + NT_STATUS_EQUAL(req->status, NT_STATUS_UNSUCCESSFUL)); + + if (!ok) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "Should have returned " + "NT_STATUS_INVALID_PARAMETER or " + "NT_STATUS_UNSUCCESSFUL " + "got %s\n", + nt_errstr(req->status))); + } + + ret = true; + +done: + if (req != NULL) { + smbcli_request_destroy(req); + } + if (fnum != -1) { + smbcli_close(cli->tree, fnum); + } + smb_raw_exit(cli->session); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + +/* + basic testing of write calls +*/ +struct torture_suite *torture_raw_write(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "write"); + + torture_suite_add_1smb_test(suite, "write", test_write); + torture_suite_add_1smb_test(suite, "write unlock", test_writeunlock); + torture_suite_add_1smb_test(suite, "write close", test_writeclose); + torture_suite_add_1smb_test(suite, "writex", test_writex); + torture_suite_add_1smb_test(suite, "bad-write", test_bad_write); + + return suite; +} diff --git a/source4/torture/rpc/alter_context.c b/source4/torture/rpc/alter_context.c new file mode 100644 index 0000000..9b69727 --- /dev/null +++ b/source4/torture/rpc/alter_context.c @@ -0,0 +1,112 @@ +/* + Unix SMB/CIFS implementation. + + test suite for dcerpc alter_context operations + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_dssetup.h" +#include "torture/rpc/torture_rpc.h" + +bool torture_rpc_alter_context(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p, *p2, *p3; + struct policy_handle *handle; + struct ndr_interface_table tmptbl; + bool ret = true; + + torture_comment(torture, "opening LSA connection\n"); + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "connecting"); + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + if (!test_lsa_OpenPolicy2(p->binding_handle, torture, &handle)) { + ret = false; + } + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + torture_comment(torture, "Opening secondary DSSETUP context\n"); + status = dcerpc_secondary_context(p, &p2, &ndr_table_dssetup); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p2, torture, &p2->syntax, &p2->transfer_syntax); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + tmptbl = ndr_table_dssetup; + tmptbl.syntax_id.if_version += 100; + torture_comment(torture, "Opening bad secondary connection\n"); + status = dcerpc_secondary_context(p, &p3, &tmptbl); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX, + "dcerpc_alter_context with wrong version should fail"); + + torture_comment(torture, "Testing DSSETUP pipe operations\n"); + ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2); + + if (handle) { + ret &= test_lsa_Close(p->binding_handle, torture, handle); + } + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax); + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + ret &= test_lsa_OpenPolicy2(p->binding_handle, torture, &handle); + + if (handle) { + ret &= test_lsa_Close(p->binding_handle, torture, handle); + } + + torture_comment(torture, "Testing change of primary context\n"); + status = dcerpc_alter_context(p, torture, &p2->syntax, &p2->transfer_syntax); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) { + + ret &= test_lsa_OpenPolicy2_ex(p->binding_handle, torture, &handle, + NT_STATUS_CONNECTION_DISCONNECTED, + NT_STATUS_CONNECTION_RESET); + + torture_assert(torture, !dcerpc_binding_handle_is_connected(p->binding_handle), + "dcerpc disconnected"); + + return ret; + } + torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed"); + + torture_comment(torture, "Testing DSSETUP pipe operations - should fault\n"); + ret &= test_DsRoleGetPrimaryDomainInformation_ext(torture, p, NT_STATUS_RPC_BAD_STUB_DATA); + + ret &= test_lsa_OpenPolicy2(p->binding_handle, torture, &handle); + + if (handle) { + ret &= test_lsa_Close(p->binding_handle, torture, handle); + } + + torture_comment(torture, "Testing DSSETUP pipe operations\n"); + + ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2); + + return ret; +} diff --git a/source4/torture/rpc/async_bind.c b/source4/torture/rpc/async_bind.c new file mode 100644 index 0000000..e86d0ab --- /dev/null +++ b/source4/torture/rpc/async_bind.c @@ -0,0 +1,86 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc torture tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Rafal Szczesniak 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" + +/* + This test initiates multiple rpc bind requests and verifies + whether all of them are served. +*/ + + +bool torture_async_bind(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + int i; + const char *binding_string; + struct cli_credentials *creds; + extern int torture_numasync; + + struct composite_context **bind_req; + struct dcerpc_pipe **pipes; + const struct ndr_interface_table **table; + + if (!torture_setting_bool(torture, "async", false)) { + printf("async bind test disabled - enable async tests to use\n"); + return true; + } + + binding_string = torture_setting_string(torture, "binding", NULL); + + /* talloc context */ + mem_ctx = talloc_init("torture_async_bind"); + if (mem_ctx == NULL) return false; + + bind_req = talloc_array(torture, struct composite_context*, torture_numasync); + if (bind_req == NULL) return false; + pipes = talloc_array(torture, struct dcerpc_pipe*, torture_numasync); + if (pipes == NULL) return false; + table = talloc_array(torture, const struct ndr_interface_table*, torture_numasync); + if (table == NULL) return false; + + /* credentials */ + creds = samba_cmdline_get_creds(); + + /* send bind requests */ + for (i = 0; i < torture_numasync; i++) { + table[i] = &ndr_table_lsarpc; + bind_req[i] = dcerpc_pipe_connect_send(mem_ctx, binding_string, + table[i], creds, torture->ev, torture->lp_ctx); + } + + /* recv bind requests */ + for (i = 0; i < torture_numasync; i++) { + status = dcerpc_pipe_connect_recv(bind_req[i], mem_ctx, &pipes[i]); + if (!NT_STATUS_IS_OK(status)) { + printf("async rpc connection failed: %s\n", nt_errstr(status)); + return false; + } + } + + talloc_free(mem_ctx); + return true; +} diff --git a/source4/torture/rpc/atsvc.c b/source4/torture/rpc/atsvc.c new file mode 100644 index 0000000..729a7ca --- /dev/null +++ b/source4/torture/rpc/atsvc.c @@ -0,0 +1,138 @@ +/* + Unix SMB/CIFS implementation. + test suite for atsvc rpc operations + + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_atsvc_c.h" +#include "torture/rpc/torture_rpc.h" + +static bool test_JobGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t job_id) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct atsvc_JobGetInfo r; + struct atsvc_JobInfo *info = talloc(tctx, struct atsvc_JobInfo); + if (!info) { + return false; + } + + r.in.servername = dcerpc_server_name(p); + r.in.job_id = job_id; + r.out.job_info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobGetInfo_r(b, tctx, &r), + "JobGetInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "JobGetInfo failed"); + + return true; +} + +static bool test_JobDel(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t min_job_id, + uint32_t max_job_id) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct atsvc_JobDel r; + + r.in.servername = dcerpc_server_name(p); + r.in.min_job_id = min_job_id; + r.in.max_job_id = max_job_id; + + torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobDel_r(b, tctx, &r), + "JobDel failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "JobDel failed"); + + return true; +} + +static bool test_JobEnum(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct atsvc_JobEnum r; + struct atsvc_enum_ctr ctr; + uint32_t resume_handle = 0, i, total_entries = 0; + + bool ret = true; + + r.in.servername = dcerpc_server_name(p); + ctr.entries_read = 0; + ctr.first_entry = NULL; + r.in.ctr = r.out.ctr = &ctr; + r.in.preferred_max_len = 0xffffffff; + r.in.resume_handle = r.out.resume_handle = &resume_handle; + r.out.total_entries = &total_entries; + + torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobEnum_r(b, tctx, &r), + "JobEnum failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "JobEnum failed"); + + for (i = 0; i < r.out.ctr->entries_read; i++) { + if (!test_JobGetInfo(p, tctx, r.out.ctr->first_entry[i].job_id)) { + ret = false; + } + } + + return ret; +} + +static bool test_JobAdd(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct atsvc_JobAdd r; + struct atsvc_JobInfo info; + + r.in.servername = dcerpc_server_name(p); + info.job_time = 0x050ae4c0; /* 11:30pm */ + info.days_of_month = 0; /* n/a */ + info.days_of_week = 0x02; /* Tuesday */ + info.flags = 0x11; /* periodic, non-interactive */ + info.command = "foo.exe"; + r.in.job_info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobAdd_r(b, tctx, &r), + "JobAdd failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "JobAdd failed"); + + /* Run EnumJobs again in case there were no jobs to begin with */ + + if (!test_JobEnum(tctx, p)) { + return false; + } + + if (!test_JobGetInfo(p, tctx, *r.out.job_id)) { + return false; + } + + if (!test_JobDel(p, tctx, *r.out.job_id, *r.out.job_id)) { + return false; + } + + return true; +} + +struct torture_suite *torture_rpc_atsvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "atsvc"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "atsvc", &ndr_table_atsvc); + + torture_rpc_tcase_add_test(tcase, "JobEnum", test_JobEnum); + torture_rpc_tcase_add_test(tcase, "JobAdd", test_JobAdd); + + return suite; +} diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c new file mode 100644 index 0000000..71cdf0f --- /dev/null +++ b/source4/torture/rpc/backupkey.c @@ -0,0 +1,2431 @@ +/* + Unix SMB/CIFS implementation. + test suite for backupkey remote protocol rpc operations + + Copyright (C) Matthieu Patou 2010-2011 + Copyright (C) Andreas Schneider 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "../libcli/security/security.h" + +#include "torture/rpc/torture_rpc.h" +#include "torture/ndr/ndr.h" + +#include "librpc/gen_ndr/ndr_backupkey_c.h" +#include "librpc/gen_ndr/ndr_backupkey.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/auth/proto.h" +#include + +#include +#include +#include +#include + +enum test_wrong { + WRONG_MAGIC, + WRONG_R2, + WRONG_PAYLOAD_LENGTH, + WRONG_CIPHERTEXT_LENGTH, + SHORT_PAYLOAD_LENGTH, + SHORT_CIPHERTEXT_LENGTH, + ZERO_PAYLOAD_LENGTH, + ZERO_CIPHERTEXT_LENGTH, + RIGHT_KEY, + WRONG_KEY, + WRONG_SID, +}; + +/* Our very special and valued secret */ +/* No need to put const as we cast the array in uint8_t + * we will get a warning about the discarded const + */ +static const char secret[] = "tata yoyo mais qu'est ce qu'il y a sous ton grand chapeau ?"; + +/* Get the SID from a user */ +static struct dom_sid *get_user_sid(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *user) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + struct lsa_Close c; + NTSTATUS status; + struct policy_handle handle; + struct lsa_LookupNames l; + struct lsa_TransSidArray sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String lsa_name; + uint32_t count = 0; + struct dom_sid *result; + TALLOC_CTX *tmp_ctx; + struct dcerpc_pipe *p2; + struct dcerpc_binding_handle *b; + + const char *domain = cli_credentials_get_domain( + samba_cmdline_get_creds()); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_lsarpc), + "could not open lsarpc pipe"); + b = p2->binding_handle; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NULL; + } + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2_r(b, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, + "OpenPolicy2 failed - %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, + "OpenPolicy2_ failed - %s\n", + nt_errstr(r.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + sids.count = 0; + sids.sids = NULL; + + lsa_name.string = talloc_asprintf(tmp_ctx, "%s\\%s", domain, user); + + l.in.handle = &handle; + l.in.num_names = 1; + l.in.names = &lsa_name; + l.in.sids = &sids; + l.in.level = 1; + l.in.count = &count; + l.out.count = &count; + l.out.sids = &sids; + l.out.domains = &domains; + + status = dcerpc_lsa_LookupNames_r(b, tmp_ctx, &l); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, + "LookupNames of %s failed - %s\n", + lsa_name.string, + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + if (domains->count == 0) { + return NULL; + } + + result = dom_sid_add_rid(mem_ctx, + domains->domains[0].sid, + l.out.sids->sids[0].rid); + c.in.handle = &handle; + c.out.handle = &handle; + + status = dcerpc_lsa_Close_r(b, tmp_ctx, &c); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, + "dcerpc_lsa_Close failed - %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + + if (!NT_STATUS_IS_OK(c.out.result)) { + torture_comment(tctx, + "dcerpc_lsa_Close failed - %s\n", + nt_errstr(c.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + talloc_free(tmp_ctx); + talloc_free(p2); + + torture_comment(tctx, "Get_user_sid finished\n"); + return result; +} + +/* + * Create a bkrp_encrypted_secret_vX structure + * the version depends on the version parameter + * the structure is returned as a blob. + * The broken flag is to indicate if we want + * to create a non conform to specification structure + */ +static DATA_BLOB *create_unencryptedsecret(TALLOC_CTX *mem_ctx, + bool broken, + int version) +{ + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + DATA_BLOB *blob = talloc_zero(mem_ctx, DATA_BLOB); + enum ndr_err_code ndr_err; + + if (version == 2) { + struct bkrp_encrypted_secret_v2 unenc_sec; + + ZERO_STRUCT(unenc_sec); + unenc_sec.secret_len = sizeof(secret); + unenc_sec.secret = discard_const_p(uint8_t, secret); + generate_random_buffer(unenc_sec.payload_key, + sizeof(unenc_sec.payload_key)); + + ndr_err = ndr_push_struct_blob(blob, blob, &unenc_sec, + (ndr_push_flags_fn_t)ndr_push_bkrp_encrypted_secret_v2); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NULL; + } + + if (broken) { + /* The magic value is correctly set by the NDR push + * but we want to test the behavior of the server + * if a different value is provided + */ + ((uint8_t*)blob->data)[4] = 79; /* A great year !!! */ + } + } + + if (version == 3) { + struct bkrp_encrypted_secret_v3 unenc_sec; + + ZERO_STRUCT(unenc_sec); + unenc_sec.secret_len = sizeof(secret); + unenc_sec.secret = discard_const_p(uint8_t, secret); + generate_random_buffer(unenc_sec.payload_key, + sizeof(unenc_sec.payload_key)); + + ndr_err = ndr_push_struct_blob(blob, blob, &unenc_sec, + (ndr_push_flags_fn_t)ndr_push_bkrp_encrypted_secret_v3); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NULL; + } + + if (broken) { + /* + * The magic value is correctly set by the NDR push + * but we want to test the behavior of the server + * if a different value is provided + */ + ((uint8_t*)blob->data)[4] = 79; /* A great year !!! */ + } + } + talloc_free(tmp_ctx); + return blob; +} + +/* + * Create an access check structure, the format depends on the version parameter. + * If broken is specified then we create a structure that isn't conform to the + * specification. + * + * If the structure can't be created then NULL is returned. + */ +static DATA_BLOB *create_access_check(struct torture_context *tctx, + struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *user, + bool broken, + uint32_t version) +{ + TALLOC_CTX *tmp_ctx = NULL; + DATA_BLOB *blob = NULL; + enum ndr_err_code ndr_err; + const struct dom_sid *sid = NULL; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NULL; + } + + sid = get_user_sid(tctx, tmp_ctx, user); + if (sid == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + blob = talloc_zero(mem_ctx, DATA_BLOB); + if (blob == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + if (version == 2) { + struct bkrp_access_check_v2 access_struct; + gnutls_hash_hd_t dig_ctx; + uint8_t nonce[32]; + int rc; + + ZERO_STRUCT(access_struct); + generate_random_buffer(nonce, sizeof(nonce)); + access_struct.nonce_len = sizeof(nonce); + access_struct.nonce = nonce; + access_struct.sid = *sid; + + ndr_err = ndr_push_struct_blob(blob, blob, &access_struct, + (ndr_push_flags_fn_t)ndr_push_bkrp_access_check_v2); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + + /* + * We pushed the whole structure including a null hash + * but the hash need to be calculated only up to the hash field + * so we reduce the size of what has to be calculated + */ + + rc = gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA1); + if (rc != GNUTLS_E_SUCCESS) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + rc = gnutls_hash(dig_ctx, + blob->data, + blob->length - sizeof(access_struct.hash)); + gnutls_hash_deinit(dig_ctx, + blob->data + blob->length - sizeof(access_struct.hash)); + if (rc != GNUTLS_E_SUCCESS) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + + /* Altering the SHA */ + if (broken) { + blob->data[blob->length - 1]++; + } + } + + if (version == 3) { + struct bkrp_access_check_v3 access_struct; + gnutls_hash_hd_t dig_ctx; + uint8_t nonce[32]; + int rc; + + ZERO_STRUCT(access_struct); + generate_random_buffer(nonce, sizeof(nonce)); + access_struct.nonce_len = sizeof(nonce); + access_struct.nonce = nonce; + access_struct.sid = *sid; + + ndr_err = ndr_push_struct_blob(blob, blob, &access_struct, + (ndr_push_flags_fn_t)ndr_push_bkrp_access_check_v3); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + + /*We pushed the whole structure including a null hash + * but the hash need to be calculated only up to the hash field + * so we reduce the size of what has to be calculated + */ + + rc = gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA512); + if (rc != GNUTLS_E_SUCCESS) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + rc = gnutls_hash(dig_ctx, + blob->data, + blob->length - sizeof(access_struct.hash)); + gnutls_hash_deinit(dig_ctx, + blob->data + blob->length - sizeof(access_struct.hash)); + if (rc != GNUTLS_E_SUCCESS) { + talloc_free(blob); + talloc_free(tmp_ctx); + return NULL; + } + + /* Altering the SHA */ + if (broken) { + blob->data[blob->length -1]++; + } + } + talloc_free(tmp_ctx); + return blob; +} + + +static DATA_BLOB *encrypt_blob(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + DATA_BLOB *key, + DATA_BLOB *iv, + DATA_BLOB *to_encrypt, + gnutls_cipher_algorithm_t cipher_algo) +{ + gnutls_cipher_hd_t cipher_handle = { 0 }; + gnutls_datum_t gkey = { + .data = key->data, + .size = key->length, + }; + gnutls_datum_t giv = { + .data = iv->data, + .size = iv->length, + }; + DATA_BLOB *blob; + int rc; + + blob = talloc(mem_ctx, DATA_BLOB); + if (blob == NULL) { + return NULL; + } + + *blob = data_blob_talloc_zero(mem_ctx, to_encrypt->length); + if (blob->data == NULL) { + talloc_free(blob); + return NULL; + } + + rc = gnutls_cipher_init(&cipher_handle, + cipher_algo, + &gkey, + &giv); + if (rc != GNUTLS_E_SUCCESS) { + torture_comment(tctx, + "gnutls_cipher_init failed: %s\n", + gnutls_strerror(rc)); + talloc_free(blob); + return NULL; + } + + rc = gnutls_cipher_encrypt2(cipher_handle, + to_encrypt->data, + to_encrypt->length, + blob->data, + blob->length); + gnutls_cipher_deinit(cipher_handle); + if (rc != GNUTLS_E_SUCCESS) { + torture_comment(tctx, + "gnutls_cipher_decrypt2 failed: %s\n", + gnutls_strerror(rc)); + return NULL; + } + + return blob; +} + +/* + * Certs used for this protocol have a GUID in the issuer_uniq_id field. + * This function fetch it. + */ +static struct GUID *get_cert_guid(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + uint8_t *cert_data, + uint32_t cert_len) +{ + gnutls_x509_crt_t x509_cert = NULL; + gnutls_datum_t x509_crt_data = { + .data = cert_data, + .size = cert_len, + }; + uint8_t dummy[1] = {0}; + DATA_BLOB issuer_unique_id = { + .data = dummy, + .length = 0, + }; + struct GUID *guid = talloc_zero(mem_ctx, struct GUID); + NTSTATUS status; + int rc; + + rc = gnutls_x509_crt_init(&x509_cert); + if (rc != GNUTLS_E_SUCCESS) { + torture_comment(tctx, + "gnutls_x509_crt_init failed - %s", + gnutls_strerror(rc)); + return NULL; + } + + rc = gnutls_x509_crt_import(x509_cert, + &x509_crt_data, + GNUTLS_X509_FMT_DER); + if (rc != GNUTLS_E_SUCCESS) { + torture_comment(tctx, + "gnutls_x509_crt_import failed - %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(x509_cert); + return NULL; + } + + /* Get the buffer size */ + rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert, + (char *)issuer_unique_id.data, + &issuer_unique_id.length); + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER || + issuer_unique_id.length == 0) { + gnutls_x509_crt_deinit(x509_cert); + return NULL; + } + + issuer_unique_id = data_blob_talloc_zero(mem_ctx, + issuer_unique_id.length); + if (issuer_unique_id.data == NULL) { + gnutls_x509_crt_deinit(x509_cert); + return NULL; + } + + rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert, + (char *)issuer_unique_id.data, + &issuer_unique_id.length); + gnutls_x509_crt_deinit(x509_cert); + if (rc != GNUTLS_E_SUCCESS) { + torture_comment(tctx, + "gnutls_x509_crt_get_issuer_unique_id failed - %s", + gnutls_strerror(rc)); + return NULL; + } + + status = GUID_from_data_blob(&issuer_unique_id, guid); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return guid; +} + +/* + * Encrypt a blob with the private key of the certificate + * passed as a parameter. + */ +static DATA_BLOB *encrypt_blob_pk(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + uint8_t *cert_data, + uint32_t cert_len, + DATA_BLOB *to_encrypt) +{ + gnutls_x509_crt_t x509_cert; + gnutls_datum_t x509_crt_data = { + .data = cert_data, + .size = cert_len, + }; + gnutls_pubkey_t pubkey; + gnutls_datum_t plaintext = { + .data = to_encrypt->data, + .size = to_encrypt->length, + }; + gnutls_datum_t ciphertext = { + .data = NULL, + }; + DATA_BLOB *blob; + int rc; + + rc = gnutls_x509_crt_init(&x509_cert); + if (rc != GNUTLS_E_SUCCESS) { + return NULL; + } + + rc = gnutls_x509_crt_import(x509_cert, + &x509_crt_data, + GNUTLS_X509_FMT_DER); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit(x509_cert); + return NULL; + } + + rc = gnutls_pubkey_init(&pubkey); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit(x509_cert); + return NULL; + } + + rc = gnutls_pubkey_import_x509(pubkey, + x509_cert, + 0); + gnutls_x509_crt_deinit(x509_cert); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_pubkey_deinit(pubkey); + return NULL; + } + + rc = gnutls_pubkey_encrypt_data(pubkey, + 0, + &plaintext, + &ciphertext); + gnutls_pubkey_deinit(pubkey); + if (rc != GNUTLS_E_SUCCESS) { + return NULL; + } + + blob = talloc_zero(mem_ctx, DATA_BLOB); + if (blob == NULL) { + gnutls_pubkey_deinit(pubkey); + return NULL; + } + + *blob = data_blob_talloc(blob, ciphertext.data, ciphertext.size); + gnutls_free(ciphertext.data); + if (blob->data == NULL) { + gnutls_pubkey_deinit(pubkey); + return NULL; + } + + return blob; +} + +static struct bkrp_BackupKey *createRetrieveBackupKeyGUIDStruct(struct torture_context *tctx, + struct dcerpc_pipe *p, int version, DATA_BLOB *out) +{ + struct dcerpc_binding *binding; + struct bkrp_client_side_wrapped data; + struct GUID *g = talloc(tctx, struct GUID); + struct bkrp_BackupKey *r = talloc_zero(tctx, struct bkrp_BackupKey); + enum ndr_err_code ndr_err; + DATA_BLOB blob; + NTSTATUS status; + + if (r == NULL) { + return NULL; + } + + binding = dcerpc_binding_dup(tctx, p->binding); + if (binding == NULL) { + return NULL; + } + + status = dcerpc_binding_set_flags(binding, DCERPC_SEAL|DCERPC_AUTH_SPNEGO, 0); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + ZERO_STRUCT(data); + status = GUID_from_string(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID, g); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + r->in.guidActionAgent = g; + data.version = version; + ndr_err = ndr_push_struct_blob(&blob, tctx, &data, + (ndr_push_flags_fn_t)ndr_push_bkrp_client_side_wrapped); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NULL; + } + r->in.data_in = blob.data; + r->in.data_in_len = blob.length; + r->out.data_out = &out->data; + r->out.data_out_len = talloc(r, uint32_t); + return r; +} + +static struct bkrp_BackupKey *createRestoreGUIDStruct(struct torture_context *tctx, + struct dcerpc_pipe *p, int version, DATA_BLOB *out, + bool norevert, + bool broken_version, + bool broken_user, + bool broken_magic_secret, + bool broken_magic_access, + bool broken_hash_access, + bool broken_cert_guid) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct bkrp_client_side_wrapped data; + DATA_BLOB *xs; + DATA_BLOB *sec; + DATA_BLOB *enc_sec = NULL; + DATA_BLOB *enc_xs = NULL; + DATA_BLOB *blob2; + DATA_BLOB enc_sec_reverted; + DATA_BLOB key; + DATA_BLOB iv; + DATA_BLOB out_blob; + struct GUID *guid, *g; + int t; + uint32_t size; + enum ndr_err_code ndr_err; + NTSTATUS status; + const char *user; + gnutls_cipher_algorithm_t cipher_algo; + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, version, &out_blob); + if (r == NULL) { + return NULL; + } + + if (broken_user) { + /* we take a fake user*/ + user = "guest"; + } else { + user = cli_credentials_get_username( + samba_cmdline_get_creds()); + } + + + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + "Get GUID"); + torture_assert_werr_ok(tctx, r->out.result, + "Get GUID"); + + /* + * We have to set it outside of the function createRetrieveBackupKeyGUIDStruct + * the len of the blob, this is due to the fact that they don't have the + * same size (one is 32bits the other 64bits) + */ + out_blob.length = *r->out.data_out_len; + + sec = create_unencryptedsecret(tctx, broken_magic_secret, version); + if (sec == NULL) { + return NULL; + } + + xs = create_access_check(tctx, p, tctx, user, broken_hash_access, version); + if (xs == NULL) { + return NULL; + } + + if (broken_magic_access){ + /* The start of the access_check structure contains the + * GUID of the certificate + */ + xs->data[0]++; + } + + enc_sec = encrypt_blob_pk(tctx, tctx, out_blob.data, out_blob.length, sec); + if (!enc_sec) { + return NULL; + } + enc_sec_reverted.data = talloc_array(tctx, uint8_t, enc_sec->length); + if (enc_sec_reverted.data == NULL) { + return NULL; + } + enc_sec_reverted.length = enc_sec->length; + + /* + * We DO NOT revert the array on purpose it's in order to check that + * when the server is not able to decrypt then it answer the correct error + */ + if (norevert) { + for(t=0; t< enc_sec->length; t++) { + enc_sec_reverted.data[t] = ((uint8_t*)enc_sec->data)[t]; + } + } else { + for(t=0; t< enc_sec->length; t++) { + enc_sec_reverted.data[t] = ((uint8_t*)enc_sec->data)[enc_sec->length - t -1]; + } + } + + size = sec->length; + switch (version) { + case 2: + cipher_algo = GNUTLS_CIPHER_3DES_CBC; + break; + case 3: + cipher_algo = GNUTLS_CIPHER_AES_256_CBC; + break; + default: + return NULL; + } + iv.length = gnutls_cipher_get_iv_size(cipher_algo); + iv.data = sec->data + (size - iv.length); + + key.length = gnutls_cipher_get_key_size(cipher_algo); + key.data = sec->data + (size - (key.length + iv.length)); + + enc_xs = encrypt_blob(tctx, tctx, &key, &iv, xs, cipher_algo); + if (!enc_xs) { + return NULL; + } + + /* To cope with the fact that heimdal do padding at the end for the moment */ + enc_xs->length = xs->length; + + guid = get_cert_guid(tctx, tctx, out_blob.data, out_blob.length); + if (guid == NULL) { + return NULL; + } + + if (broken_version) { + data.version = 1; + } else { + data.version = version; + } + + data.guid = *guid; + data.encrypted_secret = enc_sec_reverted.data; + data.access_check = enc_xs->data; + data.encrypted_secret_len = enc_sec->length; + data.access_check_len = enc_xs->length; + + /* We want the blob to persist after this function so we don't + * allocate it in the stack + */ + blob2 = talloc(tctx, DATA_BLOB); + if (blob2 == NULL) { + return NULL; + } + + ndr_err = ndr_push_struct_blob(blob2, tctx, &data, + (ndr_push_flags_fn_t)ndr_push_bkrp_client_side_wrapped); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NULL; + } + + if (broken_cert_guid) { + blob2->data[12]++; + } + + ZERO_STRUCT(*r); + + g = talloc(tctx, struct GUID); + if (g == NULL) { + return NULL; + } + + status = GUID_from_string(BACKUPKEY_RESTORE_GUID, g); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + r->in.guidActionAgent = g; + r->in.data_in = blob2->data; + r->in.data_in_len = blob2->length; + r->in.param = 0; + r->out.data_out = &(out->data); + r->out.data_out_len = talloc(r, uint32_t); + return r; +} + +/* Check that we are able to receive the certificate of the DCs + * used for client wrap version of the backup key protocol + */ +static bool test_RetrieveBackupKeyGUID(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + if (r == NULL) { + return false; + } + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + "Get GUID"); + + out_blob.length = *r->out.data_out_len; + torture_assert_werr_equal(tctx, + r->out.result, + WERR_OK, + "Wrong dce/rpc error code"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, + "Get GUID"); + } + return true; +} + +/* Test to check the failure to recover a secret because the + * secret blob is not reversed + */ +static bool test_RestoreGUID_ko(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + true, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Wrong error code"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_wrongversion(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + false, true, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Wrong error code on wrong version"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_wronguser(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + false, false, true, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_ACCESS, "Restore GUID"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_v3(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, + false, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 1, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_OK, "Restore GUID"); + torture_assert_str_equal(tctx, (char*)resp.secret.data, secret, "Wrong secret"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + false, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + torture_assert_werr_equal(tctx, r->out.result, WERR_OK, "Restore GUID"); + torture_assert_ndr_err_equal(tctx, + ndr_pull_struct_blob(&out_blob, tctx, &resp, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped), + NDR_ERR_SUCCESS, + "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_str_equal(tctx, (char*)resp.secret.data, secret, "Wrong secret"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_badmagiconsecret(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, + false, false, false, true, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Wrong error code while providing bad magic in secret"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_emptyrequest(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, + false, false, false, true, false, false, true); + + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + r->in.data_in = talloc(tctx, uint8_t); + r->in.data_in_len = 0; + r->in.param = 0; + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Bad error code on wrong has in access check"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_badcertguid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, + false, false, false, false, false, false, true); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct() failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + + /* + * Windows 2012R2 has, presumably, a programming error + * returning an NTSTATUS code on this interface + */ + if (W_ERROR_V(r->out.result) != NT_STATUS_V(NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check"); + } + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_badmagicaccesscheck(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + false, false, false, false, true, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +static bool test_RestoreGUID_badhashaccesscheck(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum ndr_err_code ndr_err; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_client_side_unwrapped resp; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, + false, false, false, false, false, true, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); + torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); + out_blob.length = *r->out.data_out_len; + ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); + torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check"); + } else { + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, "Get GUID"); + } + + return true; +} + +/* + * Check that the RSA modulus in the certificate of the DCs has 2048 bits. + */ +static bool test_RetrieveBackupKeyGUID_validate(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + torture_assert(tctx, r != NULL, "test_RetrieveBackupKeyGUID_validate failed"); + + if (r == NULL) { + return false; + } + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + gnutls_x509_crt_t x509_cert = NULL; + gnutls_pubkey_t pubkey = NULL; + gnutls_datum_t x509_crt_data; + gnutls_pk_algorithm_t pubkey_algo; + uint8_t dummy[1] = {0}; + DATA_BLOB subject_unique_id = { + .data = dummy, + .length = 0, + }; + DATA_BLOB issuer_unique_id = { + .data = dummy, + .length = 0, + }; + DATA_BLOB reversed = { + .data = dummy, + .length = 0, + }; + DATA_BLOB serial_number; + unsigned int RSA_returned_bits = 0; + int version; + size_t i; + int cmp; + int rc; + + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + "Get GUID"); + + torture_assert_werr_ok(tctx, r->out.result, + "Get GUID"); + + out_blob.length = *r->out.data_out_len; + + x509_crt_data.data = out_blob.data; + x509_crt_data.size = out_blob.length; + + rc = gnutls_x509_crt_init(&x509_cert); + if (rc != GNUTLS_E_SUCCESS) { + return NULL; + } + + rc = gnutls_x509_crt_import(x509_cert, + &x509_crt_data, + GNUTLS_X509_FMT_DER); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_x509_crt_import failed"); + + /* Compare unique ids */ + + /* Get buffer size */ + rc = gnutls_x509_crt_get_subject_unique_id(x509_cert, + (char *)subject_unique_id.data, + &subject_unique_id.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SHORT_MEMORY_BUFFER, + "gnutls_x509_crt_get_subject_unique_id " + "get buffer size failed"); + + subject_unique_id = data_blob_talloc_zero(tctx, + subject_unique_id.length); + + rc = gnutls_x509_crt_get_subject_unique_id(x509_cert, + (char *)subject_unique_id.data, + &subject_unique_id.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_x509_crt_get_subject_unique_id failed"); + + rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert, + (char *)issuer_unique_id.data, + &issuer_unique_id.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SHORT_MEMORY_BUFFER, + "gnutls_x509_crt_get_issuer_unique_id " + "get buffer size failed"); + + issuer_unique_id = data_blob_talloc_zero(tctx, + issuer_unique_id.length); + + rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert, + (char *)issuer_unique_id.data, + &issuer_unique_id.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_x509_crt_get_issuer_unique_id failed"); + + cmp = data_blob_cmp(&subject_unique_id, &issuer_unique_id); + torture_assert(tctx, + cmp == 0, + "The GUID to identify the public key is not " + "identical"); + + rc = gnutls_x509_crt_get_serial(x509_cert, + reversed.data, + &reversed.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SHORT_MEMORY_BUFFER, + "gnutls_x509_crt_get_serial " + "get buffer size failed"); + + reversed = data_blob_talloc_zero(tctx, + reversed.length); + + rc = gnutls_x509_crt_get_serial(x509_cert, + reversed.data, + &reversed.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_x509_crt_get_serial failed"); + + /* + * Heimdal sometimes adds a leading byte to the data buffer of + * the serial number. So lets uses the subject_unique_id size + * and ignore the leading byte. + */ + serial_number = data_blob_talloc_zero(tctx, + subject_unique_id.length); + + for (i = 0; i < serial_number.length; i++) { + serial_number.data[i] = reversed.data[reversed.length - i - 1]; + } + + cmp = data_blob_cmp(&subject_unique_id, &serial_number); + torture_assert(tctx, + cmp == 0, + "The GUID to identify the public key is not " + "identical"); + + /* Check certificate version */ + version = gnutls_x509_crt_get_version(x509_cert); + torture_assert_int_equal(tctx, + version, + 3, + "Invalid certificate version"); + + /* Get the public key */ + rc = gnutls_pubkey_init(&pubkey); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_pubkey_init failed"); + + rc = gnutls_pubkey_import_x509(pubkey, + x509_cert, + 0); + gnutls_x509_crt_deinit(x509_cert); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_pubkey_import_x509 failed"); + + pubkey_algo = gnutls_pubkey_get_pk_algorithm(pubkey, + &RSA_returned_bits); + gnutls_pubkey_deinit(pubkey); + torture_assert_int_equal(tctx, + pubkey_algo, + GNUTLS_PK_RSA, + "gnutls_pubkey_get_pk_algorithm did " + "not return a RSA key"); + torture_assert_int_equal(tctx, + RSA_returned_bits, + 2048, + "RSA Key doesn't have 2048 bits"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, + "Get GUID"); + } + + return true; +} + +static bool test_ServerWrap_encrypt_decrypt(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_ok(tctx, + r.out.result, + "decrypt"); + decrypted.length = *r.out.data_out_len; + + /* Compare */ + torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_ok(tctx, + r.out.result, + "decrypt"); + decrypted.length = *r.out.data_out_len; + + /* Compare */ + torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed"); + return true; +} + +static bool test_ServerWrap_decrypt_wrong_keyGUID(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum ndr_err_code ndr_err; + struct bkrp_server_side_wrapped server_side_wrapped; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped"); + + /* Change the GUID */ + server_side_wrapped.guid = GUID_random(); + + ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_DATA"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_DATA"); + + return true; +} + +static bool test_ServerWrap_decrypt_empty_request(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t short_request[4] = { 1, 0, 0, 0 }; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARAMETER"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARAMETER"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = NULL; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_INVALID_PARAMETER_MIX, + "decrypt"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = NULL; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_INVALID_PARAMETER_MIX, + "decrypt"); + + return true; +} + + +static bool test_ServerWrap_decrypt_short_request(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t short_request[4] = { 1, 0, 0, 0 }; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 4; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 4; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARAMETER"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 1; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARAMETER"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 1; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "decrypt should fail with WERR_INVALID_PARAMETER"); + + return true; +} + +static bool test_ServerWrap_encrypt_decrypt_manual(struct torture_context *tctx, + struct bkrp_server_side_wrapped *server_side_wrapped, + enum test_wrong wrong) +{ + char *lsa_binding_string = NULL; + struct dcerpc_binding *lsa_binding = NULL; + struct dcerpc_pipe *lsa_p = NULL; + struct dcerpc_binding_handle *lsa_b = NULL; + struct lsa_OpenSecret r_secret; + struct lsa_QuerySecret r_query_secret; + struct policy_handle *handle, sec_handle; + struct bkrp_BackupKey r; + struct GUID preferred_key_guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB preferred_key, preferred_key_clear, session_key, + decrypt_key, decrypt_key_clear, encrypted_blob, + sid_blob; + struct bkrp_dc_serverwrap_key server_key; + struct lsa_DATA_BUF_PTR bufp1; + char *key_guid_string; + struct bkrp_rc4encryptedpayload rc4payload; + struct dom_sid *caller_sid; + uint8_t symkey[20]; /* SHA-1 hash len */ + uint8_t mackey[20]; /* SHA-1 hash len */ + uint8_t mac[20]; /* SHA-1 hash len */ + gnutls_hmac_hd_t hmac_hnd; + gnutls_cipher_hd_t cipher_hnd; + gnutls_datum_t cipher_key; + int rc; + + ZERO_STRUCT(r); + ZERO_STRUCT(r_secret); + ZERO_STRUCT(r_query_secret); + + /* Now read BCKUPKEY_P and prove we can do a matching decrypt and encrypt */ + + /* lsa_OpenSecret only works with ncacn_np and AUTH_LEVEL_NONE */ + lsa_binding_string = talloc_asprintf(tctx, "ncacn_np:%s", + torture_setting_string(tctx, "host", NULL)); + torture_assert(tctx, lsa_binding_string != NULL, "lsa_binding_string"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_parse_binding(tctx, lsa_binding_string, &lsa_binding), + "Failed to parse dcerpc binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &lsa_p, + lsa_binding, &ndr_table_lsarpc, + samba_cmdline_get_creds(), + tctx->ev, tctx->lp_ctx), + "Opening LSA pipe"); + lsa_b = lsa_p->binding_handle; + + torture_assert(tctx, test_lsa_OpenPolicy2(lsa_b, tctx, &handle), "OpenPolicy failed"); + r_secret.in.name.string = "G$BCKUPKEY_P"; + + r_secret.in.handle = handle; + r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_secret.out.sec_handle = &sec_handle; + + torture_comment(tctx, "Testing OpenSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, r_secret.out.result, + "OpenSecret failed"); + + r_query_secret.in.sec_handle = &sec_handle; + r_query_secret.in.new_val = &bufp1; + bufp1.buf = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret), + "QuerySecret failed"); + torture_assert_ntstatus_ok(tctx, r_query_secret.out.result, + "QuerySecret failed"); + + + preferred_key.data = r_query_secret.out.new_val->buf->data; + preferred_key.length = r_query_secret.out.new_val->buf->size; + torture_assert_ntstatus_ok(tctx, dcerpc_fetch_session_key(lsa_p, &session_key), + "dcerpc_fetch_session_key failed"); + + torture_assert_ntstatus_ok(tctx, + sess_decrypt_blob(tctx, + &preferred_key, &session_key, &preferred_key_clear), + "sess_decrypt_blob failed"); + + torture_assert_ntstatus_ok(tctx, GUID_from_ndr_blob(&preferred_key_clear, &preferred_key_guid), + "GUID parse failed"); + + torture_assert_guid_equal(tctx, server_side_wrapped->guid, + preferred_key_guid, + "GUID didn't match value pointed at by G$BCKUPKEY_P"); + + /* And read BCKUPKEY_ and get the actual key */ + + key_guid_string = GUID_string(tctx, &server_side_wrapped->guid); + r_secret.in.name.string = talloc_asprintf(tctx, "G$BCKUPKEY_%s", key_guid_string); + + r_secret.in.handle = handle; + r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_secret.out.sec_handle = &sec_handle; + + torture_comment(tctx, "Testing OpenSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, r_secret.out.result, + "OpenSecret failed"); + + r_query_secret.in.sec_handle = &sec_handle; + r_query_secret.in.new_val = &bufp1; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret), + "QuerySecret failed"); + torture_assert_ntstatus_ok(tctx, r_query_secret.out.result, + "QuerySecret failed"); + + + decrypt_key.data = r_query_secret.out.new_val->buf->data; + decrypt_key.length = r_query_secret.out.new_val->buf->size; + + torture_assert_ntstatus_ok(tctx, + sess_decrypt_blob(tctx, + &decrypt_key, &session_key, &decrypt_key_clear), + "sess_decrypt_blob failed"); + + torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&decrypt_key_clear, tctx, &server_key, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key), + NDR_ERR_SUCCESS, "Failed to parse server_key"); + + torture_assert_int_equal(tctx, server_key.magic, 1, "Failed to correctly decrypt server key"); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + gnutls_hmac_init(&hmac_hnd, + GNUTLS_MAC_SHA1, + server_key.key, + sizeof(server_key.key)); + gnutls_hmac(hmac_hnd, + server_side_wrapped->r2, + sizeof(server_side_wrapped->r2)); + gnutls_hmac_output(hmac_hnd, symkey); + + /* rc4 decrypt sid and secret using sym key */ + cipher_key.data = symkey; + cipher_key.size = sizeof(symkey); + + encrypted_blob = data_blob_talloc(tctx, server_side_wrapped->rc4encryptedpayload, + server_side_wrapped->ciphertext_length); + + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &cipher_key, + NULL); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_cipher_init failed"); + rc = gnutls_cipher_encrypt2(cipher_hnd, + encrypted_blob.data, + encrypted_blob.length, + encrypted_blob.data, + encrypted_blob.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_cipher_encrypt failed"); + gnutls_cipher_deinit(cipher_hnd); + + torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&encrypted_blob, tctx, &rc4payload, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_rc4encryptedpayload), + NDR_ERR_SUCCESS, "Failed to parse rc4encryptedpayload"); + + torture_assert_int_equal(tctx, rc4payload.secret_data.length, + server_side_wrapped->payload_length, + "length of decrypted payload not the length declared in surrounding structure"); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + gnutls_hmac(hmac_hnd, + rc4payload.r3, + sizeof(rc4payload.r3)); + gnutls_hmac_deinit(hmac_hnd, mackey); + + torture_assert_ndr_err_equal(tctx, ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid), + NDR_ERR_SUCCESS, "unable to push SID"); + + gnutls_hmac_init(&hmac_hnd, + GNUTLS_MAC_SHA1, + mackey, + sizeof(mackey)); + /* SID field */ + gnutls_hmac(hmac_hnd, + sid_blob.data, + sid_blob.length); + /* Secret field */ + gnutls_hmac(hmac_hnd, + rc4payload.secret_data.data, + rc4payload.secret_data.length); + gnutls_hmac_output(hmac_hnd, mac); + + torture_assert_mem_equal(tctx, mac, rc4payload.mac, sizeof(mac), "mac not correct"); + torture_assert_int_equal(tctx, rc4payload.secret_data.length, + plaintext.length, "decrypted data is not correct length"); + torture_assert_mem_equal(tctx, rc4payload.secret_data.data, + plaintext.data, plaintext.length, + "decrypted data is not correct"); + + /* Not strictly correct all the time, but good enough for this test */ + caller_sid = get_user_sid(tctx, tctx, + cli_credentials_get_username( + samba_cmdline_get_creds())); + + torture_assert_sid_equal(tctx, &rc4payload.sid, caller_sid, "Secret saved with wrong SID"); + + + /* RE-encrypt */ + + if (wrong == WRONG_SID) { + rc4payload.sid.sub_auths[rc4payload.sid.num_auths - 1] = DOMAIN_RID_KRBTGT; + } + + dump_data_pw("mackey: \n", mackey, sizeof(mackey)); + + torture_assert_ndr_err_equal(tctx, + ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid), + NDR_ERR_SUCCESS, + "push of sid failed"); + + /* SID field */ + gnutls_hmac(hmac_hnd, + sid_blob.data, + sid_blob.length); + /* Secret field */ + gnutls_hmac(hmac_hnd, + rc4payload.secret_data.data, + rc4payload.secret_data.length); + gnutls_hmac_deinit(hmac_hnd, rc4payload.mac); + + dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac)); + + torture_assert_ndr_err_equal(tctx, + ndr_push_struct_blob(&encrypted_blob, tctx, &rc4payload, + (ndr_push_flags_fn_t)ndr_push_bkrp_rc4encryptedpayload), + NDR_ERR_SUCCESS, + "push of rc4payload failed"); + + if (wrong == WRONG_KEY) { + symkey[0] = 78; + symkey[1] = 78; + symkey[2] = 78; + } + + /* rc4 encrypt sid and secret using sym key */ + cipher_key.data = symkey; + cipher_key.size = sizeof(symkey); + + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &cipher_key, + NULL); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_cipher_init failed"); + rc = gnutls_cipher_encrypt2(cipher_hnd, + encrypted_blob.data, + encrypted_blob.length, + encrypted_blob.data, + encrypted_blob.length); + torture_assert_int_equal(tctx, + rc, + GNUTLS_E_SUCCESS, + "gnutls_cipher_encrypt failed"); + gnutls_cipher_deinit(cipher_hnd); + + + /* re-create server wrap structure */ + + torture_assert_int_equal(tctx, encrypted_blob.length, + server_side_wrapped->ciphertext_length, + "expected encrypted length not to change"); + if (wrong == RIGHT_KEY) { + torture_assert_mem_equal(tctx, server_side_wrapped->rc4encryptedpayload, + encrypted_blob.data, + encrypted_blob.length, + "expected encrypted data not to change"); + } + + server_side_wrapped->payload_length = rc4payload.secret_data.length; + server_side_wrapped->ciphertext_length = encrypted_blob.length; + server_side_wrapped->rc4encryptedpayload = encrypted_blob.data; + + return true; +} + + +static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, + struct dcerpc_pipe *p, + enum test_wrong wrong) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum ndr_err_code ndr_err; + struct bkrp_server_side_wrapped server_side_wrapped; + bool repush = false; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped"); + + torture_assert_int_equal(tctx, server_side_wrapped.payload_length, plaintext.length, + "wrong payload length"); + + switch (wrong) { + case WRONG_MAGIC: + /* Change the magic. Forced by our NDR layer, so do it raw */ + SIVAL(encrypted.data, 0, 78); /* valid values are 1-3 */ + break; + case WRONG_R2: + server_side_wrapped.r2[0] = 78; + server_side_wrapped.r2[1] = 78; + server_side_wrapped.r2[2] = 78; + repush = true; + break; + case WRONG_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = UINT32_MAX - 8; + repush = true; + break; + case WRONG_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, UINT32_MAX - 8); /* valid values are 1-3 */ + break; + case SHORT_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = server_side_wrapped.payload_length - 8; + repush = true; + break; + case SHORT_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, server_side_wrapped.ciphertext_length - 8); /* valid values are 1-3 */ + break; + case ZERO_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = 0; + repush = true; + break; + case ZERO_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, 0); /* valid values are 1-3 */ + break; + + case RIGHT_KEY: + case WRONG_KEY: + case WRONG_SID: + torture_assert(tctx, + test_ServerWrap_encrypt_decrypt_manual(tctx, &server_side_wrapped, wrong), + "test_ServerWrap_encrypt_decrypt_manual failed"); + repush = true; + break; + } + + if (repush) { + ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped"); + } + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + + if ((wrong == WRONG_R2 || wrong == WRONG_KEY) + && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_SID, + "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAMETER"); + } else if (wrong == RIGHT_KEY) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_OK, + "decrypt should succeed!"); + } else if (wrong == WRONG_SID) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_ACCESS, + "decrypt should fail with WERR_INVALID_ACCESS"); + } else { + if (!W_ERROR_EQUAL(r.out.result, WERR_INVALID_ACCESS) + && !W_ERROR_EQUAL(r.out.result, WERR_INVALID_PARAMETER)) { + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_ACCESS, WERR_INVALID_PARAMETER or WERR_INVALID_DATA"); + } + } + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + + if ((wrong == WRONG_R2 || wrong == WRONG_KEY) + && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_SID, + "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAMETER"); + } else if (wrong == RIGHT_KEY) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_OK, + "decrypt should succeed!"); + } else if (wrong == WRONG_SID) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_ACCESS, + "decrypt should fail with WERR_INVALID_ACCESS"); + } else { + if (!W_ERROR_EQUAL(r.out.result, WERR_INVALID_ACCESS) + && !W_ERROR_EQUAL(r.out.result, WERR_INVALID_PARAMETER)) { + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_ACCESS, WERR_INVALID_PARAMETER or WERR_INVALID_DATA"); + } + } + + return true; +} + +static bool test_ServerWrap_decrypt_wrong_magic(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_MAGIC); +} + +static bool test_ServerWrap_decrypt_wrong_r2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_R2); +} + +static bool test_ServerWrap_decrypt_wrong_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_short_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_zero_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_wrong_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_CIPHERTEXT_LENGTH); +} + +static bool test_ServerWrap_decrypt_short_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_CIPHERTEXT_LENGTH); +} + +static bool test_ServerWrap_decrypt_zero_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_CIPHERTEXT_LENGTH); +} + +static bool test_ServerWrap_encrypt_decrypt_remote_key(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, RIGHT_KEY); +} + +static bool test_ServerWrap_encrypt_decrypt_wrong_key(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_KEY); +} + +static bool test_ServerWrap_encrypt_decrypt_wrong_sid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_SID); +} + +struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "backupkey"); + + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "backupkey", + &ndr_table_backupkey); + + torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid", + test_RetrieveBackupKeyGUID); + + torture_rpc_tcase_add_test(tcase, "restore_guid", + test_RestoreGUID); + + torture_rpc_tcase_add_test(tcase, "restore_guid version 3", + test_RestoreGUID_v3); + +/* We double the test in order to be sure that we don't mess stuff (ie. freeing static stuff) */ + + torture_rpc_tcase_add_test(tcase, "restore_guid_2nd", + test_RestoreGUID); + + torture_rpc_tcase_add_test(tcase, "unable_to_decrypt_secret", + test_RestoreGUID_ko); + + torture_rpc_tcase_add_test(tcase, "wrong_user_restore_guid", + test_RestoreGUID_wronguser); + + torture_rpc_tcase_add_test(tcase, "wrong_version_restore_guid", + test_RestoreGUID_wrongversion); + + torture_rpc_tcase_add_test(tcase, "bad_magic_on_secret_restore_guid", + test_RestoreGUID_badmagiconsecret); + + torture_rpc_tcase_add_test(tcase, "bad_hash_on_secret_restore_guid", + test_RestoreGUID_badhashaccesscheck); + + torture_rpc_tcase_add_test(tcase, "bad_magic_on_accesscheck_restore_guid", + test_RestoreGUID_badmagicaccesscheck); + + torture_rpc_tcase_add_test(tcase, "bad_cert_guid_restore_guid", + test_RestoreGUID_badcertguid); + + torture_rpc_tcase_add_test(tcase, "empty_request_restore_guid", + test_RestoreGUID_emptyrequest); + + torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid_validate", + test_RetrieveBackupKeyGUID_validate); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt", + test_ServerWrap_encrypt_decrypt); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_keyGUID", + test_ServerWrap_decrypt_wrong_keyGUID); + + torture_rpc_tcase_add_test(tcase, "server_wrap_empty_request", + test_ServerWrap_decrypt_empty_request); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_request", + test_ServerWrap_decrypt_short_request); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_magic", + test_ServerWrap_decrypt_wrong_magic); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_r2", + test_ServerWrap_decrypt_wrong_r2); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_payload_length", + test_ServerWrap_decrypt_wrong_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_payload_length", + test_ServerWrap_decrypt_short_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_payload_length", + test_ServerWrap_decrypt_zero_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_ciphertext_length", + test_ServerWrap_decrypt_wrong_ciphertext_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_ciphertext_length", + test_ServerWrap_decrypt_short_ciphertext_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_ciphertext_length", + test_ServerWrap_decrypt_zero_ciphertext_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_remote_key", + test_ServerWrap_encrypt_decrypt_remote_key); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_key", + test_ServerWrap_encrypt_decrypt_wrong_key); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_sid", + test_ServerWrap_encrypt_decrypt_wrong_sid); + + return suite; +} diff --git a/source4/torture/rpc/bench.c b/source4/torture/rpc/bench.c new file mode 100644 index 0000000..88825c5 --- /dev/null +++ b/source4/torture/rpc/bench.c @@ -0,0 +1,152 @@ +/* + Unix SMB/CIFS implementation. + + simple RPC benchmark + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/rpc/torture_rpc.h" + +/**************************/ +/* srvsvc_NetShare */ +/**************************/ +static bool test_NetShareEnumAll(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct srvsvc_NetShareEnumAll r; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr0 c0; + struct srvsvc_NetShareCtr1 c1; + struct srvsvc_NetShareCtr2 c2; + struct srvsvc_NetShareCtr501 c501; + struct srvsvc_NetShareCtr502 c502; + uint32_t totalentries = 0; + uint32_t levels[] = {0, 1, 2, 501, 502}; + int i; + bool ret = true; + uint32_t resume_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.resume_handle = &resume_handle; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ibinding_handle; + struct policy_handle *handle; + + torture_assert(tctx, + test_lsa_OpenPolicy2(b, tctx, &handle), + "failed to open policy"); + + torture_assert(tctx, + test_lsa_Close(b, tctx, handle), + "failed to close policy"); + + return true; +} + +static bool test_bind(struct torture_context *tctx, + const void *private_data) +{ + struct dcerpc_binding *binding; + struct dcerpc_pipe *p; + NTSTATUS status; + const uint32_t *flags = (const uint32_t *)private_data; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to parse binding string"); + + status = dcerpc_binding_set_flags(binding, *flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p, binding, + &ndr_table_lsarpc, + samba_cmdline_get_creds(), + tctx->ev, + tctx->lp_ctx), + "failed to connect pipe"); + + torture_assert(tctx, + test_openpolicy(tctx, p), + "failed to test openpolicy"); + + talloc_free(p); + + return true; +} + +/** + * Verifies a handle created in a connection is available on + * a second connection when the same association group is + * requested in the bind operation. The LSA interface can't be + * used because it runs in preforking mode in the selftests. + * Association groups should work when binding to interfaces + * running in the same process. + */ +static bool test_assoc_group_handles_external(struct torture_context *tctx, + const void *private_data) +{ + struct dcerpc_binding *binding1 = NULL; + struct dcerpc_binding *binding2 = NULL; + struct dcerpc_pipe *p1 = NULL; + struct dcerpc_pipe *p2 = NULL; + struct epm_Lookup r; + struct epm_LookupHandleFree f; + struct policy_handle handle; + uint32_t assoc_group_id; + uint32_t num_ents = 0; + + ZERO_STRUCT(handle); + + /* Open first pipe and open a policy handle */ + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding1), + "failed to parse binding string"); + dcerpc_binding_set_transport(binding1, NCACN_IP_TCP); + dcerpc_binding_set_string_option(binding1, "endpoint", "135"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p1, binding1, + &ndr_table_epmapper, + samba_cmdline_get_creds(), + tctx->ev, + tctx->lp_ctx), + "failed to connect first pipe"); + + r.in.inquiry_type = RPC_C_EP_ALL_ELTS; + r.in.object = NULL; + r.in.interface_id = NULL; + r.in.vers_option = RPC_C_VERS_ALL; + + r.in.entry_handle = &handle; + r.in.max_ents = 1; + + r.out.entry_handle = &handle; + r.out.num_ents = &num_ents; + + torture_assert_ntstatus_ok(tctx, + dcerpc_epm_Lookup_r(p1->binding_handle, tctx, &r), + "failed EPM Lookup"); + torture_assert_int_equal(tctx, + r.out.result, + EPMAPPER_STATUS_OK, + "failed EPM Lookup"); + + /* Open second pipe, different association group. Handle not found */ + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding2), + "failed to parse binding string"); + dcerpc_binding_set_transport(binding2, NCACN_IP_TCP); + dcerpc_binding_set_string_option(binding2, "endpoint", "135"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, binding2, + &ndr_table_epmapper, + samba_cmdline_get_creds(), + tctx->ev, + tctx->lp_ctx), + "failed to connect second pipe"); + + torture_assert_ntstatus_equal(tctx, + dcerpc_epm_Lookup_r(p2->binding_handle, tctx, &r), + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "Unexpected EPM Lookup success"); + + /* Open second pipe, same association group. Handle is found */ + assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding); + dcerpc_binding_set_assoc_group_id(binding2, assoc_group_id); + + TALLOC_FREE(p2); + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, binding2, + &ndr_table_epmapper, + samba_cmdline_get_creds(), + tctx->ev, + tctx->lp_ctx), + "failed to connect second pipe"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_epm_Lookup_r(p2->binding_handle, tctx, &r), + "failed EPM Lookup"); + + torture_assert_int_equal(tctx, + r.out.result, + EPMAPPER_STATUS_OK, + "failed EPM Lookup"); + + /* Cleanup */ + f.in.entry_handle = &handle; + f.out.entry_handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_epm_LookupHandleFree_r(p1->binding_handle, tctx, &f), + "failed EPM LookupHandleFree"); + + torture_assert_int_equal(tctx, + r.out.result, + EPMAPPER_STATUS_OK, + "failed EPM LookupHandleFree"); + + TALLOC_FREE(p1); + TALLOC_FREE(p2); + TALLOC_FREE(binding2); + TALLOC_FREE(binding1); + + return true; +} + +static void test_bind_op(struct torture_suite *suite, + const char *name, + uint32_t flags) +{ + uint32_t *flags_p = talloc(suite, uint32_t); + + *flags_p = flags; + + torture_suite_add_simple_tcase_const(suite, name, test_bind, flags_p); +} + + +struct torture_suite *torture_rpc_bind(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "bind"); + struct { + const char *test_name; + uint32_t flags; + } tests[] = { + { + .test_name = "ntlm,sign", + .flags = DCERPC_AUTH_NTLM | DCERPC_SIGN + },{ + .test_name = "ntlm,sign,seal", + .flags = DCERPC_AUTH_NTLM | DCERPC_SIGN | DCERPC_SEAL + },{ + .test_name = "spnego,sign", + .flags = DCERPC_AUTH_SPNEGO | DCERPC_SIGN + },{ + .test_name = "spnego,sign,seal", + .flags = DCERPC_AUTH_SPNEGO | DCERPC_SIGN | DCERPC_SEAL + } + }; + int i; + + for (i=0; i < ARRAY_SIZE(tests); i++) { + test_bind_op(suite, tests[i].test_name, tests[i].flags); + } + for (i=0; i < ARRAY_SIZE(tests); i++) { + test_bind_op(suite, talloc_asprintf(suite, "bigendian,%s", tests[i].test_name), tests[i].flags | DCERPC_PUSH_BIGENDIAN); + } + + torture_suite_add_simple_tcase_const(suite, + "assoc_group_handles_external", + test_assoc_group_handles_external, + NULL); + + return suite; +} diff --git a/source4/torture/rpc/browser.c b/source4/torture/rpc/browser.c new file mode 100644 index 0000000..2fbcd4e --- /dev/null +++ b/source4/torture/rpc/browser.c @@ -0,0 +1,124 @@ +/* + Unix SMB/CIFS implementation. + + test suite for browser rpc operations + + Copyright (C) Stefan Metzmacher 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_browser_c.h" +#include "torture/rpc/torture_rpc.h" + +bool test_BrowserrQueryOtherDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct BrowserrQueryOtherDomains r; + struct BrowserrSrvInfo info; + struct BrowserrSrvInfo100Ctr ctr100; + struct srvsvc_NetSrvInfo100 entries100[1]; + struct BrowserrSrvInfo101Ctr ctr101; + struct srvsvc_NetSrvInfo101 entries101[1]; + uint32_t total_entries; + NTSTATUS status; + + torture_comment(tctx, "dcerpc_BrowserrQueryOtherDomains\n"); + + ZERO_STRUCT(r); + ZERO_STRUCT(info); + ZERO_STRUCT(ctr100); + ZERO_STRUCT(entries100); + ZERO_STRUCT(ctr101); + ZERO_STRUCT(entries101); + total_entries = 0; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info = &info; + r.out.info = &info; + r.out.total_entries = &total_entries; + + info.level = 100; + info.info.info100 = &ctr100; + + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_ok(tctx, r.out.result, "BrowserrQueryOtherDomains failed"); + torture_assert_int_equal(tctx, *r.out.total_entries, 0, "BrowserrQueryOtherDomains"); + + info.info.info100 = &ctr100; + ctr100.entries_read = ARRAY_SIZE(entries100); + ctr100.entries = entries100; + + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_ok(tctx, r.out.result, "BrowserrQueryOtherDomains failed"); + torture_assert_int_equal(tctx, *r.out.total_entries, 0, "BrowserrQueryOtherDomains"); + + info.info.info100 = NULL; + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_PARAMETER, r.out.result, + "BrowserrQueryOtherDomains failed"); + + info.level = 101; + info.info.info101 = &ctr101; + + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result, + "BrowserrQueryOtherDomains"); + + info.info.info101 = &ctr101; + ctr101.entries_read = ARRAY_SIZE(entries101); + ctr101.entries = entries101; + + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result, + "BrowserrQueryOtherDomains"); + + info.info.info101 = NULL; + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result, + "BrowserrQueryOtherDomains"); + + info.level = 102; + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result, + "BrowserrQueryOtherDomains"); + + info.level = 0; + status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed"); + torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result, + "BrowserrQueryOtherDomains"); + + return true; +} + +struct torture_suite *torture_rpc_browser(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "browser"); + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, "browser", &ndr_table_browser); + + torture_rpc_tcase_add_test(tcase, "BrowserrQueryOtherDomains", test_BrowserrQueryOtherDomains); + + return suite; +} + diff --git a/source4/torture/rpc/clusapi.c b/source4/torture/rpc/clusapi.c new file mode 100644 index 0000000..183ff54 --- /dev/null +++ b/source4/torture/rpc/clusapi.c @@ -0,0 +1,4185 @@ +/* + Unix SMB/CIFS implementation. + test suite for clusapi rpc operations + + Copyright (C) Günther Deschner 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_clusapi_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "libcli/registry/util_reg.h" + +struct torture_clusapi_context { + struct dcerpc_pipe *p; + const char *NodeName; + const char *ClusterName; + uint16_t lpwMajorVersion; + uint16_t lpwMinorVersion; + uint16_t lpwBuildNumber; +}; + +static bool test_OpenCluster_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenCluster r; + WERROR Status; + + r.out.Status = &Status; + r.out.Cluster = Cluster; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenCluster_r(b, tctx, &r), + "OpenCluster failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenCluster failed"); + + return true; +} + +static bool test_OpenClusterEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenClusterEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.Status = &Status; + r.out.hCluster = Cluster; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenClusterEx_r(b, tctx, &r), + "OpenClusterEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenClusterEx failed"); + + return true; +} + +static bool test_CloseCluster_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseCluster r; + + r.in.Cluster = Cluster; + r.out.Cluster = Cluster; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseCluster_r(b, tctx, &r), + "CloseCluster failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseCluster failed"); + + torture_assert(tctx, + ndr_policy_handle_empty(Cluster), + "policy_handle non empty after CloseCluster"); + + return true; +} + +static bool test_OpenCluster(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + test_CloseCluster_int(tctx, t->p, &Cluster); + + return true; +} + +static bool test_OpenClusterEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + + if (!test_OpenClusterEx_int(tctx, t->p, &Cluster)) { + return false; + } + + test_CloseCluster_int(tctx, t->p, &Cluster); + + return true; +} + +static bool test_CloseCluster(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + return test_CloseCluster_int(tctx, t->p, &Cluster); +} + +static bool test_GetClusterName_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char **ClusterName) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetClusterName r; + const char *NodeName; + + r.out.ClusterName = ClusterName; + r.out.NodeName = &NodeName; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetClusterName_r(b, tctx, &r), + "GetClusterName failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetClusterName failed"); + + return true; +} + +static bool test_SetClusterName(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_SetClusterName r; + const char *NewClusterName; + WERROR rpc_status; + + torture_assert(tctx, + test_GetClusterName_int(tctx, t->p, &NewClusterName), + "failed to query old ClusterName"); + + r.in.NewClusterName = NewClusterName; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_SetClusterName_r(b, tctx, &r), + "SetClusterName failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_RESOURCE_PROPERTIES_STORED, + "SetClusterName failed"); + + return true; +} + +static bool test_GetClusterName(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + const char *ClusterName; + + return test_GetClusterName_int(tctx, t->p, &ClusterName); +} + +static bool test_GetClusterVersion(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_GetClusterVersion r; + uint16_t lpwMajorVersion; + uint16_t lpwMinorVersion; + uint16_t lpwBuildNumber; + const char *lpszVendorId; + const char *lpszCSDVersion; + + r.out.lpwMajorVersion = &lpwMajorVersion; + r.out.lpwMinorVersion = &lpwMinorVersion; + r.out.lpwBuildNumber = &lpwBuildNumber; + r.out.lpszVendorId = &lpszVendorId; + r.out.lpszCSDVersion = &lpszCSDVersion; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetClusterVersion_r(b, tctx, &r), + "GetClusterVersion failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CALL_NOT_IMPLEMENTED, + "GetClusterVersion failed"); + + return true; +} + +static bool test_GetClusterVersion2(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_GetClusterVersion2 r; + uint16_t lpwMajorVersion; + uint16_t lpwMinorVersion; + uint16_t lpwBuildNumber; + const char *lpszVendorId; + const char *lpszCSDVersion; + struct CLUSTER_OPERATIONAL_VERSION_INFO *ppClusterOpVerInfo; + WERROR rpc_status; + + r.out.lpwMajorVersion = &lpwMajorVersion; + r.out.lpwMinorVersion = &lpwMinorVersion; + r.out.lpwBuildNumber = &lpwBuildNumber; + r.out.lpszVendorId = &lpszVendorId; + r.out.lpszCSDVersion = &lpszCSDVersion; + r.out.ppClusterOpVerInfo = &ppClusterOpVerInfo; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetClusterVersion2_r(b, tctx, &r), + "GetClusterVersion2 failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetClusterVersion2 failed"); + + return true; +} + +static bool test_CreateEnum(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType[] = { + CLUSTER_ENUM_NODE, + CLUSTER_ENUM_RESTYPE, + CLUSTER_ENUM_RESOURCE, + CLUSTER_ENUM_GROUP, + CLUSTER_ENUM_NETWORK, + CLUSTER_ENUM_NETINTERFACE, + CLUSTER_ENUM_INTERNAL_NETWORK, + CLUSTER_ENUM_SHARED_VOLUME_RESOURCE + }; + uint32_t dwType_invalid[] = { + 0x00000040, + 0x00000080, + 0x00000100 /* and many more ... */ + }; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + for (i=0; i < ARRAY_SIZE(dwType); i++) { + + r.in.dwType = dwType[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + } + + for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) { + + r.in.dwType = dwType_invalid[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "CreateEnum failed"); + } + + return true; +} + +static bool test_CreateEnumEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CreateEnumEx r; + uint32_t dwType[] = { + CLUSTER_ENUM_NODE, + CLUSTER_ENUM_RESTYPE, + CLUSTER_ENUM_RESOURCE, + CLUSTER_ENUM_GROUP, + CLUSTER_ENUM_NETWORK, + CLUSTER_ENUM_NETINTERFACE, + CLUSTER_ENUM_INTERNAL_NETWORK, + CLUSTER_ENUM_SHARED_VOLUME_RESOURCE + }; + uint32_t dwType_invalid[] = { + 0x00000040, + 0x00000080, + 0x00000100 /* and many more ... */ + }; + struct ENUM_LIST *ReturnIdEnum; + struct ENUM_LIST *ReturnNameEnum; + WERROR rpc_status; + int i; + + for (i=0; i < ARRAY_SIZE(dwType); i++) { + + r.in.hCluster = *Cluster; + r.in.dwType = dwType[i]; + r.in.dwOptions = 0; + r.out.ReturnIdEnum = &ReturnIdEnum; + r.out.ReturnNameEnum = &ReturnNameEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnumEx_r(b, tctx, &r), + "CreateEnumEx failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnumEx failed"); + } + + for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) { + + r.in.hCluster = *Cluster; + r.in.dwType = dwType_invalid[i]; + r.in.dwOptions = 0; + r.out.ReturnIdEnum = &ReturnIdEnum; + r.out.ReturnNameEnum = &ReturnNameEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnumEx_r(b, tctx, &r), + "CreateEnumEx failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "CreateEnumEx failed"); + } + + return true; +} + +static bool test_CreateEnumEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + bool ret; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + ret = test_CreateEnumEx_int(tctx, t->p, &Cluster); + + test_CloseCluster_int(tctx, t->p, &Cluster); + + return ret; +} + + +static bool test_GetQuorumResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_GetQuorumResource r; + const char *lpszResourceName; + const char *lpszDeviceName; + uint32_t pdwMaxQuorumLogSize; + WERROR rpc_status; + + r.out.lpszResourceName = &lpszResourceName; + r.out.lpszDeviceName = &lpszDeviceName; + r.out.pdwMaxQuorumLogSize = &pdwMaxQuorumLogSize; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetQuorumResource_r(b, tctx, &r), + "GetQuorumResource failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetQuorumResource failed"); + + return true; +} + +static bool test_SetQuorumResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_SetQuorumResource r; + const char *lpszDeviceName = ""; + uint32_t dwMaxQuorumLogSize = 0; + WERROR rpc_status; + struct policy_handle hResource; + + /* we need to figure out how this call works and what we provide as + devicename and resource handle - gd + */ + + torture_skip(tctx, "skipping SetQuorumResource test"); + + ZERO_STRUCT(hResource); + + r.in.hResource = hResource; + r.in.lpszDeviceName = lpszDeviceName; + r.in.dwMaxQuorumLogSize = dwMaxQuorumLogSize; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_SetQuorumResource_r(b, tctx, &r), + "SetQuorumResource failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "SetQuorumResource failed"); + + return true; +} + +static bool test_OpenResource_int_exp(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszResourceName, + struct policy_handle *hResource, + WERROR expected_Status, + WERROR expected_rpc_status) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenResource r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszResourceName = lpszResourceName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hResource = hResource; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenResource_r(b, tctx, &r), + "OpenResource failed"); + torture_assert_werr_equal(tctx, + *r.out.Status, expected_Status, + "OpenResource failed"); + torture_assert_werr_equal(tctx, + *r.out.rpc_status, expected_rpc_status, + "OpenResource failed"); + + return true; +} + +bool test_OpenResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszResourceName, + struct policy_handle *hResource) +{ + return test_OpenResource_int_exp(tctx, p, + lpszResourceName, + hResource, + WERR_OK, WERR_OK); +} + +static bool test_OpenResourceEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszResourceName, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenResourceEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + WERROR rpc_status; + + r.in.lpszResourceName = lpszResourceName; + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hResource = hResource; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenResourceEx_r(b, tctx, &r), + "OpenResourceEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenResourceEx failed"); + + return true; +} + +bool test_CloseResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseResource r; + + r.in.Resource = hResource; + r.out.Resource = hResource; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseResource_r(b, tctx, &r), + "CloseResource failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseResource failed"); + torture_assert(tctx, + ndr_policy_handle_empty(hResource), + "policy_handle non empty after CloseResource"); + + return true; +} + +static bool test_OpenResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + test_CloseResource_int(tctx, t->p, &hResource); + + if (!test_OpenResource_int_exp(tctx, t->p, "", &hResource, WERR_RESOURCE_NOT_FOUND, WERR_OK)) { + return false; + } + + torture_assert(tctx, + ndr_policy_handle_empty(&hResource), + "expected empty policy handle"); + + if (!test_OpenResource_int_exp(tctx, t->p, "jfUF38fjSNcfn", &hResource, WERR_RESOURCE_NOT_FOUND, WERR_OK)) { + return false; + } + + torture_assert(tctx, + ndr_policy_handle_empty(&hResource), + "expected empty policy handle"); + + return true; +} + +static bool test_OpenResourceEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + + if (!test_OpenResourceEx_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + test_CloseResource_int(tctx, t->p, &hResource); + + return true; +} + + +static bool test_CloseResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + return test_CloseResource_int(tctx, t->p, &hResource); +} + +static bool test_OpenGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszGroupName, + struct policy_handle *hGroup); +static bool test_CloseGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Group); + +static bool test_CreateResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CreateResource r; + const char *lpszResourceName = "wurst"; + const char *lpszResourceType = "Generic Service"; + WERROR Status; + WERROR rpc_status; + struct policy_handle hGroup; + + torture_assert(tctx, + test_OpenGroup_int(tctx, p, "Cluster Group", &hGroup), + "failed to open group"); + + r.in.hGroup = hGroup; + r.in.lpszResourceName = lpszResourceName; + r.in.lpszResourceType = lpszResourceType; + r.in.dwFlags = CLUSTER_RESOURCE_DEFAULT_MONITOR; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hResource = hResource; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResource_r(b, tctx, &r), + "CreateResource failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "CreateResource failed"); + + test_CloseGroup_int(tctx, p, &hGroup); + + return true; +} + +static bool test_DeleteResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_DeleteResource r; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_DeleteResource_r(b, tctx, &r), + "DeleteResource failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "DeleteResource failed"); + + return true; +} + +static bool test_CreateResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + + if (!test_CreateResource_int(tctx, t->p, &hResource)) { + return false; + } + + test_DeleteResource_int(tctx, t->p, &hResource); + + return true; +} + +static bool test_DeleteResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + + if (!test_CreateResource_int(tctx, t->p, &hResource)) { + return false; + } + + return test_DeleteResource_int(tctx, t->p, &hResource); +} + +static bool test_SetResourceName_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_SetResourceName r; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.in.lpszResourceName = "wurst"; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_SetResourceName_r(b, tctx, &r), + "SetResourceName failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "SetResourceName failed"); + + return true; +} + +static bool test_SetResourceName(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_CreateResource_int(tctx, t->p, &hResource)) { + return false; + } + + ret = test_SetResourceName_int(tctx, t->p, &hResource); + + test_DeleteResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_GetResourceState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceState r; + enum clusapi_ClusterResourceState State; + const char *NodeName; + const char *GroupName; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.State = &State; + r.out.NodeName = &NodeName; + r.out.GroupName = &GroupName; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceState_r(b, tctx, &r), + "GetResourceState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceState failed"); + + return true; +} + +static bool test_GetResourceState(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_GetResourceState_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_GetResourceId_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceId r; + const char *pGuid; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.pGuid = &pGuid; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceId_r(b, tctx, &r), + "GetResourceId failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceId failed"); + + return true; +} + +static bool test_GetResourceId(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_GetResourceId_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_GetResourceType_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceType r; + const char *lpszResourceType; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.lpszResourceType = &lpszResourceType; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceType_r(b, tctx, &r), + "GetResourceType failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceType failed"); + + return true; +} + +static bool test_GetResourceType(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_GetResourceType_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_FailResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_FailResource r; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_FailResource_r(b, tctx, &r), + "FailResource failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "FailResource failed"); + + return true; +} + +static bool test_FailResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_FailResource_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +bool test_OnlineResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OnlineResource r; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OnlineResource_r(b, tctx, &r), + "OnlineResource failed"); + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)) { + torture_result(tctx, TORTURE_FAIL, + "OnlineResource failed with %s", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_OnlineResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_OnlineResource_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +bool test_OfflineResource_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OfflineResource r; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OfflineResource_r(b, tctx, &r), + "OfflineResource failed"); + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)) { + torture_result(tctx, TORTURE_FAIL, + "OfflineResource failed with %s", + win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_OfflineResource(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_OfflineResource_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_CreateResEnum_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CreateResEnum r; + uint32_t dwType = CLUSTER_ENUM_RESOURCE; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResEnum_r(b, tctx, &r), + "CreateResEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateResEnum failed"); + + return true; +} + +static bool test_CreateResEnum(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_CreateResEnum_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_GetResourceDependencyExpression_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceDependencyExpression r; + const char *lpszDependencyExpression; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.lpszDependencyExpression = &lpszDependencyExpression; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceDependencyExpression_r(b, tctx, &r), + "GetResourceDependencyExpression failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceDependencyExpression failed"); + + return true; +} + +static bool test_GetResourceDependencyExpression(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) { + return false; + } + + ret = test_GetResourceDependencyExpression_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_GetResourceNetworkName_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceNetworkName r; + const char *lpszName; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.lpszName = &lpszName; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceNetworkName_r(b, tctx, &r), + "GetResourceNetworkName failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceNetworkName failed"); + + return true; +} + +static bool test_GetResourceNetworkName(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hResource; + bool ret = true; + + if (!test_OpenResource_int(tctx, t->p, "Network Name", &hResource)) { + return false; + } + + ret = test_GetResourceNetworkName_int(tctx, t->p, &hResource); + + test_CloseResource_int(tctx, t->p, &hResource); + + return ret; +} + +static bool test_ResourceTypeControl_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster, + const char *resource_type, + enum clusapi_ResourceTypeControlCode dwControlCode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_ResourceTypeControl r; + uint32_t lpBytesReturned; + uint32_t lpcbRequired; + WERROR rpc_status; + + r.in.hCluster = *Cluster; + r.in.lpszResourceTypeName = resource_type; + r.in.dwControlCode = 0; + r.in.lpInBuffer = NULL; + r.in.nInBufferSize = 0; + r.in.nOutBufferSize = 0; + r.out.lpOutBuffer = NULL; + r.out.lpBytesReturned = &lpBytesReturned; + r.out.lpcbRequired = &lpcbRequired; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r), + "ResourceTypeControl failed"); + + if (strequal(r.in.lpszResourceTypeName, "MSMQ") || + strequal(r.in.lpszResourceTypeName, "MSMQTriggers")) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CLUSTER_RESTYPE_NOT_SUPPORTED, + "ResourceTypeControl failed"); + return true; + } + + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_FUNCTION, + "ResourceTypeControl failed"); + + r.in.dwControlCode = dwControlCode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r), + "ResourceTypeControl failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired); + r.in.nOutBufferSize = *r.out.lpcbRequired; + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r), + "ResourceTypeControl failed"); + } + torture_assert_werr_ok(tctx, + r.out.result, + "ResourceTypeControl failed"); + + /* now try what happens when we query with a buffer large enough to hold + * the entire packet */ + + r.in.nOutBufferSize = 0x4000; + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r), + "ResourceTypeControl failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "ResourceTypeControl failed"); + torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize, + "lpBytesReturned expected to be smaller than input size nOutBufferSize"); + + return true; +} + +static bool test_ResourceTypeControl(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *resourcetype_name) +{ + struct policy_handle Cluster; + bool ret = true; + uint32_t control_codes[] = { + CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO, + CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS, + CLUSCTL_RESOURCE_TYPE_GET_COMMON_PROPERTIES, + CLUSCTL_RESOURCE_TYPE_GET_RO_COMMON_PROPERTIES, + CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_PROPERTIES + }; + int i; + + if (!test_OpenCluster_int(tctx, p, &Cluster)) { + return false; + } + + for (i=0; i < ARRAY_SIZE(control_codes); i++) { + ret = test_ResourceTypeControl_int(tctx, p, &Cluster, + resourcetype_name, + control_codes[i]); + if (!ret) { + goto done; + } + } + + done: + test_CloseCluster_int(tctx, p, &Cluster); + + return ret; +} + + + +static bool test_one_resourcetype(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *resourcetype_name) +{ + torture_assert(tctx, + test_ResourceTypeControl(tctx, p, resourcetype_name), + "failed to query ResourceTypeControl"); + + return true; +} + +static bool test_one_resource(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *resource_name) +{ + struct policy_handle hResource; + + torture_assert(tctx, + test_OpenResource_int(tctx, p, resource_name, &hResource), + "failed to open resource"); + test_CloseResource_int(tctx, p, &hResource); + + torture_assert(tctx, + test_OpenResourceEx_int(tctx, p, resource_name, &hResource), + "failed to openex resource"); + + torture_assert(tctx, + test_GetResourceType_int(tctx, p, &hResource), + "failed to query resource type"); + torture_assert(tctx, + test_GetResourceId_int(tctx, p, &hResource), + "failed to query resource id"); + torture_assert(tctx, + test_GetResourceState_int(tctx, p, &hResource), + "failed to query resource state"); + torture_assert(tctx, + test_CreateResEnum_int(tctx, p, &hResource), + "failed to query resource enum"); + torture_assert(tctx, + test_GetResourceDependencyExpression_int(tctx, p, &hResource), + "failed to query resource dependency expression"); + torture_assert(tctx, + test_GetResourceNetworkName_int(tctx, p, &hResource), + "failed to query resource network name"); + + test_CloseResource_int(tctx, p, &hResource); + + return true; +} + +static bool test_all_resources(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_RESOURCE; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_RESOURCE, "type mismatch"); + + torture_assert(tctx, + test_one_resource(tctx, t->p, e.Name), + "failed to test one resource"); + } + + return true; +} + +static bool test_all_resourcetypes(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_RESTYPE; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_RESTYPE, "type mismatch"); + + torture_assert(tctx, + test_one_resourcetype(tctx, t->p, e.Name), + "failed to test one resourcetype"); + } + + return true; +} + + +static bool test_OpenNode_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNodeName, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNode r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNodeName = lpszNodeName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNode= hNode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNode_r(b, tctx, &r), + "OpenNode failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNode failed"); + + return true; +} + +static bool test_OpenNodeEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNodeName, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNodeEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNodeName = lpszNodeName; + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNode= hNode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNodeEx_r(b, tctx, &r), + "OpenNodeEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNodeEx failed"); + + return true; +} + + +static bool test_CloseNode_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Node) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseNode r; + + r.in.Node = Node; + r.out.Node = Node; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseNode_r(b, tctx, &r), + "CloseNode failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseNode failed"); + torture_assert(tctx, + ndr_policy_handle_empty(Node), + "policy_handle non empty after CloseNode"); + + return true; +} + +static bool test_OpenNode(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + test_CloseNode_int(tctx, t->p, &hNode); + + return true; +} + +static bool test_OpenNodeEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + + if (!test_OpenNodeEx_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + test_CloseNode_int(tctx, t->p, &hNode); + + return true; +} + +static bool test_CloseNode(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + return test_CloseNode_int(tctx, t->p, &hNode); +} + +static bool test_GetNodeState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNodeState r; + enum clusapi_ClusterNodeState State; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.out.State = &State; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNodeState_r(b, tctx, &r), + "GetNodeState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNodeState failed"); + + return true; +} + +static bool test_GetNodeState(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_GetNodeState_int(tctx, t->p, &hNode); + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_GetNodeId_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNodeId r; + const char *pGuid; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.out.pGuid = &pGuid; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNodeId_r(b, tctx, &r), + "GetNodeId failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNodeId failed"); + + return true; +} + +static bool test_GetNodeId(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_GetNodeId_int(tctx, t->p, &hNode); + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_NodeControl_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode, + enum clusapi_NodeControlCode dwControlCode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_NodeControl r; + uint32_t lpBytesReturned; + uint32_t lpcbRequired; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.in.dwControlCode = 0; + r.in.lpInBuffer = NULL; + r.in.nInBufferSize = 0; + r.in.nOutBufferSize = 0; + r.out.lpOutBuffer = NULL; + r.out.lpBytesReturned = &lpBytesReturned; + r.out.lpcbRequired = &lpcbRequired; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_NodeControl_r(b, tctx, &r), + "NodeControl failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_FUNCTION, + "NodeControl failed"); + + r.in.dwControlCode = dwControlCode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_NodeControl_r(b, tctx, &r), + "NodeControl failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired); + r.in.nOutBufferSize = *r.out.lpcbRequired; + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_NodeControl_r(b, tctx, &r), + "NodeControl failed"); + } + torture_assert_werr_ok(tctx, + r.out.result, + "NodeControl failed"); + + /* now try what happens when we query with a buffer large enough to hold + * the entire packet */ + + r.in.nOutBufferSize = 0x4000; + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_NodeControl_r(b, tctx, &r), + "NodeControl failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "NodeControl failed"); + torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize, + "lpBytesReturned expected to be smaller than input size nOutBufferSize"); + + if (dwControlCode == CLUSCTL_NODE_GET_ID) { + const char *str; + DATA_BLOB blob = data_blob_const(r.out.lpOutBuffer, *r.out.lpBytesReturned); + + torture_assert(tctx, *r.out.lpBytesReturned >= 4, "must be at least 4 bytes long"); + torture_assert(tctx, (*r.out.lpBytesReturned % 2) == 0, "must be a multiple of 2"); + + torture_assert(tctx, + pull_reg_sz(tctx, &blob, &str), + "failed to pull unicode string"); + + torture_comment(tctx, "got this node id: '%s'", str); + } + + return true; +} + +static bool test_NodeControl(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_NodeControl_int(tctx, t->p, &hNode, CLUSCTL_NODE_GET_RO_COMMON_PROPERTIES); + if (!ret) { + return false; + } + + ret = test_NodeControl_int(tctx, t->p, &hNode, CLUSCTL_NODE_GET_ID); + if (!ret) { + return false; + } + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_PauseNode_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_PauseNode r; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_PauseNode_r(b, tctx, &r), + "PauseNode failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "PauseNode failed"); + + return true; +} + +static bool test_PauseNode(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_PauseNode_int(tctx, t->p, &hNode); + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_ResumeNode_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_ResumeNode r; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ResumeNode_r(b, tctx, &r), + "ResumeNode failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CLUSTER_NODE_NOT_PAUSED, + "ResumeNode gave unexpected result"); + + return true; +} + +static bool test_ResumeNode(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_ResumeNode_int(tctx, t->p, &hNode); + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_EvictNode_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_EvictNode r; + WERROR rpc_status; + + r.in.hNode = *hNode; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_EvictNode_r(b, tctx, &r), + "EvictNode failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "EvictNode failed"); + + return true; +} + +static bool test_EvictNode(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNode; + bool ret = true; + + if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) { + return false; + } + + ret = test_EvictNode_int(tctx, t->p, &hNode); + + test_CloseNode_int(tctx, t->p, &hNode); + + return ret; +} + +static bool test_one_node(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *node_name) +{ + struct policy_handle hNode; + + torture_assert(tctx, + test_OpenNode_int(tctx, p, node_name, &hNode), + "failed to open node"); + test_CloseNode_int(tctx, p, &hNode); + + torture_assert(tctx, + test_OpenNodeEx_int(tctx, p, node_name, &hNode), + "failed to openex node"); + + torture_assert(tctx, + test_GetNodeId_int(tctx, p, &hNode), + "failed to query node id"); + torture_assert(tctx, + test_GetNodeState_int(tctx, p, &hNode), + "failed to query node id"); + + test_CloseNode_int(tctx, p, &hNode); + + return true; +} + +static bool test_all_nodes(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_NODE; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NODE, "type mismatch"); + + torture_assert(tctx, + test_one_node(tctx, t->p, e.Name), + "failed to test one node"); + } + + return true; +} + +static bool test_OpenGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszGroupName, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenGroup r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszGroupName = lpszGroupName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hGroup= hGroup; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenGroup_r(b, tctx, &r), + "OpenGroup failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenGroup failed"); + + return true; +} + +static bool test_OpenGroupEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszGroupName, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenGroupEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + WERROR rpc_status; + + r.in.lpszGroupName = lpszGroupName; + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hGroup= hGroup; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenGroupEx_r(b, tctx, &r), + "OpenGroupEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenGroupEx failed"); + + return true; +} + +static bool test_CloseGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Group) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseGroup r; + + r.in.Group = Group; + r.out.Group = Group; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseGroup_r(b, tctx, &r), + "CloseGroup failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseGroup failed"); + torture_assert(tctx, + ndr_policy_handle_empty(Group), + "policy_handle non empty after CloseGroup"); + + return true; +} + +static bool test_OpenGroup(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return true; +} + +static bool test_OpenGroupEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + + if (!test_OpenGroupEx_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return true; +} + +static bool test_CloseGroup(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + return test_CloseGroup_int(tctx, t->p, &hGroup); +} + +static bool test_GetGroupState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetGroupState r; + enum clusapi_ClusterGroupState State; + const char *NodeName; + WERROR rpc_status; + + r.in.hGroup = *hGroup; + r.out.State = &State; + r.out.NodeName = &NodeName; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetGroupState_r(b, tctx, &r), + "GetGroupState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetGroupState failed"); + + return true; +} + +static bool test_GetGroupState(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + bool ret = true; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + ret = test_GetGroupState_int(tctx, t->p, &hGroup); + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return ret; +} + +static bool test_GetGroupId_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetGroupId r; + const char *pGuid; + WERROR rpc_status; + + r.in.hGroup = *hGroup; + r.out.pGuid = &pGuid; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetGroupId_r(b, tctx, &r), + "GetGroupId failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetGroupId failed"); + + return true; +} + +static bool test_GetGroupId(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + bool ret = true; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + ret = test_GetGroupId_int(tctx, t->p, &hGroup); + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return ret; +} + +static bool test_GroupControl_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup, + enum clusapi_GroupControlCode dwControlCode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GroupControl r; + uint32_t lpBytesReturned; + uint32_t lpcbRequired; + WERROR rpc_status; + + r.in.hGroup = *hGroup; + r.in.dwControlCode = 0; + r.in.lpInBuffer = NULL; + r.in.nInBufferSize = 0; + r.in.nOutBufferSize = 0; + r.out.lpOutBuffer = NULL; + r.out.lpBytesReturned = &lpBytesReturned; + r.out.lpcbRequired = &lpcbRequired; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GroupControl_r(b, tctx, &r), + "GroupControl failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_FUNCTION, + "GroupControl failed"); + + r.in.dwControlCode = dwControlCode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GroupControl_r(b, tctx, &r), + "GroupControl failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired); + r.in.nOutBufferSize = *r.out.lpcbRequired; + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GroupControl_r(b, tctx, &r), + "GroupControl failed"); + } + torture_assert_werr_ok(tctx, + r.out.result, + "GroupControl failed"); + + /* now try what happens when we query with a buffer large enough to hold + * the entire packet */ + + r.in.nOutBufferSize = 0x400; + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GroupControl_r(b, tctx, &r), + "GroupControl failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GroupControl failed"); + torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize, + "lpBytesReturned expected to be smaller than input size nOutBufferSize"); + + return true; +} + +static bool test_CreateGroupResourceEnum_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CreateGroupResourceEnum r; + uint32_t dwType[] = { + CLUSTER_GROUP_ENUM_CONTAINS, + CLUSTER_GROUP_ENUM_NODES + }; + uint32_t dwType_invalid[] = { + 0x00000040, + 0x00000080, + 0x00000100 /* and many more ... */ + }; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.hGroup = *hGroup; + + for (i=0; i < ARRAY_SIZE(dwType); i++) { + + r.in.hGroup = *hGroup; + r.in.dwType = dwType[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateGroupResourceEnum_r(b, tctx, &r), + "CreateGroupResourceEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateGroupResourceEnum failed"); + } + + for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) { + + r.in.dwType = dwType_invalid[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateGroupResourceEnum_r(b, tctx, &r), + "CreateGroupResourceEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateGroupResourceEnum failed"); + } + + return true; +} + + +static bool test_GroupControl(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + bool ret = true; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_CHARACTERISTICS); + if (!ret) { + return false; + } + + ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_RO_COMMON_PROPERTIES); + if (!ret) { + return false; + } + + ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_FLAGS); + if (!ret) { + return false; + } + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return ret; +} + +static bool test_OnlineGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OnlineGroup r; + WERROR rpc_status; + + r.in.hGroup = *hGroup; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OnlineGroup_r(b, tctx, &r), + "OnlineGroup failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "OnlineGroup failed"); + + return true; +} + +static bool test_OnlineGroup(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + bool ret = true; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + ret = test_OnlineGroup_int(tctx, t->p, &hGroup); + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return ret; +} + +static bool test_OfflineGroup_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hGroup) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OfflineGroup r; + WERROR rpc_status; + + r.in.hGroup = *hGroup; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OfflineGroup_r(b, tctx, &r), + "OfflineGroup failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "OfflineGroup failed"); + + return true; +} + +static bool test_OfflineGroup(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroup; + bool ret = true; + + if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) { + return false; + } + + ret = test_OfflineGroup_int(tctx, t->p, &hGroup); + + test_CloseGroup_int(tctx, t->p, &hGroup); + + return ret; +} + +static bool test_one_group(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *group_name) +{ + struct policy_handle hGroup; + + torture_assert(tctx, + test_OpenGroup_int(tctx, p, group_name, &hGroup), + "failed to open group"); + test_CloseGroup_int(tctx, p, &hGroup); + + torture_assert(tctx, + test_OpenGroupEx_int(tctx, p, group_name, &hGroup), + "failed to openex group"); + + torture_assert(tctx, + test_GetGroupId_int(tctx, p, &hGroup), + "failed to query group id"); + torture_assert(tctx, + test_GetGroupState_int(tctx, p, &hGroup), + "failed to query group id"); + + torture_assert(tctx, + test_GroupControl_int(tctx, p, &hGroup, CLUSCTL_GROUP_GET_FLAGS), + "failed to query group control"); + + torture_assert(tctx, + test_CreateGroupResourceEnum_int(tctx, p, &hGroup), + "failed to query resource enum"); + + test_CloseGroup_int(tctx, p, &hGroup); + + return true; +} + +static bool test_all_groups(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_GROUP; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_GROUP, "type mismatch"); + + torture_assert(tctx, + test_one_group(tctx, t->p, e.Name), + "failed to test one group"); + } + + return true; +} + +static bool test_BackupClusterDatabase(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_BackupClusterDatabase r; + WERROR rpc_status; + + r.in.lpszPathName = "c:\\cluster_backup"; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_BackupClusterDatabase_r(b, tctx, &r), + "BackupClusterDatabase failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CALL_NOT_IMPLEMENTED, + "BackupClusterDatabase failed"); + + return true; +} + +static bool test_SetServiceAccountPassword(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_SetServiceAccountPassword r; + uint32_t SizeReturned; + uint32_t ExpectedBufferSize; + + r.in.lpszNewPassword = "P@ssw0rd!"; + r.in.dwFlags = IDL_CLUSTER_SET_PASSWORD_IGNORE_DOWN_NODES; + r.in.ReturnStatusBufferSize = 1024; + r.out.ReturnStatusBufferPtr = NULL; + r.out.SizeReturned = &SizeReturned; + r.out.ExpectedBufferSize = &ExpectedBufferSize; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_SetServiceAccountPassword_r(b, tctx, &r), + "SetServiceAccountPassword failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CALL_NOT_IMPLEMENTED, + "SetServiceAccountPassword failed"); + + return true; +} + +static bool test_ClusterControl_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster, + enum clusapi_ClusterControlCode dwControlCode) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_ClusterControl r; + uint32_t lpBytesReturned; + uint32_t lpcbRequired; + WERROR rpc_status; + + r.in.hCluster = *Cluster; + r.in.dwControlCode = 0; + r.in.lpInBuffer = NULL; + r.in.nInBufferSize = 0; + r.in.nOutBufferSize = 0; + r.out.lpOutBuffer = NULL; + r.out.lpBytesReturned = &lpBytesReturned; + r.out.lpcbRequired = &lpcbRequired; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ClusterControl_r(b, tctx, &r), + "ClusterControl failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_FUNCTION, + "ClusterControl failed"); + + r.in.dwControlCode = dwControlCode; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ClusterControl_r(b, tctx, &r), + "ClusterControl failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired); + r.in.nOutBufferSize = *r.out.lpcbRequired; + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ClusterControl_r(b, tctx, &r), + "ClusterControl failed"); + } + torture_assert_werr_ok(tctx, + r.out.result, + "ClusterControl failed"); + + /* now try what happens when we query with a buffer large enough to hold + * the entire packet */ + + r.in.nOutBufferSize = 0xffff; + r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_ClusterControl_r(b, tctx, &r), + "ClusterControl failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "ClusterControl failed"); + torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize, + "lpBytesReturned expected to be smaller than input size nOutBufferSize"); + + return true; +} + +static bool test_ClusterControl(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + bool ret = true; + uint32_t control_codes[] = { + CLUSCTL_CLUSTER_GET_COMMON_PROPERTIES, + CLUSCTL_CLUSTER_GET_RO_COMMON_PROPERTIES, + CLUSCTL_CLUSTER_GET_FQDN, + CLUSCTL_CLUSTER_GET_PRIVATE_PROPERTIES, + CLUSCTL_CLUSTER_CHECK_VOTER_DOWN + }; + int i; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + for (i=0; i < ARRAY_SIZE(control_codes); i++) { + ret = test_ClusterControl_int(tctx, t->p, &Cluster, + control_codes[i]); + if (!ret) { + goto done; + } + } + + done: + test_CloseCluster_int(tctx, t->p, &Cluster); + + return ret; +} + +static bool test_CreateResTypeEnum(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateResTypeEnum r; + uint32_t dwType[] = { + CLUSTER_RESOURCE_TYPE_ENUM_NODES, + CLUSTER_RESOURCE_TYPE_ENUM_RESOURCES + }; + uint32_t dwType_invalid[] = { + 0x00000040, + 0x00000080, + 0x00000100 /* and many more ... */ + }; + const char *valid_names[] = { + "Physical Disk", + "Storage Pool" + }; + const char *invalid_names[] = { + "INVALID_TYPE_XXXX" + }; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i, s; + + for (s = 0; s < ARRAY_SIZE(valid_names); s++) { + + r.in.lpszTypeName = valid_names[s]; + + for (i=0; i < ARRAY_SIZE(dwType); i++) { + + r.in.dwType = dwType[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r), + "CreateResTypeEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateResTypeEnum failed"); + } + + for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) { + + r.in.dwType = dwType_invalid[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r), + "CreateResTypeEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateResTypeEnum failed"); + } + } + + for (s = 0; s < ARRAY_SIZE(invalid_names); s++) { + + r.in.lpszTypeName = invalid_names[s]; + + for (i=0; i < ARRAY_SIZE(dwType); i++) { + + r.in.dwType = dwType[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r), + "CreateResTypeEnum failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CLUSTER_RESOURCE_TYPE_NOT_FOUND, + "CreateResTypeEnum failed"); + } + + for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) { + + r.in.dwType = dwType_invalid[i]; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r), + "CreateResTypeEnum failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_CLUSTER_RESOURCE_TYPE_NOT_FOUND, + "CreateResTypeEnum failed"); + } + } + + + return true; +} + +static bool test_CreateGroupEnum_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Cluster, + const char **multi_sz, + const char **multi_sz_ro) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CreateGroupEnum r; + struct GROUP_ENUM_LIST *pResultList; + WERROR rpc_status; + DATA_BLOB blob = data_blob_null; + DATA_BLOB blob_ro = data_blob_null; + + r.in.hCluster = *Cluster; + r.in.pProperties = blob.data; + r.in.cbProperties = blob.length; + r.in.pRoProperties = blob_ro.data; + r.in.cbRoProperties = blob_ro.length; + r.out.ppResultList = &pResultList; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateGroupEnum_r(b, tctx, &r), + "CreateGroupEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateGroupEnum failed"); + + if (!push_reg_multi_sz(tctx, &blob, multi_sz)) { + return false; + } + + if (!push_reg_multi_sz(tctx, &blob_ro, multi_sz_ro)) { + return false; + } + + r.in.pProperties = blob.data; + r.in.cbProperties = blob.length; + + r.in.pRoProperties = blob_ro.data; + r.in.cbRoProperties = blob_ro.length; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateGroupEnum_r(b, tctx, &r), + "CreateGroupEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateGroupEnum failed"); + +#if 0 + { + int i; + enum ndr_err_code ndr_err; + + for (i=0; i < pResultList->EntryCount; i++) { + struct clusapi_PROPERTY_LIST list; + torture_comment(tctx, "entry #%d\n", i); + + blob = data_blob_const(pResultList->Entry[i].Properties, + pResultList->Entry[i].cbProperties); + + ndr_err = ndr_pull_struct_blob(&blob, tctx, &list, + (ndr_pull_flags_fn_t)ndr_pull_clusapi_PROPERTY_LIST); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NDR_PRINT_DEBUG(clusapi_PROPERTY_LIST, &list); + } + + blob_ro = data_blob_const(pResultList->Entry[i].RoProperties, + pResultList->Entry[i].cbRoProperties); + + ndr_err = ndr_pull_struct_blob(&blob_ro, tctx, &list, + (ndr_pull_flags_fn_t)ndr_pull_clusapi_PROPERTY_LIST); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NDR_PRINT_DEBUG(clusapi_PROPERTY_LIST, &list); + } + } + } +#endif + + return true; +} + +static bool test_CreateGroupEnum(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle Cluster; + bool ret; + const char *multi_sz[] = { + "Priority", NULL, + }; + const char *multi_sz_ro[] = { + "GroupType", NULL, + }; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + ret = test_CreateGroupEnum_int(tctx, t->p, &Cluster, + multi_sz, multi_sz_ro); + if (!ret) { + goto done; + } + + done: + test_CloseCluster_int(tctx, t->p, &Cluster); + + return ret; +} + +static bool test_OpenNetwork_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNetworkName, + struct policy_handle *hNetwork) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNetwork r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNetworkName = lpszNetworkName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNetwork = hNetwork ; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNetwork_r(b, tctx, &r), + "OpenNetwork failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNetwork failed"); + + return true; +} + +static bool test_OpenNetworkEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNetworkName, + struct policy_handle *hNetwork) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNetworkEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNetworkName = lpszNetworkName; + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNetwork = hNetwork ; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNetworkEx_r(b, tctx, &r), + "OpenNetworkEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNetworkEx failed"); + + return true; +} + +static bool test_CloseNetwork_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *Network) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseNetwork r; + + r.in.Network = Network; + r.out.Network = Network; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseNetwork_r(b, tctx, &r), + "CloseNetwork failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseNetwork failed"); + torture_assert(tctx, + ndr_policy_handle_empty(Network), + "policy_handle non empty after CloseNetwork"); + + return true; +} + +static bool test_OpenNetwork(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetwork; + + if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) { + return false; + } + + test_CloseNetwork_int(tctx, t->p, &hNetwork); + + return true; +} + +static bool test_OpenNetworkEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetwork; + + if (!test_OpenNetworkEx_int(tctx, t->p, "Cluster Network 1", &hNetwork)) { + return false; + } + + test_CloseNetwork_int(tctx, t->p, &hNetwork); + + return true; +} + +static bool test_CloseNetwork(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetwork; + + if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) { + return false; + } + + return test_CloseNetwork_int(tctx, t->p, &hNetwork); +} + +static bool test_GetNetworkState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNetwork) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNetworkState r; + enum clusapi_ClusterNetworkState State; + WERROR rpc_status; + + r.in.hNetwork = *hNetwork; + r.out.State = &State; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNetworkState_r(b, tctx, &r), + "GetNetworkState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNetworkState failed"); + + return true; +} + +static bool test_GetNetworkState(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetwork; + bool ret = true; + + if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) { + return false; + } + + ret = test_GetNetworkState_int(tctx, t->p, &hNetwork); + + test_CloseNetwork_int(tctx, t->p, &hNetwork); + + return ret; +} + +static bool test_GetNetworkId_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNetwork) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNetworkId r; + const char *pGuid; + WERROR rpc_status; + + r.in.hNetwork = *hNetwork; + r.out.pGuid = &pGuid; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNetworkId_r(b, tctx, &r), + "GetNetworkId failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNetworkId failed"); + + return true; +} + +static bool test_GetNetworkId(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetwork; + bool ret = true; + + if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) { + return false; + } + + ret = test_GetNetworkId_int(tctx, t->p, &hNetwork); + + test_CloseNetwork_int(tctx, t->p, &hNetwork); + + return ret; +} + +static bool test_one_network(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *network_name) +{ + struct policy_handle hNetwork; + + torture_assert(tctx, + test_OpenNetwork_int(tctx, p, network_name, &hNetwork), + "failed to open network"); + test_CloseNetwork_int(tctx, p, &hNetwork); + + torture_assert(tctx, + test_OpenNetworkEx_int(tctx, p, network_name, &hNetwork), + "failed to openex network"); + + torture_assert(tctx, + test_GetNetworkId_int(tctx, p, &hNetwork), + "failed to query network id"); + torture_assert(tctx, + test_GetNetworkState_int(tctx, p, &hNetwork), + "failed to query network id"); + + test_CloseNetwork_int(tctx, p, &hNetwork); + + return true; +} + +static bool test_all_networks(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_NETWORK; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NETWORK, "type mismatch"); + + torture_assert(tctx, + test_one_network(tctx, t->p, e.Name), + "failed to test one network"); + } + + return true; +} + +static bool test_OpenNetInterface_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNetInterfaceName, + struct policy_handle *hNetInterface) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNetInterface r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNetInterfaceName = lpszNetInterfaceName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNetInterface = hNetInterface; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNetInterface_r(b, tctx, &r), + "OpenNetInterface failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNetInterface failed"); + + return true; +} + +static bool test_OpenNetInterfaceEx_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszNetInterfaceName, + struct policy_handle *hNetInterface) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenNetInterfaceEx r; + uint32_t lpdwGrantedAccess; + WERROR Status; + WERROR rpc_status; + + r.in.lpszNetInterfaceName = lpszNetInterfaceName; + r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.lpdwGrantedAccess = &lpdwGrantedAccess; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hNetInterface = hNetInterface; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenNetInterfaceEx_r(b, tctx, &r), + "OpenNetInterfaceEx failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenNetInterfaceEx failed"); + + return true; +} + +static bool test_CloseNetInterface_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *NetInterface) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseNetInterface r; + + r.in.NetInterface = NetInterface; + r.out.NetInterface = NetInterface; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseNetInterface_r(b, tctx, &r), + "CloseNetInterface failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseNetInterface failed"); + torture_assert(tctx, + ndr_policy_handle_empty(NetInterface), + "policy_handle non empty after CloseNetInterface"); + + return true; +} + +static bool test_OpenNetInterface(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetInterface; + + if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) { + return false; + } + + test_CloseNetInterface_int(tctx, t->p, &hNetInterface); + + return true; +} + +static bool test_OpenNetInterfaceEx(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetInterface; + + if (!test_OpenNetInterfaceEx_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) { + return false; + } + + test_CloseNetInterface_int(tctx, t->p, &hNetInterface); + + return true; +} + +static bool test_CloseNetInterface(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetInterface; + + if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) { + return false; + } + + return test_CloseNetInterface_int(tctx, t->p, &hNetInterface); +} + +static bool test_GetNetInterfaceState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNetInterface) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNetInterfaceState r; + enum clusapi_ClusterNetInterfaceState State; + WERROR rpc_status; + + r.in.hNetInterface = *hNetInterface; + r.out.State = &State; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNetInterfaceState_r(b, tctx, &r), + "GetNetInterfaceState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNetInterfaceState failed"); + + return true; +} + +static bool test_GetNetInterfaceState(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetInterface; + bool ret = true; + + if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) { + return false; + } + + ret = test_GetNetInterfaceState_int(tctx, t->p, &hNetInterface); + + test_CloseNetInterface_int(tctx, t->p, &hNetInterface); + + return ret; +} + +static bool test_GetNetInterfaceId_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hNetInterface) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetNetInterfaceId r; + const char *pGuid; + WERROR rpc_status; + + r.in.hNetInterface = *hNetInterface; + r.out.pGuid = &pGuid; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetNetInterfaceId_r(b, tctx, &r), + "GetNetInterfaceId failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetNetInterfaceId failed"); + + return true; +} + +static bool test_GetNetInterfaceId(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hNetInterface; + bool ret = true; + + if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) { + return false; + } + + ret = test_GetNetInterfaceId_int(tctx, t->p, &hNetInterface); + + test_CloseNetInterface_int(tctx, t->p, &hNetInterface); + + return ret; +} + +static bool test_one_netinterface(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *netinterface_name) +{ + struct policy_handle hNetInterface; + + torture_assert(tctx, + test_OpenNetInterface_int(tctx, p, netinterface_name, &hNetInterface), + "failed to open netinterface"); + test_CloseNetInterface_int(tctx, p, &hNetInterface); + + torture_assert(tctx, + test_OpenNetInterfaceEx_int(tctx, p, netinterface_name, &hNetInterface), + "failed to openex netinterface"); + + torture_assert(tctx, + test_GetNetInterfaceId_int(tctx, p, &hNetInterface), + "failed to query netinterface id"); + torture_assert(tctx, + test_GetNetInterfaceState_int(tctx, p, &hNetInterface), + "failed to query netinterface id"); + + test_CloseNetInterface_int(tctx, p, &hNetInterface); + + return true; +} + +static bool test_all_netinterfaces(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateEnum r; + uint32_t dwType = CLUSTER_ENUM_NETINTERFACE; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + int i; + + r.in.dwType = dwType; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "CreateEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateEnum failed"); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NETINTERFACE, "type mismatch"); + + torture_assert(tctx, + test_one_netinterface(tctx, t->p, e.Name), + "failed to test one netinterface"); + } + + return true; +} + +static bool test_CloseKey_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *pKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseKey r; + + r.in.pKey = pKey; + r.out.pKey = pKey; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseKey_r(b, tctx, &r), + "CloseKey failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseKey failed"); + torture_assert(tctx, + ndr_policy_handle_empty(pKey), + "policy_handle non empty after CloseKey"); + + return true; +} + +static bool test_GetRootKey_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *phKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetRootKey r; + WERROR Status; + WERROR rpc_status; + + r.in.samDesired = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.Status = &Status; + r.out.rpc_status = &rpc_status; + r.out.phKey = phKey; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetRootKey_r(b, tctx, &r), + "GetRootKey failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "GetRootKey failed"); + + return true; +} + +static bool test_EnumKey_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_EnumKey r; + const char *KeyName; + NTTIME lpftLastWriteTime; + WERROR rpc_status; + + r.in.hKey = *hKey; + r.in.dwIndex = 0; + r.out.KeyName = &KeyName; + r.out.lpftLastWriteTime = &lpftLastWriteTime; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_EnumKey_r(b, tctx, &r), + "EnumKey failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "EnumKey failed"); + + return true; +} + +static bool test_OpenKey_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey, + const char *lpSubKey, + struct policy_handle *phKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenKey r; + WERROR Status; + WERROR rpc_status; + + r.in.hKey = *hKey; + r.in.lpSubKey = lpSubKey; + r.in.samDesired = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.Status = &Status; + r.out.rpc_status = &rpc_status; + r.out.phKey = phKey; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenKey_r(b, tctx, &r), + "OpenKey failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenKey failed"); + + return true; +} + +static bool test_EnumValue_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_EnumValue r; + const char *lpValueName; + uint32_t lpType; + uint32_t TotalSize; + WERROR rpc_status; + int i = 0; + + do { + uint32_t lpcbData = 2048; + + r.in.hKey = *hKey; + r.in.dwIndex = i++; + r.in.lpcbData = &lpcbData; + r.out.lpValueName = &lpValueName; + r.out.lpType = &lpType; + r.out.lpData = talloc_array(tctx, uint8_t, lpcbData); + r.out.TotalSize = &TotalSize; + r.out.rpc_status = &rpc_status; + r.out.lpcbData = &lpcbData; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_EnumValue_r(b, tctx, &r), + "EnumValue failed"); + + } while (W_ERROR_IS_OK(r.out.result)); + + torture_assert_werr_equal(tctx, + r.out.result, + WERR_NO_MORE_ITEMS, + "EnumValue failed"); + + return true; +} + +static bool test_QueryInfoKey_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_QueryInfoKey r; + uint32_t lpcSubKeys; + uint32_t lpcbMaxSubKeyLen; + uint32_t lpcValues; + uint32_t lpcbMaxValueNameLen; + uint32_t lpcbMaxValueLen; + uint32_t lpcbSecurityDescriptor; + NTTIME lpftLastWriteTime; + WERROR rpc_status; + + r.in.hKey = *hKey; + r.out.lpcSubKeys = &lpcSubKeys; + r.out.lpcbMaxSubKeyLen = &lpcbMaxSubKeyLen; + r.out.lpcValues = &lpcValues; + r.out.lpcbMaxValueNameLen = &lpcbMaxValueNameLen; + r.out.lpcbMaxValueLen = &lpcbMaxValueLen; + r.out.lpcbSecurityDescriptor = &lpcbSecurityDescriptor; + r.out.lpftLastWriteTime = &lpftLastWriteTime; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_QueryInfoKey_r(b, tctx, &r), + "QueryInfoKey failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "QueryInfoKey failed"); + + return true; +} + +static bool test_GetKeySecurity_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetKeySecurity r; + uint32_t SecurityInformation = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + struct RPC_SECURITY_DESCRIPTOR pRpcSecurityDescriptor; + WERROR rpc_status; + + ZERO_STRUCT(pRpcSecurityDescriptor); + + r.in.hKey = *hKey; + r.in.SecurityInformation = SecurityInformation; + r.in.pRpcSecurityDescriptor = &pRpcSecurityDescriptor; + r.out.rpc_status = &rpc_status; + r.out.pRpcSecurityDescriptor = &pRpcSecurityDescriptor; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetKeySecurity_r(b, tctx, &r), + "GetKeySecurity failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + pRpcSecurityDescriptor.lpSecurityDescriptor = talloc_array(tctx, + uint8_t, pRpcSecurityDescriptor.cbInSecurityDescriptor); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetKeySecurity_r(b, tctx, &r), + "GetKeySecurity failed"); + } + + torture_assert_werr_ok(tctx, + r.out.result, + "GetKeySecurity failed"); + + return true; +} + +static bool test_GetRootKey(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hKey; + + if (!test_GetRootKey_int(tctx, t->p, &hKey)) { + return false; + } + + test_CloseKey_int(tctx, t->p, &hKey); + + return true; +} + +static bool test_CloseKey(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hKey; + + if (!test_GetRootKey_int(tctx, t->p, &hKey)) { + return false; + } + + return test_CloseKey_int(tctx, t->p, &hKey); +} + +static bool test_EnumKey(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hKey; + bool ret = true; + + if (!test_GetRootKey_int(tctx, t->p, &hKey)) { + return false; + } + + ret = test_EnumKey_int(tctx, t->p, &hKey); + + test_CloseKey_int(tctx, t->p, &hKey); + + return ret; +} + +static bool test_QueryValue_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey, + const char *ValueName) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_QueryValue r; + uint32_t lpValueType; + uint32_t lpcbRequired; + WERROR rpc_status; + + r.in.hKey = *hKey; + r.in.lpValueName = ValueName; + r.in.cbData = 0; + r.out.lpValueType = &lpValueType; + r.out.lpData = NULL; + r.out.lpcbRequired = &lpcbRequired; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + + r.in.cbData = lpcbRequired; + r.out.lpData = talloc_zero_array(tctx, uint8_t, r.in.cbData); + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + } + + torture_assert_werr_ok(tctx, + r.out.result, + "QueryValue failed"); + + if (lpValueType == REG_SZ) { + const char *s; + DATA_BLOB blob = data_blob_const(r.out.lpData, lpcbRequired); + pull_reg_sz(tctx, &blob, &s); + torture_comment(tctx, "got: %s\n", s); + } + + return true; +} + +static bool test_QueryValue(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hKey; + bool ret = true; + + if (!test_GetRootKey_int(tctx, t->p, &hKey)) { + return false; + } + + ret = test_QueryValue_int(tctx, t->p, &hKey, "ClusterInstanceID"); + + test_CloseKey_int(tctx, t->p, &hKey); + + return ret; +} + + +static bool test_one_key(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hKey, + const char *KeyName) +{ + struct policy_handle phKey; + + torture_assert(tctx, + test_OpenKey_int(tctx, p, hKey, KeyName, &phKey), + "failed to open key"); + + torture_assert(tctx, + test_QueryInfoKey_int(tctx, p, &phKey), + "failed to enum values"); + torture_assert(tctx, + test_GetKeySecurity_int(tctx, p, &phKey), + "failed to get key security"); + + torture_assert(tctx, + test_EnumValue_int(tctx, p, &phKey), + "failed to enum values"); + + torture_assert(tctx, + test_CloseKey_int(tctx, p, &phKey), + "failed to close key"); + + return true; +} + +static bool test_all_keys(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct policy_handle hKey; + struct clusapi_EnumKey r; + const char *KeyName; + NTTIME lpftLastWriteTime; + WERROR rpc_status; + int i = 0; + + if (!test_GetRootKey_int(tctx, t->p, &hKey)) { + return false; + } + + do { + r.in.hKey = hKey; + r.in.dwIndex = i++; + r.out.KeyName = &KeyName; + r.out.lpftLastWriteTime = &lpftLastWriteTime; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_EnumKey_r(b, tctx, &r), + "EnumKey failed"); + + if (W_ERROR_IS_OK(r.out.result)) { + torture_assert(tctx, + test_one_key(tctx, t->p, &hKey, KeyName), + "failed to test one key"); + } + + } while (W_ERROR_IS_OK(r.out.result)); + + torture_assert_werr_equal(tctx, + r.out.result, + WERR_NO_MORE_ITEMS, + "EnumKey failed"); + + test_CloseKey_int(tctx, t->p, &hKey); + + return true; +} + +static bool test_OpenGroupSet_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *lpszGroupSetName, + struct policy_handle *hGroupSet) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_OpenGroupSet r; + WERROR Status; + WERROR rpc_status; + + r.in.lpszGroupSetName = lpszGroupSetName; + r.out.rpc_status = &rpc_status; + r.out.Status = &Status; + r.out.hGroupSet = hGroupSet; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_OpenGroupSet_r(b, tctx, &r), + "OpenGroupSet failed"); + torture_assert_werr_ok(tctx, + *r.out.Status, + "OpenGroupSet failed"); + + return true; +} + +static bool test_CloseGroupSet_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *GroupSet) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_CloseGroupSet r; + + r.in.GroupSet = GroupSet; + r.out.GroupSet = GroupSet; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CloseGroupSet_r(b, tctx, &r), + "CloseGroupSet failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CloseGroupSet failed"); + torture_assert(tctx, + ndr_policy_handle_empty(GroupSet), + "policy_handle non empty after CloseGroupSet"); + + return true; +} + +static bool test_OpenGroupSet(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroupSet; + + if (t->lpwMajorVersion < 0x000a) { + torture_skip(tctx, "GroupSet fn not available on old clusters"); + return true; + } + + if (!test_OpenGroupSet_int(tctx, t->p, "Cluster Group", &hGroupSet)) { + return false; + } + + test_CloseGroupSet_int(tctx, t->p, &hGroupSet); + + return true; +} + +static bool test_CloseGroupSet(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct policy_handle hGroupSet; + + if (t->lpwMajorVersion < 0x000a) { + torture_skip(tctx, "GroupSet fn not available on old clusters"); + return true; + } + + if (!test_OpenGroupSet_int(tctx, t->p, "Cluster Group", &hGroupSet)) { + return false; + } + + return test_CloseGroupSet_int(tctx, t->p, &hGroupSet); +} + +static bool test_one_groupset(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *groupset_name) +{ + struct policy_handle hGroupSet; + + torture_assert(tctx, + test_OpenGroupSet_int(tctx, p, groupset_name, &hGroupSet), + "failed to open groupset"); + + test_CloseGroupSet_int(tctx, p, &hGroupSet); + + return true; +} + +static bool test_all_groupsets(struct torture_context *tctx, + void *data) +{ + struct torture_clusapi_context *t = + talloc_get_type_abort(data, struct torture_clusapi_context); + struct dcerpc_binding_handle *b = t->p->binding_handle; + struct clusapi_CreateGroupSetEnum r; + struct ENUM_LIST *ReturnEnum; + struct policy_handle Cluster; + WERROR rpc_status; + int i; + + if (!test_OpenCluster_int(tctx, t->p, &Cluster)) { + return false; + } + + r.in.hCluster = Cluster; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateGroupSetEnum_r(b, tctx, &r), + "CreateGroupSetEnum failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "CreateGroupSetEnum failed"); + + test_CloseCluster_int(tctx, t->p, &Cluster); + + for (i=0; i < ReturnEnum->EntryCount; i++) { + + struct ENUM_ENTRY e = ReturnEnum->Entry[i]; + + torture_assert(tctx, + test_one_groupset(tctx, t->p, e.Name), + "failed to test one groupset"); + } + + return true; +} + +static bool torture_rpc_clusapi_setup_common(struct torture_context *tctx, + struct torture_clusapi_context *t) +{ + struct dcerpc_binding_handle *b; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &t->p, &ndr_table_clusapi), + "Error connecting to server"); + + b = t->p->binding_handle; + + { + struct clusapi_GetClusterName r; + + r.out.ClusterName = &t->ClusterName; + r.out.NodeName = &t->NodeName; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetClusterName_r(b, tctx, &r), + "GetClusterName failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetClusterName failed"); + } + { + struct clusapi_GetClusterVersion2 r; + const char *lpszVendorId; + const char *lpszCSDVersion; + struct CLUSTER_OPERATIONAL_VERSION_INFO *ppClusterOpVerInfo; + WERROR rpc_status; + + r.out.lpwMajorVersion = &t->lpwMajorVersion; + r.out.lpwMinorVersion = &t->lpwMinorVersion; + r.out.lpwBuildNumber = &t->lpwBuildNumber; + r.out.lpszVendorId = &lpszVendorId; + r.out.lpszCSDVersion = &lpszCSDVersion; + r.out.ppClusterOpVerInfo = &ppClusterOpVerInfo; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetClusterVersion2_r(b, tctx, &r), + "GetClusterVersion2 failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetClusterVersion2 failed"); + } + + return true; +} + +static bool torture_rpc_clusapi_setup(struct torture_context *tctx, + void **data) +{ + struct torture_clusapi_context *t; + + *data = t = talloc_zero(tctx, struct torture_clusapi_context); + + return torture_rpc_clusapi_setup_common(tctx, t); +} + +static bool torture_rpc_clusapi_teardown(struct torture_context *tctx, + void *data) +{ + talloc_free(data); + + return true; +} + +void torture_tcase_cluster(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "OpenCluster", + test_OpenCluster); + torture_tcase_add_simple_test(tcase, "OpenClusterEx", + test_OpenClusterEx); + torture_tcase_add_simple_test(tcase, "CloseCluster", + test_CloseCluster); + torture_tcase_add_simple_test(tcase, "SetClusterName", + test_SetClusterName); + torture_tcase_add_simple_test(tcase, "GetClusterName", + test_GetClusterName); + torture_tcase_add_simple_test(tcase, "GetClusterVersion", + test_GetClusterVersion); + torture_tcase_add_simple_test(tcase, "CreateEnum", + test_CreateEnum); + torture_tcase_add_simple_test(tcase, "CreateEnumEx", + test_CreateEnumEx); + torture_tcase_add_simple_test(tcase, "GetClusterVersion2", + test_GetClusterVersion2); + torture_tcase_add_simple_test(tcase, "BackupClusterDatabase", + test_BackupClusterDatabase); + torture_tcase_add_simple_test(tcase, "SetServiceAccountPassword", + test_SetServiceAccountPassword); + torture_tcase_add_simple_test(tcase, "ClusterControl", + test_ClusterControl); + torture_tcase_add_simple_test(tcase, "CreateResTypeEnum", + test_CreateResTypeEnum); + torture_tcase_add_simple_test(tcase, "CreateGroupEnum", + test_CreateGroupEnum); + +} + +void torture_tcase_resource(struct torture_tcase *tcase) +{ + struct torture_test *test; + + torture_tcase_add_simple_test(tcase, "GetQuorumResource", + test_GetQuorumResource); + torture_tcase_add_simple_test(tcase, "SetQuorumResource", + test_SetQuorumResource); + torture_tcase_add_simple_test(tcase, "OpenResource", + test_OpenResource); + torture_tcase_add_simple_test(tcase, "OpenResourceEx", + test_OpenResourceEx); + torture_tcase_add_simple_test(tcase, "CloseResource", + test_CloseResource); + torture_tcase_add_simple_test(tcase, "CreateResource", + test_CreateResource); + torture_tcase_add_simple_test(tcase, "DeleteResource", + test_DeleteResource); + torture_tcase_add_simple_test(tcase, "SetResourceName", + test_SetResourceName); + torture_tcase_add_simple_test(tcase, "GetResourceState", + test_GetResourceState); + torture_tcase_add_simple_test(tcase, "GetResourceId", + test_GetResourceId); + torture_tcase_add_simple_test(tcase, "GetResourceType", + test_GetResourceType); + torture_tcase_add_simple_test(tcase, "CreateResEnum", + test_CreateResEnum); + test = torture_tcase_add_simple_test(tcase, "FailResource", + test_FailResource); + test->dangerous = true; + torture_tcase_add_simple_test(tcase, "OnlineResource", + test_OnlineResource); + test = torture_tcase_add_simple_test(tcase, "OfflineResource", + test_OfflineResource); + test->dangerous = true; + torture_tcase_add_simple_test(tcase, "GetResourceDependencyExpression", + test_GetResourceDependencyExpression); + torture_tcase_add_simple_test(tcase, "GetResourceNetworkName", + test_GetResourceNetworkName); + torture_tcase_add_simple_test(tcase, "all_resources", + test_all_resources); +} + +void torture_tcase_resourcetype(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "all_resourcetypes", + test_all_resourcetypes); +} + +void torture_tcase_node(struct torture_tcase *tcase) +{ + struct torture_test *test; + + torture_tcase_add_simple_test(tcase, "OpenNode", + test_OpenNode); + torture_tcase_add_simple_test(tcase, "OpenNodeEx", + test_OpenNodeEx); + torture_tcase_add_simple_test(tcase, "CloseNode", + test_CloseNode); + torture_tcase_add_simple_test(tcase, "GetNodeState", + test_GetNodeState); + torture_tcase_add_simple_test(tcase, "GetNodeId", + test_GetNodeId); + torture_tcase_add_simple_test(tcase, "NodeControl", + test_NodeControl); + test = torture_tcase_add_simple_test(tcase, "PauseNode", + test_PauseNode); + test->dangerous = true; + torture_tcase_add_simple_test(tcase, "ResumeNode", + test_ResumeNode); + test = torture_tcase_add_simple_test(tcase, "EvictNode", + test_EvictNode); + test->dangerous = true; + torture_tcase_add_simple_test(tcase, "all_nodes", + test_all_nodes); +} + +void torture_tcase_group(struct torture_tcase *tcase) +{ + struct torture_test *test; + + torture_tcase_add_simple_test(tcase, "OpenGroup", + test_OpenGroup); + torture_tcase_add_simple_test(tcase, "OpenGroupEx", + test_OpenGroupEx); + torture_tcase_add_simple_test(tcase, "CloseGroup", + test_CloseGroup); + torture_tcase_add_simple_test(tcase, "GetGroupState", + test_GetGroupState); + torture_tcase_add_simple_test(tcase, "GetGroupId", + test_GetGroupId); + torture_tcase_add_simple_test(tcase, "GroupControl", + test_GroupControl); + torture_tcase_add_simple_test(tcase, "OnlineGroup", + test_OnlineGroup); + test = torture_tcase_add_simple_test(tcase, "OfflineGroup", + test_OfflineGroup); + test->dangerous = true; + torture_tcase_add_simple_test(tcase, "all_groups", + test_all_groups); +} + +void torture_tcase_network(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "OpenNetwork", + test_OpenNetwork); + torture_tcase_add_simple_test(tcase, "OpenNetworkEx", + test_OpenNetworkEx); + torture_tcase_add_simple_test(tcase, "CloseNetwork", + test_CloseNetwork); + torture_tcase_add_simple_test(tcase, "GetNetworkState", + test_GetNetworkState); + torture_tcase_add_simple_test(tcase, "GetNetworkId", + test_GetNetworkId); + torture_tcase_add_simple_test(tcase, "all_networks", + test_all_networks); +} + +void torture_tcase_netinterface(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "OpenNetInterface", + test_OpenNetInterface); + torture_tcase_add_simple_test(tcase, "OpenNetInterfaceEx", + test_OpenNetInterfaceEx); + torture_tcase_add_simple_test(tcase, "CloseNetInterface", + test_CloseNetInterface); + torture_tcase_add_simple_test(tcase, "GetNetInterfaceState", + test_GetNetInterfaceState); + torture_tcase_add_simple_test(tcase, "GetNetInterfaceId", + test_GetNetInterfaceId); + torture_tcase_add_simple_test(tcase, "all_netinterfaces", + test_all_netinterfaces); +} + +void torture_tcase_registry(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "GetRootKey", + test_GetRootKey); + torture_tcase_add_simple_test(tcase, "CloseKey", + test_CloseKey); + torture_tcase_add_simple_test(tcase, "EnumKey", + test_EnumKey); + torture_tcase_add_simple_test(tcase, "QueryValue", + test_QueryValue); + torture_tcase_add_simple_test(tcase, "all_keys", + test_all_keys); +} + +void torture_tcase_groupset(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "OpenGroupSet", + test_OpenGroupSet); + torture_tcase_add_simple_test(tcase, "CloseGroupSet", + test_CloseGroupSet); + torture_tcase_add_simple_test(tcase, "all_groupsets", + test_all_groupsets); +} + +struct torture_suite *torture_rpc_clusapi(TALLOC_CTX *mem_ctx) +{ + struct torture_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "clusapi"); + + tcase = torture_suite_add_tcase(suite, "cluster"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_cluster(tcase); + + tcase = torture_suite_add_tcase(suite, "resource"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_resource(tcase); + + tcase = torture_suite_add_tcase(suite, "resourcetype"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_resourcetype(tcase); + + + tcase = torture_suite_add_tcase(suite, "node"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_node(tcase); + + tcase = torture_suite_add_tcase(suite, "group"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_group(tcase); + + tcase = torture_suite_add_tcase(suite, "network"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_network(tcase); + + tcase = torture_suite_add_tcase(suite, "netinterface"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_netinterface(tcase); + + tcase = torture_suite_add_tcase(suite, "registry"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_registry(tcase); + + tcase = torture_suite_add_tcase(suite, "groupset"); + + torture_tcase_set_fixture(tcase, + torture_rpc_clusapi_setup, + torture_rpc_clusapi_teardown); + + torture_tcase_groupset(tcase); + + return suite; +} diff --git a/source4/torture/rpc/countcalls.c b/source4/torture/rpc/countcalls.c new file mode 100644 index 0000000..52be979 --- /dev/null +++ b/source4/torture/rpc/countcalls.c @@ -0,0 +1,131 @@ +/* + Unix SMB/CIFS implementation. + + count number of calls on an interface + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/ndr/libndr.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + + + +bool count_calls(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const struct ndr_interface_table *iface, + bool all) +{ + struct dcerpc_pipe *p; + DATA_BLOB stub_in, stub_out; + int i; + NTSTATUS status = torture_rpc_connection(tctx, &p, iface); + if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status) + || NT_STATUS_IS_RPC(status) + || NT_STATUS_EQUAL(NT_STATUS_PORT_UNREACHABLE, status) + || NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + if (all) { + /* Not fatal if looking for all pipes */ + return true; + } else { + printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status)); + return false; + } + } else if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status)); + return false; + } + + stub_in = data_blob_null; + + printf("\nScanning pipe '%s'\n", iface->name); + + for (i=0;i<500;i++) { + uint32_t out_flags = 0; + + status = dcerpc_binding_handle_raw_call(p->binding_handle, + NULL, i, + 0, /* in_flags */ + stub_in.data, + stub_in.length, + mem_ctx, + &stub_out.data, + &stub_out.length, + &out_flags); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + i--; + break; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)) { + i--; + break; + } + } + + if (i==500) { + talloc_free(p); + printf("no limit on calls: %s!?\n", nt_errstr(status)); + return false; + } + + printf("Found %d calls\n", i); + + talloc_free(p); + + return true; + +} + +bool torture_rpc_countcalls(struct torture_context *torture) +{ + const struct ndr_interface_table *iface; + const char *iface_name; + bool ret = true; + const struct ndr_interface_list *l; + iface_name = lpcfg_parm_string(torture->lp_ctx, NULL, "countcalls", "interface"); + if (iface_name != NULL) { + iface = ndr_table_by_name(iface_name); + if (!iface) { + printf("Unknown interface '%s'\n", iface_name); + return false; + } + return count_calls(torture, torture, iface, false); + } + + for (l=ndr_table_list();l;l=l->next) { + TALLOC_CTX *loop_ctx; + loop_ctx = talloc_named(torture, 0, "torture_rpc_councalls loop context"); + ret &= count_calls(torture, loop_ctx, l->table, true); + talloc_free(loop_ctx); + } + return ret; +} diff --git a/source4/torture/rpc/dfs.c b/source4/torture/rpc/dfs.c new file mode 100644 index 0000000..14af288 --- /dev/null +++ b/source4/torture/rpc/dfs.c @@ -0,0 +1,651 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc dfs operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_dfs_c.h" +#include "libnet/libnet.h" +#include "torture/util.h" +#include "libcli/libcli.h" +#include "lib/cmdline/cmdline.h" + +#define SMBTORTURE_DFS_SHARENAME "smbtorture_dfs_share" +#define SMBTORTURE_DFS_DIRNAME "\\smbtorture_dfs_dir" +#define SMBTORTURE_DFS_PATHNAME "C:"SMBTORTURE_DFS_DIRNAME + +#define IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(x,y)\ + if (x == DFS_MANAGER_VERSION_W2K3) {\ + if (!W_ERROR_EQUAL(y,WERR_NOT_SUPPORTED)) {\ + printf("expected WERR_NOT_SUPPORTED\n");\ + return false;\ + }\ + return true;\ + }\ + +static bool test_NetShareAdd(struct torture_context *tctx, + const char *host, + const char *sharename, + const char *dir) +{ + NTSTATUS status; + struct srvsvc_NetShareInfo2 i; + struct libnet_context* libnetctx; + struct libnet_AddShare r; + + printf("Creating share %s\n", sharename); + + if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) { + return false; + } + + libnetctx->cred = samba_cmdline_get_creds(); + + i.name = sharename; + i.type = STYPE_DISKTREE; + i.path = dir; + i.max_users = (uint32_t) -1; + i.comment = "created by smbtorture"; + i.password = NULL; + i.permissions = 0x0; + i.current_users = 0x0; + + r.level = 2; + r.in.server_name = host; + r.in.share = i; + + status = libnet_AddShare(libnetctx, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Failed to add new share: %s (%s)\n", + nt_errstr(status), r.out.error_string); + return false; + } + + return true; +} + +static bool test_NetShareDel(struct torture_context *tctx, + const char *host, + const char *sharename) +{ + NTSTATUS status; + struct libnet_context* libnetctx; + struct libnet_DelShare r; + + torture_comment(tctx, "Deleting share %s\n", sharename); + + if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) { + return false; + } + + libnetctx->cred = samba_cmdline_get_creds(); + + r.in.share_name = sharename; + r.in.server_name = host; + + status = libnet_DelShare(libnetctx, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Failed to delete share: %s (%s)\n", + nt_errstr(status), r.out.error_string); + return false; + } + + return true; +} + +static bool test_CreateDir(TALLOC_CTX *mem_ctx, + struct smbcli_state **cli, + struct torture_context *tctx, + const char *host, + const char *share, + const char *dir) +{ + printf("Creating directory %s\n", dir); + + if (!torture_open_connection_share(mem_ctx, cli, tctx, host, share, tctx->ev)) { + return false; + } + + if (!torture_setup_dir(*cli, dir)) { + return false; + } + + return true; +} + +static bool test_DeleteDir(struct torture_context *tctx, + struct smbcli_state *cli, + const char *dir) +{ + torture_comment(tctx, "Deleting directory %s\n", dir); + + if (smbcli_deltree(cli->tree, dir) == -1) { + printf("Unable to delete dir %s - %s\n", dir, + smbcli_errstr(cli->tree)); + return false; + } + + return true; +} + +static bool test_GetManagerVersion_opts(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + enum dfs_ManagerVersion *version_p) +{ + struct dfs_GetManagerVersion r; + enum dfs_ManagerVersion version; + + r.out.version = &version; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_GetManagerVersion_r(b, tctx, &r), + "GetManagerVersion failed"); + + if (version_p) { + *version_p = version; + } + + return true; +} + + +static bool test_GetManagerVersion(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_GetManagerVersion_opts(tctx, b, NULL); +} + +static bool test_ManagerInitialize(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + enum dfs_ManagerVersion version; + struct dfs_ManagerInitialize r; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "Testing ManagerInitialize\n"); + + torture_assert(tctx, + test_GetManagerVersion_opts(tctx, b, &version), + "GetManagerVersion failed"); + + r.in.servername = host; + r.in.flags = 0; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_ManagerInitialize_r(b, tctx, &r), + "ManagerInitialize failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_warning(tctx, "dfs_ManagerInitialize failed - %s\n", + win_errstr(r.out.result)); + IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result); + return false; + } + + return true; +} + +static bool test_GetInfoLevel(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint16_t level, + const char *root) +{ + struct dfs_GetInfo r; + union dfs_Info info; + + torture_comment(tctx, "Testing GetInfo level %u on '%s'\n", level, root); + + r.in.dfs_entry_path = root; + r.in.servername = NULL; + r.in.sharename = NULL; + r.in.level = level; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_GetInfo_r(b, tctx, &r), + "GetInfo failed"); + + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) { + torture_warning(tctx, "dfs_GetInfo failed - %s\n", win_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_GetInfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *root) +{ + bool ret = true; + /* 103, 104, 105, 106 is only available on Set */ + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 100, 101, 102, 103, 104, 105, 106}; + int i; + + for (i=0;icount = 0; + e.e.info1->s = &s; + s.path = NULL; + + torture_comment(tctx, "Testing EnumEx level %u on '%s'\n", level, dfs_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_EnumEx_r(b, tctx, &rex), + "EnumEx failed"); + torture_assert_werr_ok(tctx, rex.out.result, + "EnumEx failed"); + + if (level == 1 && rex.out.total) { + int i; + for (i=0;i<*rex.out.total;i++) { + const char *root = rex.out.info->e.info1->s[i].path; + if (!test_GetInfo(tctx, b, root)) { + ret = false; + } + } + } + + if (level == 300 && rex.out.total) { + int i,k; + for (i=0;i<*rex.out.total;i++) { + uint16_t levels[] = {1, 2, 3, 4, 200}; /* 300 */ + const char *root = rex.out.info->e.info300->s[i].dom_root; + for (k=0;kcount = 0; + e.e.info1->s = &s; + s.path = NULL; + + torture_comment(tctx, "Testing Enum level %u\n", level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_Enum_r(b, tctx, &r), + "Enum failed"); + + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) { + torture_warning(tctx, "dfs_Enum failed - %s\n", win_errstr(r.out.result)); + return false; + } + + if (level == 1 && r.out.total) { + int i; + for (i=0;i<*r.out.total;i++) { + const char *root = r.out.info->e.info1->s[i].path; + if (!test_GetInfo(tctx, b, root)) { + ret = false; + } + } + } + + return ret; +} + + +static bool test_Enum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0;ibinding_handle; + const char *host = torture_setting_string(tctx, "host", NULL); + + for (i=0;iev)) { + test_DeleteDir(tctx, cli, dir); + torture_close_connection(cli); + } +} + +static bool test_StdRoot(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char *sharename = SMBTORTURE_DFS_SHARENAME; + const char *dir = SMBTORTURE_DFS_DIRNAME; + const char *path = SMBTORTURE_DFS_PATHNAME; + struct smbcli_state *cli; + bool ret = true; + const char *host = torture_setting_string(tctx, "host", NULL); + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing StdRoot\n"); + + test_cleanup_stdroot(tctx, b, host, sharename, dir); + + torture_assert(tctx, + test_CreateDir(tctx, &cli, tctx, host, "C$", dir), + "failed to connect C$ share and to create directory"); + torture_assert(tctx, + test_NetShareAdd(tctx, host, sharename, path), + "failed to create new share"); + + ret &= test_AddStdRoot(tctx, b, host, sharename); + ret &= test_RemoveStdRoot(tctx, b, host, sharename); + ret &= test_AddStdRootForced(tctx, b, host, sharename); + ret &= test_NetShareDel(tctx, host, sharename); + ret &= test_DeleteDir(tctx, cli, dir); + + torture_close_connection(cli); + + return ret; +} + +static bool test_GetDcAddress(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *host) +{ + struct dfs_GetDcAddress r; + uint8_t is_root = 0; + uint32_t ttl = 0; + const char *ptr; + + torture_comment(tctx, "Testing GetDcAddress\n"); + + ptr = host; + + r.in.servername = host; + r.in.server_fullname = r.out.server_fullname = &ptr; + r.in.is_root = r.out.is_root = &is_root; + r.in.ttl = r.out.ttl = &ttl; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_GetDcAddress_r(b, tctx, &r), + "GetDcAddress failed"); + torture_assert_werr_ok(tctx, r.out.result, + "dfs_GetDcAddress failed"); + + return true; +} + +static bool test_SetDcAddress(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *host) +{ + struct dfs_SetDcAddress r; + + torture_comment(tctx, "Testing SetDcAddress\n"); + + r.in.servername = host; + r.in.server_fullname = host; + r.in.flags = 0; + r.in.ttl = 1000; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_SetDcAddress_r(b, tctx, &r), + "SetDcAddress failed"); + torture_assert_werr_ok(tctx, r.out.result, + "dfs_SetDcAddress failed"); + + return true; +} + +static bool test_DcAddress(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_GetDcAddress(tctx, b, host)) { + return false; + } + + if (!test_SetDcAddress(tctx, b, host)) { + return false; + } + + return true; +} + +static bool test_FlushFtTable(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *host, + const char *sharename) +{ + struct dfs_FlushFtTable r; + enum dfs_ManagerVersion version; + + torture_comment(tctx, "Testing FlushFtTable\n"); + + torture_assert(tctx, + test_GetManagerVersion_opts(tctx, b, &version), + "GetManagerVersion failed"); + + r.in.servername = host; + r.in.rootshare = sharename; + + torture_assert_ntstatus_ok(tctx, + dcerpc_dfs_FlushFtTable_r(b, tctx, &r), + "FlushFtTable failed"); + if (!W_ERROR_IS_OK(r.out.result)) { + torture_warning(tctx, "dfs_FlushFtTable failed - %s\n", + win_errstr(r.out.result)); + IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result); + return false; + } + + return true; +} + +static bool test_FtRoot(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char *sharename = SMBTORTURE_DFS_SHARENAME; + const char *host = torture_setting_string(tctx, "host", NULL); + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_FlushFtTable(tctx, b, host, sharename); +} + +struct torture_suite *torture_rpc_dfs(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "dfs"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "netdfs", + &ndr_table_netdfs); + + torture_rpc_tcase_add_test(tcase, "GetManagerVersion", test_GetManagerVersion); + torture_rpc_tcase_add_test(tcase, "ManagerInitialize", test_ManagerInitialize); + torture_rpc_tcase_add_test(tcase, "Enum", test_Enum); + torture_rpc_tcase_add_test(tcase, "EnumEx", test_EnumEx); + torture_rpc_tcase_add_test(tcase, "StdRoot", test_StdRoot); + torture_rpc_tcase_add_test(tcase, "FtRoot", test_FtRoot); + torture_rpc_tcase_add_test(tcase, "DcAddress", test_DcAddress); + + return suite; +} diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c new file mode 100644 index 0000000..4c7e2fc --- /dev/null +++ b/source4/torture/rpc/drsuapi.c @@ -0,0 +1,1049 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005-2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/security/dom_sid.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "torturetest" + +static bool test_DsBind(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *bind_handle, + struct drsuapi_DsBindInfo28 *srv_info28) +{ + NTSTATUS status; + struct drsuapi_DsBind r; + struct GUID bind_guid; + struct drsuapi_DsBindInfo28 *bind_info28; + struct drsuapi_DsBindInfoCtr bind_info_ctr; + + ZERO_STRUCT(bind_info_ctr); + bind_info_ctr.length = 28; + + bind_info28 = &bind_info_ctr.info.info28; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; + bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); + + r.in.bind_guid = &bind_guid; + r.in.bind_info = &bind_info_ctr; + r.out.bind_handle = bind_handle; + + torture_comment(tctx, "Testing DsBind\n"); + + status = dcerpc_drsuapi_DsBind_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind"); + + if (srv_info28 != NULL) { + *srv_info28 = r.out.bind_info->info.info28; + } + + return true; +} + +static bool test_DsGetDomainControllerInfo(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsGetDomainControllerInfo r; + union drsuapi_DsGetDCInfoCtr ctr; + union drsuapi_DsGetDCInfoRequest req; + int32_t level_out = 0; + bool found = false; + int i, j, k; + + struct { + const char *name; + WERROR expected; + } names[] = { + { + .name = torture_join_dom_netbios_name(priv->join), + .expected = WERR_OK + }, + { + .name = torture_join_dom_dns_name(priv->join), + .expected = WERR_OK + }, + { + .name = "__UNKNOWN_DOMAIN__", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + { + .name = "unknown.domain.samba.example.com", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + }; + int levels[] = {1, 2}; + int level; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + for (j=0; j < ARRAY_SIZE(names); j++) { + level = levels[i]; + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + + r.in.req->req1.domain_name = names[j].name; + r.in.req->req1.level = level; + + r.out.ctr = &ctr; + r.out.level_out = &level_out; + + torture_comment(tctx, + "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n", + r.in.req->req1.level, r.in.req->req1.domain_name); + + status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed"); + torture_assert_werr_equal(tctx, + r.out.result, names[j].expected, + "DsGetDomainControllerInfo level with dns domain failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + /* If this was an error, we can't read the result structure */ + continue; + } + + torture_assert_int_equal(tctx, + r.in.req->req1.level, *r.out.level_out, + "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs"); + + switch (level) { + case 1: + for (k=0; k < r.out.ctr->ctr1.count; k++) { + if (strcasecmp_m(r.out.ctr->ctr1.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + found = true; + break; + } + } + break; + case 2: + for (k=0; k < r.out.ctr->ctr2.count; k++) { + if (strcasecmp_m(r.out.ctr->ctr2.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + found = true; + priv->dcinfo = r.out.ctr->ctr2.array[k]; + break; + } + } + break; + } + torture_assert(tctx, found, + "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join"); + } + } + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + + r.out.ctr = &ctr; + r.out.level_out = &level_out; + + r.in.req->req1.domain_name = "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */ + r.in.req->req1.level = -1; + + torture_comment(tctx, "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n", + r.in.req->req1.level, r.in.req->req1.domain_name); + + status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed"); + torture_assert_werr_ok(tctx, r.out.result, + "DsGetDomainControllerInfo with dns domain failed"); + + { + const char *dc_account = talloc_asprintf(tctx, "%s\\%s$", + torture_join_dom_netbios_name(priv->join), + priv->dcinfo.netbios_name); + torture_comment(tctx, "%s: Enum active LDAP sessions searching for %s\n", __func__, dc_account); + for (k=0; k < r.out.ctr->ctr01.count; k++) { + if (strcasecmp_m(r.out.ctr->ctr01.array[k].client_account, + dc_account)) { + found = true; + break; + } + } + torture_assert(tctx, found, + "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records"); + } + + + return true; +} + +static bool test_DsWriteAccountSpn(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsWriteAccountSpn r; + union drsuapi_DsWriteAccountSpnRequest req; + struct drsuapi_DsNameString names[2]; + union drsuapi_DsWriteAccountSpnResult res; + uint32_t level_out; + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + + torture_comment(tctx, "Testing DsWriteAccountSpn\n"); + + r.in.req->req1.operation = DRSUAPI_DS_SPN_OPERATION_ADD; + r.in.req->req1.unknown1 = 0; + r.in.req->req1.object_dn = priv->dcinfo.computer_dn; + r.in.req->req1.count = 2; + r.in.req->req1.spn_names = names; + names[0].str = talloc_asprintf(tctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name); + names[1].str = talloc_asprintf(tctx, "smbtortureSPN/%s",priv->dcinfo.dns_name); + + r.out.res = &res; + r.out.level_out = &level_out; + + status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn"); + + r.in.req->req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE; + r.in.req->req1.unknown1 = 0; + + status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn"); + + return true; +} + +static bool test_DsReplicaGetInfo(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsReplicaGetInfo r; + union drsuapi_DsReplicaGetInfoRequest req; + union drsuapi_DsReplicaInfo info; + enum drsuapi_DsReplicaInfoType info_type; + int i; + struct { + int32_t level; + int32_t infotype; + const char *obj_dn; + } array[] = { + { + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_NEIGHBORS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_CURSORS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO, + DRSUAPI_DS_REPLICA_INFO_PENDING_OPS, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CURSORS2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CURSORS3, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_REPSTO, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS, + "__IGNORED__" + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1, + NULL + },{ + DRSUAPI_DS_REPLICA_GET_INFO2, + DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS, + NULL + } + }; + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping DsReplicaGetInfo test against Samba4\n"); + return true; + } + + r.in.bind_handle = &priv->bind_handle; + r.in.req = &req; + + for (i=0; i < ARRAY_SIZE(array); i++) { + const char *object_dn; + + torture_comment(tctx, "Testing DsReplicaGetInfo level %d infotype %d\n", + array[i].level, array[i].infotype); + + object_dn = (array[i].obj_dn ? array[i].obj_dn : priv->domain_obj_dn); + + r.in.level = array[i].level; + switch(r.in.level) { + case DRSUAPI_DS_REPLICA_GET_INFO: + r.in.req->req1.info_type = array[i].infotype; + r.in.req->req1.object_dn = object_dn; + ZERO_STRUCT(r.in.req->req1.source_dsa_guid); + break; + case DRSUAPI_DS_REPLICA_GET_INFO2: + r.in.req->req2.info_type = array[i].infotype; + r.in.req->req2.object_dn = object_dn; + ZERO_STRUCT(r.in.req->req2.source_dsa_guid); + r.in.req->req2.flags = 0; + r.in.req->req2.attribute_name = NULL; + r.in.req->req2.value_dn_str = NULL; + r.in.req->req2.enumeration_context = 0; + break; + } + + r.out.info = &info; + r.out.info_type = &info_type; + + status = dcerpc_drsuapi_DsReplicaGetInfo_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo"); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) { + torture_comment(tctx, + "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n", + array[i].level, array[i].infotype); + } else { + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo"); + } + } + + return true; +} + +static bool test_DsReplicaSync(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + int i; + struct drsuapi_DsReplicaSync r; + union drsuapi_DsReplicaSyncRequest sync_req; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct dom_sid null_sid; + struct { + int32_t level; + } array[] = { + { + 1 + } + }; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_comment(tctx, "DsReplicaSync disabled - enable dangerous tests to use\n"); + return true; + } + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping DsReplicaSync test against Samba4\n"); + return true; + } + + ZERO_STRUCT(null_sid); + + r.in.bind_handle = &priv->bind_handle; + + for (i=0; i < ARRAY_SIZE(array); i++) { + torture_comment(tctx, "Testing DsReplicaSync level %d\n", + array[i].level); + + r.in.level = array[i].level; + switch(r.in.level) { + case 1: + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:""; + + sync_req.req1.naming_context = &nc; + sync_req.req1.source_dsa_guid = priv->dcinfo.ntds_guid; + sync_req.req1.source_dsa_dns = NULL; + sync_req.req1.options = 16; + + r.in.req = &sync_req; + break; + } + + status = dcerpc_drsuapi_DsReplicaSync_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaSync"); + } + + return true; +} + +static bool test_DsReplicaUpdateRefs(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsReplicaUpdateRefs r; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct GUID dest_dsa_guid; + const char *dest_dsa_guid_str; + struct dom_sid null_sid; + + ZERO_STRUCT(null_sid); + dest_dsa_guid = GUID_random(); + dest_dsa_guid_str = GUID_string(tctx, &dest_dsa_guid); + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; /* Only version 1 is defined presently */ + + /* setup NC */ + nc.guid = priv->domain_obj_dn ? GUID_zero():priv->domain_guid; + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : ""; + + /* default setup for request */ + r.in.req.req1.naming_context = &nc; + r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "%s._msdn.%s", + dest_dsa_guid_str, + priv->domain_dns_name); + r.in.req.req1.dest_dsa_guid = dest_dsa_guid; + + /* 1. deleting replica dest should fail */ + torture_comment(tctx, "delete: %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_DEL_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_DS_DRA_REF_NOT_FOUND, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 2. hopefully adding random replica dest should succeed */ + torture_comment(tctx, "add : %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_ADD_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_OK, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 3. try adding same replica dest - should fail */ + torture_comment(tctx, "add : %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_ADD_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_DS_DRA_REF_ALREADY_EXISTS, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 4. try resetting same replica dest - should succeed */ + torture_comment(tctx, "reset : %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_OK, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 5. delete random replicate added at step 2. */ + torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_DEL_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_OK, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 6. try replace on non-existing replica dest - should succeed */ + torture_comment(tctx, "replace: %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_OK, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + /* 7. delete random replicate added at step 6. */ + torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name); + r.in.req.req1.options = DRSUAPI_DRS_DEL_REF; + status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call_werr(tctx, p, + status, WERR_OK, &r, + "dcerpc_drsuapi_DsReplicaUpdateRefs"); + + return true; +} + +static bool test_DsGetNCChanges(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + int i; + struct drsuapi_DsGetNCChanges r; + union drsuapi_DsGetNCChangesRequest req; + union drsuapi_DsGetNCChangesCtr ctr; + struct drsuapi_DsReplicaObjectIdentifier nc; + struct dom_sid null_sid; + uint32_t level_out; + struct { + uint32_t level; + } array[] = { + { + 5 + }, + { + 8 + } + }; + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping DsGetNCChanges test against Samba4\n"); + return true; + } + + ZERO_STRUCT(null_sid); + + for (i=0; i < ARRAY_SIZE(array); i++) { + torture_comment(tctx, + "Testing DsGetNCChanges level %d\n", + array[i].level); + + r.in.bind_handle = &priv->bind_handle; + r.in.level = array[i].level; + r.out.level_out = &level_out; + r.out.ctr = &ctr; + + switch (r.in.level) { + case 5: + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : ""; + + r.in.req = &req; + r.in.req->req5.destination_dsa_guid = GUID_random(); + r.in.req->req5.source_dsa_invocation_id = GUID_zero(); + r.in.req->req5.naming_context = &nc; + r.in.req->req5.highwatermark.tmp_highest_usn = 0; + r.in.req->req5.highwatermark.reserved_usn = 0; + r.in.req->req5.highwatermark.highest_usn = 0; + r.in.req->req5.uptodateness_vector = NULL; + r.in.req->req5.replica_flags = 0; + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) { + r.in.req->req5.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION; + } + r.in.req->req5.max_object_count = 0; + r.in.req->req5.max_ndr_size = 0; + r.in.req->req5.extended_op = DRSUAPI_EXOP_NONE; + r.in.req->req5.fsmo_info = 0; + + break; + case 8: + nc.guid = GUID_zero(); + nc.sid = null_sid; + nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : ""; + + r.in.req = &req; + r.in.req->req8.destination_dsa_guid = GUID_random(); + r.in.req->req8.source_dsa_invocation_id = GUID_zero(); + r.in.req->req8.naming_context = &nc; + r.in.req->req8.highwatermark.tmp_highest_usn = 0; + r.in.req->req8.highwatermark.reserved_usn = 0; + r.in.req->req8.highwatermark.highest_usn = 0; + r.in.req->req8.uptodateness_vector = NULL; + r.in.req->req8.replica_flags = 0; + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) { + r.in.req->req8.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION; + } + if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) { + r.in.req->req8.replica_flags |= DRSUAPI_DRS_WRIT_REP; + } + r.in.req->req8.replica_flags |= DRSUAPI_DRS_INIT_SYNC + | DRSUAPI_DRS_PER_SYNC + | DRSUAPI_DRS_GET_ANC + | DRSUAPI_DRS_NEVER_SYNCED + ; + r.in.req->req8.max_object_count = 402; + r.in.req->req8.max_ndr_size = 402116; + r.in.req->req8.extended_op = DRSUAPI_EXOP_NONE; + r.in.req->req8.fsmo_info = 0; + r.in.req->req8.partial_attribute_set = NULL; + r.in.req->req8.partial_attribute_set_ex = NULL; + r.in.req->req8.mapping_ctr.num_mappings = 0; + r.in.req->req8.mapping_ctr.mappings = NULL; + + break; + } + + status = dcerpc_drsuapi_DsGetNCChanges_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsGetNCChanges"); + } + + return true; +} + +bool test_QuerySitesByCost(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_QuerySitesByCost r; + union drsuapi_QuerySitesByCostRequest req; + + const char *my_site = "Default-First-Site-Name"; + const char *remote_site1 = "smbtorture-nonexisting-site1"; + const char *remote_site2 = "smbtorture-nonexisting-site2"; + + req.req1.site_from = talloc_strdup(tctx, my_site); + req.req1.num_req = 2; + req.req1.site_to = talloc_zero_array(tctx, const char *, 2); + req.req1.site_to[0] = talloc_strdup(tctx, remote_site1); + req.req1.site_to[1] = talloc_strdup(tctx, remote_site2); + req.req1.flags = 0; + + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + + status = dcerpc_drsuapi_QuerySitesByCost_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_QuerySitesByCost"); + + if (W_ERROR_IS_OK(r.out.result)) { + torture_assert_werr_equal(tctx, + r.out.ctr->ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND, + "dcerpc_drsuapi_QuerySitesByCost"); + torture_assert_werr_equal(tctx, + r.out.ctr->ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND, + "dcerpc_drsuapi_QuerySitesByCost expected error_code WERR_DS_OBJ_NOT_FOUND"); + + torture_assert_int_equal(tctx, + r.out.ctr->ctr1.info[0].site_cost, -1, + "dcerpc_drsuapi_QuerySitesByCost"); + torture_assert_int_equal(tctx, + r.out.ctr->ctr1.info[1].site_cost, -1, + "dcerpc_drsuapi_QuerySitesByCost expected site cost"); + } + + return true; + + +} + +bool test_DsUnbind(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + struct drsuapi_DsUnbind r; + + r.in.bind_handle = &priv->bind_handle; + r.out.bind_handle = &priv->bind_handle; + + torture_comment(tctx, "Testing DsUnbind\n"); + + status = dcerpc_drsuapi_DsUnbind_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsUnbind"); + + return true; +} + + +/** + * Helper func to collect DC information for testing purposes. + * This function is almost identical to test_DsGetDomainControllerInfo + */ +bool torture_rpc_drsuapi_get_dcinfo(struct torture_context *torture, + struct DsPrivate *priv) +{ + NTSTATUS status; + int32_t level_out = 0; + struct drsuapi_DsGetDomainControllerInfo r; + union drsuapi_DsGetDCInfoCtr ctr; + int j, k; + const char *names[] = { + torture_join_dom_netbios_name(priv->join), + torture_join_dom_dns_name(priv->join)}; + + for (j=0; j < ARRAY_SIZE(names); j++) { + union drsuapi_DsGetDCInfoRequest req; + struct dcerpc_binding_handle *b = priv->drs_pipe->binding_handle; + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + + r.in.req->req1.domain_name = names[j]; + r.in.req->req1.level = 2; + + r.out.ctr = &ctr; + r.out.level_out = &level_out; + + status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(b, torture, &r); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + /* If this was an error, we can't read the result structure */ + continue; + } + + for (k=0; k < r.out.ctr->ctr2.count; k++) { + if (strcasecmp_m(r.out.ctr->ctr2.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + priv->dcinfo = r.out.ctr->ctr2.array[k]; + return true; + } + } + } + + return false; +} + +/** + * Common test case setup function to be used + * in DRS suit of test when appropriate + */ +bool torture_drsuapi_tcase_setup_common(struct torture_context *tctx, struct DsPrivate *priv) +{ + NTSTATUS status; + int rnd = rand() % 1000; + char *name = talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, rnd); + + torture_assert(tctx, priv, "Invalid argument"); + + priv->admin_credentials = samba_cmdline_get_creds(); + + torture_comment(tctx, "Create DRSUAPI pipe\n"); + status = torture_rpc_connection(tctx, + &priv->drs_pipe, + &ndr_table_drsuapi); + torture_assert(tctx, NT_STATUS_IS_OK(status), "Unable to connect to DRSUAPI pipe"); + + torture_comment(tctx, "About to join domain with name %s\n", name); + priv->join = torture_join_domain(tctx, name, ACB_SVRTRUST, + &priv->dc_credentials); + torture_assert(tctx, priv->join, "Failed to join as BDC"); + + if (!test_DsBind(priv->drs_pipe, tctx, + &priv->bind_handle, + &priv->srv_bind_info)) + { + /* clean up */ + torture_drsuapi_tcase_teardown_common(tctx, priv); + torture_fail(tctx, "Failed execute test_DsBind()"); + } + + /* try collect some information for testing */ + torture_rpc_drsuapi_get_dcinfo(tctx, priv); + + return true; +} + +/** + * Common test case teardown function to be used + * in DRS suit of test when appropriate + */ +bool torture_drsuapi_tcase_teardown_common(struct torture_context *tctx, struct DsPrivate *priv) +{ + if (priv->join) { + torture_leave_domain(tctx, priv->join); + } + + return true; +} + +/** + * Test case setup for DRSUAPI test case + */ +static bool torture_drsuapi_tcase_setup(struct torture_context *tctx, void **data) +{ + struct DsPrivate *priv; + + *data = priv = talloc_zero(tctx, struct DsPrivate); + + return torture_drsuapi_tcase_setup_common(tctx, priv); +} + +/** + * Test case tear-down for DRSUAPI test case + */ +static bool torture_drsuapi_tcase_teardown(struct torture_context *tctx, void *data) +{ + bool ret; + struct DsPrivate *priv = talloc_get_type(data, struct DsPrivate); + + ret = torture_drsuapi_tcase_teardown_common(tctx, priv); + + talloc_free(priv); + return ret; +} + +static bool __test_DsBind_assoc_group(struct torture_context *tctx, + const char *testname, + struct DsPrivate *priv, + struct cli_credentials *creds) +{ + NTSTATUS status; + const char *err_msg; + struct drsuapi_DsCrackNames r; + union drsuapi_DsNameRequest req; + uint32_t level_out; + union drsuapi_DsNameCtr ctr; + struct drsuapi_DsNameString names[1]; + const char *dom_sid = NULL; + struct dcerpc_pipe *p1 = NULL; + struct dcerpc_pipe *p2 = NULL; + TALLOC_CTX *mem_ctx = priv; + struct dcerpc_binding *binding = NULL; + struct policy_handle ds_bind_handle = { .handle_type = 0, }; + + torture_comment(tctx, "%s: starting...\n", testname); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "torture_rpc_binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, + &p1, + binding, + &ndr_table_drsuapi, + creds, + tctx->ev, + tctx->lp_ctx), + "connect p1"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, + &p2, + p1->binding, + &ndr_table_drsuapi, + creds, + tctx->ev, + tctx->lp_ctx), + "connect p2"); + + torture_assert(tctx, test_DsBind(p1, tctx, &ds_bind_handle, NULL), "DsBind"); + + ZERO_STRUCT(r); + r.in.bind_handle = &ds_bind_handle; + r.in.level = 1; + r.in.req = &req; + r.in.req->req1.codepage = 1252; /* german */ + r.in.req->req1.language = 0x00000407; /* german */ + r.in.req->req1.count = 1; + r.in.req->req1.names = names; + r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + + r.out.level_out = &level_out; + r.out.ctr = &ctr; + + dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join)); + + names[0].str = dom_sid; + + torture_comment(tctx, "Testing DsCrackNames on p1 with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + status = dcerpc_drsuapi_DsCrackNames_r(p1->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + torture_comment(tctx, "Testing DsCrackNames on p2 with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + TALLOC_FREE(p1); + + torture_comment(tctx, "Testing DsCrackNames on p2 (with p1 closed) with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + torture_comment(tctx, "%s: ... finished\n", testname); + return true; +} + +static bool test_DsBindAssocGroupAdmin(struct torture_context *tctx, + struct DsPrivate *priv, + struct cli_credentials *creds) +{ + return __test_DsBind_assoc_group(tctx, __func__, priv, + priv->admin_credentials); +} + +static bool test_DsBindAssocGroupDC(struct torture_context *tctx, + struct DsPrivate *priv, + struct cli_credentials *creds) +{ + return __test_DsBind_assoc_group(tctx, __func__, priv, + priv->dc_credentials); +} + +static bool test_DsBindAssocGroupWS(struct torture_context *tctx, + struct DsPrivate *priv, + struct cli_credentials *creds) +{ + struct test_join *wks_join = NULL; + struct cli_credentials *wks_credentials = NULL; + int rnd = rand() % 1000; + char *wks_name = talloc_asprintf(tctx, "WKS%s%d", TEST_MACHINE_NAME, rnd); + bool ret; + + torture_comment(tctx, "%s: About to join workstation with name %s\n", + __func__, wks_name); + wks_join = torture_join_domain(tctx, wks_name, ACB_WSTRUST, + &wks_credentials); + torture_assert(tctx, wks_join, "Failed to join as WORKSTATION"); + ret = __test_DsBind_assoc_group(tctx, __func__, priv, + wks_credentials); + torture_leave_domain(tctx, wks_join); + return ret; +} + +/** + * DRSUAPI test case implementation + */ +void torture_rpc_drsuapi_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drsuapi"); + + torture_tcase_set_fixture(tcase, torture_drsuapi_tcase_setup, + torture_drsuapi_tcase_teardown); + +#if 0 + test = torture_tcase_add_simple_test(tcase, "QuerySitesByCost", (run_func)test_QuerySitesByCost); +#endif + + torture_tcase_add_simple_test(tcase, "DsGetDomainControllerInfo", (run_func)test_DsGetDomainControllerInfo); + + torture_tcase_add_simple_test(tcase, "DsCrackNames", (run_func)test_DsCrackNames); + + torture_tcase_add_simple_test(tcase, "DsWriteAccountSpn", (run_func)test_DsWriteAccountSpn); + + torture_tcase_add_simple_test(tcase, "DsReplicaGetInfo", (run_func)test_DsReplicaGetInfo); + + torture_tcase_add_simple_test(tcase, "DsReplicaSync", (run_func)test_DsReplicaSync); + + torture_tcase_add_simple_test(tcase, "DsReplicaUpdateRefs", (run_func)test_DsReplicaUpdateRefs); + + torture_tcase_add_simple_test(tcase, "DsGetNCChanges", (run_func)test_DsGetNCChanges); + + torture_tcase_add_simple_test(tcase, "DsBindAssocGroupAdmin", (run_func)test_DsBindAssocGroupAdmin); + torture_tcase_add_simple_test(tcase, "DsBindAssocGroupDC", (run_func)test_DsBindAssocGroupDC); + torture_tcase_add_simple_test(tcase, "DsBindAssocGroupWS", (run_func)test_DsBindAssocGroupWS); +} diff --git a/source4/torture/rpc/drsuapi.h b/source4/torture/rpc/drsuapi.h new file mode 100644 index 0000000..3cc4be4 --- /dev/null +++ b/source4/torture/rpc/drsuapi.h @@ -0,0 +1,94 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "librpc/gen_ndr/drsuapi.h" + +/** + * Data structure common for most of DRSUAPI tests + */ +struct DsPrivate { + struct cli_credentials *admin_credentials; + struct dcerpc_pipe *drs_pipe; + struct policy_handle bind_handle; + struct drsuapi_DsBindInfo28 srv_bind_info; + + const char *domain_obj_dn; + const char *domain_guid_str; + const char *domain_dns_name; + struct GUID domain_guid; + struct drsuapi_DsGetDCInfo2 dcinfo; + struct test_join *join; + struct cli_credentials *dc_credentials; +}; + +/** + * Data structure of DRSUAPI W2K8 tests + * W2K8 Clients use different versions of structs + */ +struct DsPrivate_w2k8 { + struct dcerpc_pipe *drs_pipe; + struct policy_handle bind_handle; + struct GUID bind_guid; + struct drsuapi_DsBindInfoCtr srv_bind_info; + + const char *domain_obj_dn; + const char *domain_guid_str; + const char *domain_dns_name; + struct GUID domain_guid; + struct drsuapi_DsGetDCInfo3 dcinfo; + struct test_join *join; +}; + + +/** + * Custom torture macro to check dcerpc_drsuapi_ call + * return values printing more friendly messages + * \param _tctx torture context + * \param _p DCERPC pipe handle + * \param _ntstatus NTSTATUS for dcerpc_drsuapi_ call + * \param _werr_expected Expected windows error to be returned + * \param _pr in/out DCEPRC request structure - pointer + * \param _msg error message prefix + */ +#define torture_drsuapi_assert_call_werr(_tctx, _p, _ntstat, _werr_expected, _pr, _msg) \ + do { \ + NTSTATUS __nt = _ntstat; \ + if (!NT_STATUS_IS_OK(__nt)) { \ + const char *errstr = nt_errstr(__nt); \ + torture_fail(tctx, talloc_asprintf(_tctx, "%s failed - %s", _msg, errstr)); \ + } \ + torture_assert_werr_equal(_tctx, (_pr)->out.result, _werr_expected, _msg); \ + } while(0) + +/** + * Custom torture macro to check dcerpc_drsuapi_ call + * return values printing more friendly messages + * \param _tctx torture context + * \param _p DCERPC pipe handle + * \param _ntstatus NTSTATUS for dcerpc_drsuapi_ call + * \param _pr in/out DCEPRC request structure + * \param _msg error message prefix + */ +#define torture_drsuapi_assert_call(_tctx, _p, _ntstat, _pr, _msg) \ + torture_drsuapi_assert_call_werr(_tctx, _p, _ntstat, WERR_OK, _pr, _msg) + diff --git a/source4/torture/rpc/drsuapi_cracknames.c b/source4/torture/rpc/drsuapi_cracknames.c new file mode 100644 index 0000000..0cbccda --- /dev/null +++ b/source4/torture/rpc/drsuapi_cracknames.c @@ -0,0 +1,1087 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/torture_rpc.h" +#include +#include "libcli/security/security.h" + +#undef strcasecmp + +struct DsCrackNamesPrivate { + struct DsPrivate base; + + /* following names are used in Crack Names Matrix test */ + const char *fqdn_name; + const char *user_principal_name; + const char *service_principal_name; +}; + +static bool test_DsCrackNamesMatrix(struct torture_context *tctx, + struct DsPrivate *priv, const char *dn, + const char *user_principal_name, const char *service_principal_name) +{ + NTSTATUS status; + const char *err_msg; + struct drsuapi_DsCrackNames r; + union drsuapi_DsNameRequest req; + uint32_t level_out; + union drsuapi_DsNameCtr ctr; + struct dcerpc_pipe *p = priv->drs_pipe; + TALLOC_CTX *mem_ctx = priv; + + enum drsuapi_DsNameFormat formats[] = { + DRSUAPI_DS_NAME_FORMAT_UNKNOWN, + DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + DRSUAPI_DS_NAME_FORMAT_DISPLAY, + DRSUAPI_DS_NAME_FORMAT_GUID, + DRSUAPI_DS_NAME_FORMAT_CANONICAL, + DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN + }; + struct drsuapi_DsNameString names[ARRAY_SIZE(formats)]; + int i, j; + + const char *n_matrix[ARRAY_SIZE(formats)][ARRAY_SIZE(formats)]; + const char *n_from[ARRAY_SIZE(formats)]; + + ZERO_STRUCT(r); + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + r.in.req->req1.codepage = 1252; /* german */ + r.in.req->req1.language = 0x00000407; /* german */ + r.in.req->req1.count = 1; + r.in.req->req1.names = names; + r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + + r.out.level_out = &level_out; + r.out.ctr = &ctr; + + n_matrix[0][0] = dn; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + r.in.req->req1.format_desired = formats[i]; + names[0].str = dn; + torture_comment(tctx, "Testing DsCrackNames (matrix prep) with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, + "testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, + "testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } + + switch (formats[i]) { + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: + if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) { + err_msg = talloc_asprintf(mem_ctx, + "Unexpected error (%d): This name lookup should fail", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + torture_comment(tctx, __location__ ": (expected) error\n"); + break; + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: + if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NO_MAPPING) { + err_msg = talloc_asprintf(mem_ctx, + "Unexpected error (%d): This name lookup should fail", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + torture_comment(tctx, __location__ ": (expected) error\n"); + break; + case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: /* should fail as we ask server to convert to Unknown format */ + case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: + if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) { + err_msg = talloc_asprintf(mem_ctx, + "Unexpected error (%d): This name lookup should fail", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + torture_comment(tctx, __location__ ": (expected) error\n"); + break; + default: + if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames error: %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + break; + } + + switch (formats[i]) { + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: + n_from[i] = user_principal_name; + break; + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: + n_from[i] = service_principal_name; + break; + case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: + case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: + n_from[i] = NULL; + break; + default: + n_from[i] = r.out.ctr->ctr1->array[0].result_name; + printf("%s\n", n_from[i]); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (j = 0; j < ARRAY_SIZE(formats); j++) { + r.in.req->req1.format_offered = formats[i]; + r.in.req->req1.format_desired = formats[j]; + if (!n_from[i]) { + n_matrix[i][j] = NULL; + continue; + } + names[0].str = n_from[i]; + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, + "testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, + "testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", + names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, + win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } + + if (r.out.ctr->ctr1->array[0].status == DRSUAPI_DS_NAME_STATUS_OK) { + n_matrix[i][j] = r.out.ctr->ctr1->array[0].result_name; + } else { + n_matrix[i][j] = NULL; + } + } + } + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (j = 0; j < ARRAY_SIZE(formats); j++) { + torture_comment(tctx, "Converting %s (format %d)" + " to %d gave %s\n", + n_from[i] == NULL ? "NULL" : n_from[i], + formats[i], formats[j], + n_matrix[i][j] == NULL ? + "NULL" : n_matrix[i][j]); + + if (n_matrix[i][j] == n_from[j]) { + + /* We don't have a from name for these yet (and we can't map to them to find it out) */ + } else if (n_matrix[i][j] == NULL && n_from[i] == NULL) { + + /* we can't map to these two */ + } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL) { + } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) { + } else if (n_matrix[i][j] == NULL && n_from[j] != NULL) { + err_msg = talloc_asprintf(mem_ctx, + "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: should be %s", + formats[i], formats[j], n_from[j]); + torture_fail(tctx, err_msg); + } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) { + err_msg = talloc_asprintf(mem_ctx, + "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: should be %s", + formats[i], formats[j], n_matrix[i][j]); + torture_fail(tctx, err_msg); + } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) { + err_msg = talloc_asprintf(mem_ctx, + "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s", + formats[i], formats[j], n_matrix[i][j], n_from[j]); + torture_fail(tctx, err_msg); + } + } + } + + return true; +} + +bool test_DsCrackNames(struct torture_context *tctx, + struct DsPrivate *priv) +{ + NTSTATUS status; + const char *err_msg; + struct drsuapi_DsCrackNames r; + union drsuapi_DsNameRequest req; + uint32_t level_out; + union drsuapi_DsNameCtr ctr; + struct drsuapi_DsNameString names[1]; + const char *dns_domain; + const char *nt4_domain; + const char *FQDN_1779_name; + struct ldb_context *ldb; + struct ldb_dn *FQDN_1779_dn; + struct ldb_dn *realm_dn; + const char *realm_dn_str; + const char *realm_canonical; + const char *realm_canonical_ex; + const char *user_principal_name; + char *user_principal_name_short; + const char *service_principal_name; + const char *canonical_name; + const char *canonical_ex_name; + const char *dom_sid; + const char *test_dc = torture_join_netbios_name(priv->join); + struct dcerpc_pipe *p = priv->drs_pipe; + TALLOC_CTX *mem_ctx = priv; + + ZERO_STRUCT(r); + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + r.in.req->req1.codepage = 1252; /* german */ + r.in.req->req1.language = 0x00000407; /* german */ + r.in.req->req1.count = 1; + r.in.req->req1.names = names; + r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; + + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + + r.out.level_out = &level_out; + r.out.ctr = &ctr; + + dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join)); + + names[0].str = dom_sid; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + dns_domain = r.out.ctr->ctr1->array[0].dns_domain_name; + nt4_domain = r.out.ctr->ctr1->array[0].result_name; + + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_GUID; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + priv->domain_dns_name = r.out.ctr->ctr1->array[0].dns_domain_name; + priv->domain_guid_str = r.out.ctr->ctr1->array[0].result_name; + GUID_from_string(priv->domain_guid_str, &priv->domain_guid); + + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + ldb = ldb_init(mem_ctx, tctx->ev); + + realm_dn_str = r.out.ctr->ctr1->array[0].result_name; + realm_dn = ldb_dn_new(mem_ctx, ldb, realm_dn_str); + realm_canonical = ldb_dn_canonical_string(mem_ctx, realm_dn); + + if (strcmp(realm_canonical, + talloc_asprintf(mem_ctx, "%s/", dns_domain))!= 0) { + err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical name failed: %s != %s!", + realm_canonical, + talloc_asprintf(mem_ctx, "%s/", dns_domain)); + torture_fail(tctx, err_msg); + }; + + realm_canonical_ex = ldb_dn_canonical_ex_string(mem_ctx, realm_dn); + + if (strcmp(realm_canonical_ex, + talloc_asprintf(mem_ctx, "%s\n", dns_domain))!= 0) { + err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical ex name failed: %s != %s!", + realm_canonical_ex, + talloc_asprintf(mem_ctx, "%s\n", dns_domain)); + torture_fail(tctx, err_msg); + }; + + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = nt4_domain; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + priv->domain_obj_dn = r.out.ctr->ctr1->array[0].result_name; + + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc); + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + FQDN_1779_name = r.out.ctr->ctr1->array[0].result_name; + + r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_GUID; + r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; + names[0].str = priv->domain_guid_str; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", + r.out.ctr->ctr1->array[0].status); + torture_fail(tctx, err_msg); + } + + if (strcmp(priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name) != 0) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames failed to return same DNS name - expected %s got %s", + priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name); + torture_fail(tctx, err_msg); + } + + FQDN_1779_dn = ldb_dn_new(mem_ctx, ldb, FQDN_1779_name); + + canonical_name = ldb_dn_canonical_string(mem_ctx, FQDN_1779_dn); + canonical_ex_name = ldb_dn_canonical_ex_string(mem_ctx, FQDN_1779_dn); + + user_principal_name = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, dns_domain); + + /* form up a user@DOMAIN */ + user_principal_name_short = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, nt4_domain); + /* variable nt4_domain includes a trailing \ */ + user_principal_name_short[strlen(user_principal_name_short) - 1] = '\0'; + + service_principal_name = talloc_asprintf(mem_ctx, "HOST/%s", test_dc); + { + + struct { + enum drsuapi_DsNameFormat format_offered; + enum drsuapi_DsNameFormat format_desired; + const char *comment; + const char *str; + const char *expected_str; + const char *expected_dns; + enum drsuapi_DsNameStatus status; + enum drsuapi_DsNameStatus alternate_status; + enum drsuapi_DsNameFlags flags; + bool skip; + } crack[] = { + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = user_principal_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = user_principal_name_short, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = service_principal_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s", test_dc, dns_domain), + .comment = "ServicePrincipal Name", + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = FQDN_1779_name, + .expected_str = canonical_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = canonical_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = FQDN_1779_name, + .expected_str = canonical_ex_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = canonical_ex_name, + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = FQDN_1779_name, + .comment = "DN to cannoical syntactial only", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = canonical_name, + .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = FQDN_1779_name, + .comment = "DN to cannoical EX syntactial only", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = canonical_ex_name, + .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID, + .str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = priv->domain_guid_str, + .comment = "Domain GUID to NT4 ACCOUNT", + .expected_str = nt4_domain, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL, + .str = priv->domain_guid_str, + .comment = "Domain GUID to Canonical", + .expected_str = talloc_asprintf(mem_ctx, "%s/", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + .str = priv->domain_guid_str, + .comment = "Domain GUID to Canonical EX", + .expected_str = talloc_asprintf(mem_ctx, "%s\n", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "CN=Microsoft Corporation,L=Redmond,S=Washington,C=US", + .comment = "display name for Microsoft Support Account", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE, + .skip = torture_setting_bool(tctx, "samba4", false) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)), + .comment = "Account GUID -> DN", + .expected_str = FQDN_1779_name, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)), + .comment = "Account GUID -> NT4 Account", + .expected_str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.site_guid), + .comment = "Site GUID", + .expected_str = priv->dcinfo.site_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid), + .comment = "Computer GUID", + .expected_str = priv->dcinfo.computer_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid), + .comment = "Computer GUID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.server_guid), + .comment = "Server GUID", + .expected_str = priv->dcinfo.server_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = GUID_string2(mem_ctx, &priv->dcinfo.ntds_guid), + .comment = "NTDS GUID", + .expected_str = priv->dcinfo.ntds_dn, + .status = DRSUAPI_DS_NAME_STATUS_OK, + .skip = GUID_all_zero(&priv->dcinfo.ntds_guid) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = test_dc, + .comment = "DISPLAY NAME search for DC short name", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "krbtgt/%s", dns_domain), + .comment = "Looking for KRBTGT as a service principal", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = dns_domain + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "bogus/%s", dns_domain), + .comment = "Looking for bogus service principal", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = dns_domain + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "bogus/%s.%s", test_dc, dns_domain), + .comment = "Looking for bogus service on test DC", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = talloc_asprintf(mem_ctx, "%s.%s", test_dc, dns_domain) + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "krbtgt"), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Looking for the kadmin/changepw service as a service principal", + .str = talloc_asprintf(mem_ctx, "kadmin/changepw"), + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = talloc_asprintf(mem_ctx, "CN=krbtgt,CN=Users,%s", realm_dn_str), + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, dns_domain, + dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, dns_domain, + "BOGUS"), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "BOGUS" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s", + test_dc, "REALLY", + "BOGUS"), + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "BOGUS" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s.%s", + test_dc, dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = talloc_asprintf(mem_ctx, "cifs/%s", + test_dc), + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT A GUID", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT A SID", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = "NOT AN NT4 NAME", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID, + .comment = "Unparsable DN", + .str = "NOT A DN", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unparsable user principal", + .str = "NOT A PRINCIPAL", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unparsable service principal", + .str = "NOT A SERVICE PRINCIPAL", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "BIND GUID (ie, not in the directory)", + .str = DRSUAPI_DS_BIND_GUID, + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Unqualified Machine account as user principal", + .str = talloc_asprintf(mem_ctx, "%s$", test_dc), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Machine account as service principal", + .str = talloc_asprintf(mem_ctx, "%s$", test_dc), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Full Machine account as service principal", + .str = user_principal_name, + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Realm as an NT4 domain lookup", + .str = talloc_asprintf(mem_ctx, "%s\\", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "BUILTIN\\ -> DN", + .str = "BUILTIN\\", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\ -> DN", + .str = "NT AUTHORITY\\", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\ANONYMOUS LOGON -> DN", + .str = "NT AUTHORITY\\ANONYMOUS LOGON", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "NT AUTHORITY\\SYSTEM -> DN", + .str = "NT AUTHORITY\\SYSTEM", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .comment = "BUILTIN SID -> NT4 account", + .str = SID_BUILTIN, + .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = SID_BUILTIN, + .comment = "Builtin Domain SID -> DN", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .expected_str = talloc_asprintf(mem_ctx, "CN=Builtin,%s", realm_dn_str), + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .str = SID_BUILTIN_ADMINISTRATORS, + .comment = "Builtin Administrors SID -> DN", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_BUILTIN_ADMINISTRATORS, + .comment = "Builtin Administrors SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_OK, + .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_NT_ANONYMOUS, + .comment = "NT Anonymous SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .str = SID_NT_SYSTEM, + .comment = "NT SYSTEM SID -> NT4 Account", + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "Domain SID -> DN", + .str = dom_sid, + .expected_str = realm_dn_str, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, + .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, + .comment = "Domain SID -> NT4 account", + .str = dom_sid, + .expected_str = nt4_domain, + .status = DRSUAPI_DS_NAME_STATUS_OK + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "invalid user principal name", + .str = "foo@bar", + .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY, + .expected_dns = "bar" + }, + { + .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + .comment = "invalid user principal name in valid domain", + .str = talloc_asprintf(mem_ctx, "invalidusername@%s", dns_domain), + .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND + } + }; + int i; + + for (i=0; i < ARRAY_SIZE(crack); i++) { + const char *comment; + + torture_comment(tctx, "Testing DsCrackNames with name '%s'" + " offered format: %d desired format:%d\n", + crack[i].str, + crack[i].format_offered, + crack[i].format_desired); + + r.in.req->req1.format_flags = crack[i].flags; + r.in.req->req1.format_offered = crack[i].format_offered; + r.in.req->req1.format_desired = crack[i].format_desired; + names[0].str = crack[i].str; + + if (crack[i].comment) { + comment = talloc_asprintf(mem_ctx, + "'%s' with name '%s' offered format:%d desired format:%d\n", + crack[i].comment, names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + } else { + comment = talloc_asprintf(mem_ctx, "'%s' offered format:%d desired format:%d\n", + names[0].str, + r.in.req->req1.format_offered, + r.in.req->req1.format_desired); + } + if (crack[i].skip) { + torture_comment(tctx, "skipping: %s", comment); + continue; + } + status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); + torture_fail(tctx, err_msg); + } else if (!W_ERROR_IS_OK(r.out.result)) { + err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); + torture_fail(tctx, err_msg); + } else if (r.out.ctr->ctr1->array[0].status != crack[i].status) { + if (crack[i].alternate_status) { + if (r.out.ctr->ctr1->array[0].status != crack[i].alternate_status) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames unexpected status %d, wanted %d or %d on: %s", + r.out.ctr->ctr1->array[0].status, + crack[i].status, + crack[i].alternate_status, + comment); + torture_fail(tctx, err_msg); + } + } else { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames unexpected status %d, wanted %d on: %s\n", + r.out.ctr->ctr1->array[0].status, + crack[i].status, + comment); + torture_fail(tctx, err_msg); + } + } else if (crack[i].expected_str && + (!r.out.ctr->ctr1->count || + !r.out.ctr->ctr1->array[0].result_name)) + { + if (!r.out.ctr->ctr1->count) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames failed - got 0 entries, expected %s on %s", + crack[i].expected_str, comment); + torture_fail(tctx, err_msg); + } else { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames failed - got NULL pointer, expected %s on %s", + crack[i].expected_str, comment); + torture_fail(tctx, err_msg); + } + } else if (crack[i].expected_str + && (strcmp(r.out.ctr->ctr1->array[0].result_name, + crack[i].expected_str) != 0)) + { + if (strcasecmp(r.out.ctr->ctr1->array[0].result_name, + crack[i].expected_str) != 0) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames failed - got %s, expected %s on %s", + r.out.ctr->ctr1->array[0].result_name, + crack[i].expected_str, comment); + torture_fail(tctx, err_msg); + } else { + torture_comment(tctx, + "(warning) DsCrackNames returned different case - got %s, expected %s on %s\n", + r.out.ctr->ctr1->array[0].result_name, + crack[i].expected_str, comment); + } + } else if (crack[i].expected_dns + && (strcmp(r.out.ctr->ctr1->array[0].dns_domain_name, + crack[i].expected_dns) != 0)) { + err_msg = talloc_asprintf(mem_ctx, + "DsCrackNames failed - got DNS name %s, expected %s on %s", + r.out.ctr->ctr1->array[0].result_name, + crack[i].expected_str, comment); + torture_fail(tctx, err_msg); + } + + torture_comment(tctx, "Testing DsCrackNames got %s\n", r.out.ctr->ctr1->array[0].result_name); + } + } + + return test_DsCrackNamesMatrix(tctx, priv, FQDN_1779_name, + user_principal_name, service_principal_name); +} + +/** + * Test case setup for CrackNames + */ +static bool torture_drsuapi_cracknames_setup(struct torture_context *tctx, void **data) +{ + struct DsCrackNamesPrivate *priv; + + *data = priv = talloc_zero(tctx, struct DsCrackNamesPrivate); + + return torture_drsuapi_tcase_setup_common(tctx, &priv->base); +} + +/** + * Test case tear-down for CrackNames + */ +static bool torture_drsuapi_cracknames_teardown(struct torture_context *tctx, void *data) +{ + struct DsCrackNamesPrivate *priv = talloc_get_type(data, struct DsCrackNamesPrivate); + + return torture_drsuapi_tcase_teardown_common(tctx, &priv->base); +} + +/** + * CRACKNAMES test suite implementation + */ +void torture_rpc_drsuapi_cracknames_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "cracknames"); + + torture_tcase_set_fixture(tcase, + torture_drsuapi_cracknames_setup, + torture_drsuapi_cracknames_teardown); + + torture_tcase_add_simple_test(tcase, "cracknames-test", (run_func)test_DsCrackNames); +} diff --git a/source4/torture/rpc/drsuapi_w2k8.c b/source4/torture/rpc/drsuapi_w2k8.c new file mode 100644 index 0000000..9ff37ed --- /dev/null +++ b/source4/torture/rpc/drsuapi_w2k8.c @@ -0,0 +1,334 @@ +/* + Unix SMB/CIFS implementation. + + DRSUapi tests + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005-2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + +#define TEST_MACHINE_NAME "torturetest" + +/* + * DsBind as sent from W2K8 Client. + * This should work regardless of functional level, and accept + * any info <=48 + */ +bool test_DsBind_w2k8(struct torture_context *tctx, + struct DsPrivate_w2k8 *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsBind r; + struct drsuapi_DsBindInfo48 *bind_info48; + struct drsuapi_DsBindInfoCtr bind_info_ctr; + + /* We send info48 */ + ZERO_STRUCT(bind_info_ctr); + bind_info_ctr.length = 48; + + bind_info48 = &bind_info_ctr.info.info48; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; + bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; + + /* + * We wish for DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2, + * needed for DsGetDomainControllerInfo level 3 + */ + bind_info48->supported_extensions_ext |= DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid); + + r.in.bind_guid = &priv->bind_guid; + r.in.bind_info = &bind_info_ctr; + r.out.bind_handle = &priv->bind_handle; + + torture_comment(tctx, "Testing DsBind W2K8\n"); + + status = dcerpc_drsuapi_DsBind_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind"); + + torture_assert_not_null(tctx, r.out.bind_info, + "DsBind with info48 results in NULL"); + + /* cache server supported extensions, i.e. bind_info */ + priv->srv_bind_info = *r.out.bind_info; + + /* + * We do not check for length here, because it should be valid to return + * any valid info + */ + + return true; +} + +static bool test_DsGetDomainControllerInfo_w2k8(struct torture_context *tctx, + struct DsPrivate_w2k8 *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsGetDomainControllerInfo r; + union drsuapi_DsGetDCInfoCtr ctr; + int32_t level_out = 0; + uint32_t supported_extensions_ext = 0; + bool found = false; + int j, k; + + struct { + const char *name; + WERROR expected; + } names[] = { + { + .name = torture_join_dom_netbios_name(priv->join), + .expected = WERR_OK + }, + { + .name = torture_join_dom_dns_name(priv->join), + .expected = WERR_OK + }, + { + .name = "__UNKNOWN_DOMAIN__", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + { + .name = "unknown.domain.samba.example.com", + .expected = WERR_DS_OBJ_NOT_FOUND + }, + }; + + /* Levels 1 and 2 are tested in standard drsuapi tests */ + int level = 3; + + /* Do Bind first. */ + if (!test_DsBind_w2k8(tctx, priv)) { + return false; + } + + /* + * We used DsBind_w2k8, so DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2 + * should mean support for level 3 + */ + + /* + * We are looking for an extension found in info32 and later + */ + switch (priv->srv_bind_info.length) { + case 32: + supported_extensions_ext = priv->srv_bind_info.info.info32.supported_extensions_ext; + break; + case 48: + supported_extensions_ext = priv->srv_bind_info.info.info48.supported_extensions_ext; + break; + default: + supported_extensions_ext = 0; + break; + } + + supported_extensions_ext &= DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2; + torture_assert(tctx, (supported_extensions_ext > 0), + "Server does not support DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2"); + + for (j=0; j < ARRAY_SIZE(names); j++) { + union drsuapi_DsGetDCInfoRequest req; + r.in.bind_handle = &priv->bind_handle; + r.in.level = 1; + r.in.req = &req; + + r.in.req->req1.domain_name = names[j].name; + r.in.req->req1.level = level; + + r.out.ctr = &ctr; + r.out.level_out = &level_out; + + torture_comment(tctx, + "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n", + r.in.req->req1.level, r.in.req->req1.domain_name); + + status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed"); + torture_assert_werr_equal(tctx, r.out.result, names[j].expected, + "DsGetDomainControllerInfo level with dns domain failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + /* If this was an error, we can't read the result structure */ + continue; + } + + torture_assert_int_equal(tctx, r.in.req->req1.level, *r.out.level_out, + "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs"); + + for (k=0; k < r.out.ctr->ctr3.count; k++) { + if (strcasecmp_m(r.out.ctr->ctr3.array[k].netbios_name, + torture_join_netbios_name(priv->join)) == 0) { + found = true; + priv->dcinfo = r.out.ctr->ctr3.array[k]; + break; + } + } + break; + + torture_assert(tctx, found, + "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join"); + } + + return true; +} + + +bool test_DsUnbind_w2k8(struct torture_context *tctx, + struct DsPrivate_w2k8 *priv) +{ + NTSTATUS status; + struct dcerpc_pipe *p = priv->drs_pipe; + struct drsuapi_DsUnbind r; + + r.in.bind_handle = &priv->bind_handle; + r.out.bind_handle = &priv->bind_handle; + + torture_comment(tctx, "Testing DsUnbind W2K8\n"); + + status = dcerpc_drsuapi_DsUnbind_r(p->binding_handle, tctx, &r); + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsUnbind"); + + return true; +} + +/** + * Common test case setup function to be used + * in DRS suit of test when appropriate + */ +bool torture_drsuapi_w2k8_tcase_setup_common(struct torture_context *tctx, + struct DsPrivate_w2k8 *priv) +{ + NTSTATUS status; + int rnd = rand() % 1000; + char *name = talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, rnd); + struct cli_credentials *machine_credentials; + + torture_assert(tctx, priv, "Invalid argument"); + + torture_comment(tctx, "Create DRSUAPI pipe\n"); + status = torture_rpc_connection(tctx, + &priv->drs_pipe, + &ndr_table_drsuapi); + torture_assert(tctx, NT_STATUS_IS_OK(status), "Unable to connect to DRSUAPI pipe"); + + torture_comment(tctx, "About to join domain with name %s\n", name); + priv->join = torture_join_domain(tctx, name, ACB_SVRTRUST, + &machine_credentials); + torture_assert(tctx, priv->join, "Failed to join as BDC"); + + /* + * After that every test should use DsBind and DsGetDomainControllerInfo + */ + if (!test_DsBind_w2k8(tctx, priv)) { + /* clean up */ + torture_drsuapi_w2k8_tcase_teardown_common(tctx, priv); + torture_fail(tctx, "Failed execute test_DsBind_w2k8()"); + } + + + return true; +} + +/** + * Common test case teardown function to be used + * in DRS suit of test when appropriate + */ +bool torture_drsuapi_w2k8_tcase_teardown_common(struct torture_context *tctx, + struct DsPrivate_w2k8 *priv) +{ + if (priv->join) { + torture_leave_domain(tctx, priv->join); + } + + return true; +} + +/** + * Test case setup for DRSUAPI test case + */ +static bool torture_drsuapi_w2k8_tcase_setup(struct torture_context *tctx, void **data) +{ + struct DsPrivate_w2k8 *priv; + + *data = priv = talloc_zero(tctx, struct DsPrivate_w2k8); + + return torture_drsuapi_w2k8_tcase_setup_common(tctx, priv); +} + +/** + * Test case tear-down for DRSUAPI test case + */ +static bool torture_drsuapi_w2k8_tcase_teardown(struct torture_context *tctx, void *data) +{ + bool ret; + struct DsPrivate_w2k8 *priv = talloc_get_type(data, struct DsPrivate_w2k8); + + ret = torture_drsuapi_w2k8_tcase_teardown_common(tctx, priv); + + talloc_free(priv); + return ret; +} + +/** + * DRSUAPI test case implementation + */ +void torture_rpc_drsuapi_w2k8_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drsuapi_w2k8"); + + torture_tcase_set_fixture(tcase, torture_drsuapi_w2k8_tcase_setup, + torture_drsuapi_w2k8_tcase_teardown); + + torture_tcase_add_simple_test(tcase, "DsBind_W2K8", (run_func)test_DsBind_w2k8); + torture_tcase_add_simple_test(tcase, "DsGetDomainControllerInfo_W2K8", (run_func)test_DsGetDomainControllerInfo_w2k8); +} diff --git a/source4/torture/rpc/dsgetinfo.c b/source4/torture/rpc/dsgetinfo.c new file mode 100644 index 0000000..b47d6ee --- /dev/null +++ b/source4/torture/rpc/dsgetinfo.c @@ -0,0 +1,452 @@ +/* + Unix SMB/CIFS implementation. + + DsGetReplInfo test. Based on code from dssync.c + + Copyright (C) Erick Nogueira do Nascimento 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "libcli/cldap/cldap.h" +#include "torture/torture.h" +#include "../libcli/drsuapi/drsuapi.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "dsdb/samdb/samdb.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/drs/proto.h" +#include "lib/util/util_paths.h" + + +struct DsGetinfoBindInfo { + struct dcerpc_pipe *drs_pipe; + struct dcerpc_binding_handle *drs_handle; + struct drsuapi_DsBind req; + struct GUID bind_guid; + struct drsuapi_DsBindInfoCtr our_bind_info_ctr; + struct drsuapi_DsBindInfo28 our_bind_info28; + struct drsuapi_DsBindInfo28 peer_bind_info28; + struct policy_handle bind_handle; +}; + +struct DsGetinfoTest { + struct dcerpc_binding *drsuapi_binding; + + const char *ldap_url; + const char *site_name; + + const char *domain_dn; + + /* what we need to do as 'Administrator' */ + struct { + struct cli_credentials *credentials; + struct DsGetinfoBindInfo drsuapi; + } admin; +}; + + + +/* + return the default DN for a ldap server given a connected RPC pipe to the + server + */ +static const char *torture_get_ldap_base_dn(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + const char *hostname = dcerpc_binding_get_string_option(p->binding, "host"); + struct ldb_context *ldb; + const char *ldap_url = talloc_asprintf(p, "ldap://%s", hostname); + const char *attrs[] = { "defaultNamingContext", NULL }; + const char *dnstr; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + int ret; + struct ldb_result *res; + + ldb = ldb_init(tmp_ctx, tctx->ev); + if (ldb == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + if (ldb_set_opaque(ldb, "loadparm", tctx->lp_ctx)) { + talloc_free(ldb); + return NULL; + } + + ldb_set_modules_dir(ldb, + modules_path(ldb, "ldb")); + + ret = ldb_connect(ldb, ldap_url, 0, NULL); + if (ret != LDB_SUCCESS) { + torture_comment(tctx, "Failed to make LDB connection to target"); + talloc_free(tmp_ctx); + return NULL; + } + + ret = dsdb_search_dn(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, ""), + attrs, 0); + if (ret != LDB_SUCCESS) { + torture_comment(tctx, "Failed to get defaultNamingContext"); + talloc_free(tmp_ctx); + return NULL; + } + + dnstr = ldb_msg_find_attr_as_string(res->msgs[0], "defaultNamingContext", NULL); + dnstr = talloc_strdup(tctx, dnstr); + talloc_free(tmp_ctx); + return dnstr; +} + + +static struct DsGetinfoTest *test_create_context(struct torture_context *tctx) +{ + NTSTATUS status; + struct DsGetinfoTest *ctx; + struct drsuapi_DsBindInfo28 *our_bind_info28; + struct drsuapi_DsBindInfoCtr *our_bind_info_ctr; + const char *binding = torture_setting_string(tctx, "binding", NULL); + ctx = talloc_zero(tctx, struct DsGetinfoTest); + if (!ctx) return NULL; + + status = dcerpc_parse_binding(ctx, binding, &ctx->drsuapi_binding); + if (!NT_STATUS_IS_OK(status)) { + printf("Bad binding string %s\n", binding); + return NULL; + } + status = dcerpc_binding_set_flags(ctx->drsuapi_binding, DCERPC_SIGN | DCERPC_SEAL, 0); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_binding_set_flags - %s\n", nt_errstr(status)); + return NULL; + } + + /* ctx->admin ...*/ + ctx->admin.credentials = samba_cmdline_get_creds(); + + our_bind_info28 = &ctx->admin.drsuapi.our_bind_info28; + our_bind_info28->supported_extensions = 0xFFFFFFFF; + our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; + our_bind_info28->site_guid = GUID_zero(); + our_bind_info28->pid = 0; + our_bind_info28->repl_epoch = 1; + + our_bind_info_ctr = &ctx->admin.drsuapi.our_bind_info_ctr; + our_bind_info_ctr->length = 28; + our_bind_info_ctr->info.info28 = *our_bind_info28; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &ctx->admin.drsuapi.bind_guid); + + ctx->admin.drsuapi.req.in.bind_guid = &ctx->admin.drsuapi.bind_guid; + ctx->admin.drsuapi.req.in.bind_info = our_bind_info_ctr; + ctx->admin.drsuapi.req.out.bind_handle = &ctx->admin.drsuapi.bind_handle; + + return ctx; +} + +static bool _test_DsBind(struct torture_context *tctx, + struct DsGetinfoTest *ctx, struct cli_credentials *credentials, struct DsGetinfoBindInfo *b) +{ + NTSTATUS status; + bool ret = true; + + status = dcerpc_pipe_connect_b(ctx, + &b->drs_pipe, ctx->drsuapi_binding, + &ndr_table_drsuapi, + credentials, tctx->ev, tctx->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status)); + return false; + } + b->drs_handle = b->drs_pipe->binding_handle; + + status = dcerpc_drsuapi_DsBind_r(b->drs_handle, ctx, &b->req); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr); + ret = false; + } else if (!W_ERROR_IS_OK(b->req.out.result)) { + printf("DsBind failed - %s\n", win_errstr(b->req.out.result)); + ret = false; + } + + ZERO_STRUCT(b->peer_bind_info28); + if (b->req.out.bind_info) { + switch (b->req.out.bind_info->length) { + case 24: { + struct drsuapi_DsBindInfo24 *info24; + info24 = &b->req.out.bind_info->info.info24; + b->peer_bind_info28.supported_extensions= info24->supported_extensions; + b->peer_bind_info28.site_guid = info24->site_guid; + b->peer_bind_info28.pid = info24->pid; + b->peer_bind_info28.repl_epoch = 0; + break; + } + case 28: { + b->peer_bind_info28 = b->req.out.bind_info->info.info28; + break; + } + case 32: { + struct drsuapi_DsBindInfo32 *info32; + info32 = &b->req.out.bind_info->info.info32; + b->peer_bind_info28.supported_extensions= info32->supported_extensions; + b->peer_bind_info28.site_guid = info32->site_guid; + b->peer_bind_info28.pid = info32->pid; + b->peer_bind_info28.repl_epoch = info32->repl_epoch; + break; + } + case 48: { + struct drsuapi_DsBindInfo48 *info48; + info48 = &b->req.out.bind_info->info.info48; + b->peer_bind_info28.supported_extensions= info48->supported_extensions; + b->peer_bind_info28.site_guid = info48->site_guid; + b->peer_bind_info28.pid = info48->pid; + b->peer_bind_info28.repl_epoch = info48->repl_epoch; + break; + } + case 52: { + struct drsuapi_DsBindInfo52 *info52; + info52 = &b->req.out.bind_info->info.info52; + b->peer_bind_info28.supported_extensions= info52->supported_extensions; + b->peer_bind_info28.site_guid = info52->site_guid; + b->peer_bind_info28.pid = info52->pid; + b->peer_bind_info28.repl_epoch = info52->repl_epoch; + break; + } + default: + printf("DsBind - warning: unknown BindInfo length: %u\n", + b->req.out.bind_info->length); + } + } + + return ret; +} + + +static bool test_getinfo(struct torture_context *tctx, + struct DsGetinfoTest *ctx) +{ + NTSTATUS status; + struct dcerpc_pipe *p = ctx->admin.drsuapi.drs_pipe; + struct dcerpc_binding_handle *b = ctx->admin.drsuapi.drs_handle; + struct drsuapi_DsReplicaGetInfo r; + union drsuapi_DsReplicaGetInfoRequest req; + union drsuapi_DsReplicaInfo info; + enum drsuapi_DsReplicaInfoType info_type; + int i; + bool no_invalid_levels = true; + struct { + int32_t level; + int32_t infotype; + const char *obj_dn; + const char *attribute_name; + uint32_t flags; + } array[] = { + { + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_NEIGHBORS + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO, + .infotype = DRSUAPI_DS_REPLICA_INFO_PENDING_OPS + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS2 + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS3 + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2, + .obj_dn = "CN=Domain Admins,CN=Users,", + .flags = 0 + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2, + .obj_dn = "CN=Domain Admins,CN=Users,", + .flags = DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2 + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_REPSTO + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1 + },{ + .level = DRSUAPI_DS_REPLICA_GET_INFO2, + .infotype = DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS + } + }; + + ctx->domain_dn = torture_get_ldap_base_dn(tctx, p); + torture_assert(tctx, ctx->domain_dn != NULL, "Cannot get domain_dn"); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping DsReplicaGetInfo test against Samba4\n"); + return true; + } + + r.in.bind_handle = &ctx->admin.drsuapi.bind_handle; + r.in.req = &req; + + for (i=0; i < ARRAY_SIZE(array); i++) { + const char *object_dn; + + torture_comment(tctx, "Testing DsReplicaGetInfo level %d infotype %d\n", + array[i].level, array[i].infotype); + + if (array[i].obj_dn) { + object_dn = array[i].obj_dn; + if (object_dn[strlen(object_dn)-1] == ',') { + /* add the domain DN on the end */ + object_dn = talloc_asprintf(tctx, "%s%s", object_dn, ctx->domain_dn); + } + } else { + object_dn = ctx->domain_dn; + } + + r.in.level = array[i].level; + switch(r.in.level) { + case DRSUAPI_DS_REPLICA_GET_INFO: + r.in.req->req1.info_type = array[i].infotype; + r.in.req->req1.object_dn = object_dn; + ZERO_STRUCT(r.in.req->req1.source_dsa_guid); + break; + case DRSUAPI_DS_REPLICA_GET_INFO2: + r.in.req->req2.info_type = array[i].infotype; + r.in.req->req2.object_dn = object_dn; + ZERO_STRUCT(r.in.req->req2.source_dsa_guid); + r.in.req->req2.flags = 0; + r.in.req->req2.attribute_name = NULL; + r.in.req->req2.value_dn_str = NULL; + r.in.req->req2.enumeration_context = 0; + break; + } + + /* Construct a different request for some of the infoTypes */ + if (array[i].attribute_name != NULL) { + r.in.req->req2.attribute_name = array[i].attribute_name; + } + if (array[i].flags != 0) { + r.in.req->req2.flags |= array[i].flags; + } + + r.out.info = &info; + r.out.info_type = &info_type; + + status = dcerpc_drsuapi_DsReplicaGetInfo_r(b, tctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) { + torture_comment(tctx, + "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n", + array[i].level, array[i].infotype); + continue; + } + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "DsReplicaGetInfo level %d and/or infotype %d failed\n", + array[i].level, array[i].infotype)); + if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) { + /* this is a not yet supported level */ + torture_comment(tctx, + "DsReplicaGetInfo level %d and/or infotype %d not yet supported by server\n", + array[i].level, array[i].infotype); + no_invalid_levels = false; + continue; + } + + torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo"); + } + + return no_invalid_levels; +} + +/** + * DSGETINFO test case setup + */ +static bool torture_dsgetinfo_tcase_setup(struct torture_context *tctx, void **data) +{ + bool bret; + struct DsGetinfoTest *ctx; + + *data = ctx = test_create_context(tctx); + torture_assert(tctx, ctx, "test_create_context() failed"); + + bret = _test_DsBind(tctx, ctx, ctx->admin.credentials, &ctx->admin.drsuapi); + torture_assert(tctx, bret, "_test_DsBind() failed"); + + return true; +} + +/** + * DSGETINFO test case cleanup + */ +static bool torture_dsgetinfo_tcase_teardown(struct torture_context *tctx, void *data) +{ + struct DsGetinfoTest *ctx; + struct drsuapi_DsUnbind r; + struct policy_handle bind_handle; + + ctx = talloc_get_type(data, struct DsGetinfoTest); + + ZERO_STRUCT(r); + r.out.bind_handle = &bind_handle; + + /* Unbing admin handle */ + r.in.bind_handle = &ctx->admin.drsuapi.bind_handle; + if (ctx->admin.drsuapi.drs_handle) { + dcerpc_drsuapi_DsUnbind_r(ctx->admin.drsuapi.drs_handle, + ctx, &r); + } + + talloc_free(ctx); + + return true; +} + +/** + * DSGETINFO test case implementation + */ +void torture_drs_rpc_dsgetinfo_tcase(struct torture_suite *suite) +{ + typedef bool (*run_func) (struct torture_context *test, void *tcase_data); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "dsgetinfo"); + + torture_tcase_set_fixture(tcase, + torture_dsgetinfo_tcase_setup, + torture_dsgetinfo_tcase_teardown); + + torture_tcase_add_simple_test(tcase, "DsGetReplicaInfo", (run_func)test_getinfo); +} + diff --git a/source4/torture/rpc/dssetup.c b/source4/torture/rpc/dssetup.c new file mode 100644 index 0000000..9a61199 --- /dev/null +++ b/source4/torture/rpc/dssetup.c @@ -0,0 +1,64 @@ +/* + Unix SMB/CIFS implementation. + + test suite for dssetup rpc operations + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_dssetup_c.h" +#include "torture/rpc/torture_rpc.h" + + +bool test_DsRoleGetPrimaryDomainInformation_ext(struct torture_context *tctx, + struct dcerpc_pipe *p, + NTSTATUS ext_status) +{ + struct dssetup_DsRoleGetPrimaryDomainInformation r; + NTSTATUS status; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=DS_ROLE_BASIC_INFORMATION; i <= DS_ROLE_OP_STATUS; i++) { + r.in.level = i; + torture_comment(tctx, "dcerpc_dssetup_DsRoleGetPrimaryDomainInformation level %d\n", i); + + status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation_r(b, tctx, &r); + torture_assert_ntstatus_equal(tctx, ext_status, status, "DsRoleGetPrimaryDomainInformation failed"); + if (NT_STATUS_IS_OK(ext_status)) { + torture_assert_werr_ok(tctx, r.out.result, "DsRoleGetPrimaryDomainInformation failed"); + } + } + + return true; +} + +bool test_DsRoleGetPrimaryDomainInformation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_DsRoleGetPrimaryDomainInformation_ext(tctx, p, NT_STATUS_OK); +} + +struct torture_suite *torture_rpc_dssetup(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "dssetup"); + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, "dssetup", &ndr_table_dssetup); + + torture_rpc_tcase_add_test(tcase, "DsRoleGetPrimaryDomainInformation", test_DsRoleGetPrimaryDomainInformation); + + return suite; +} diff --git a/source4/torture/rpc/echo.c b/source4/torture/rpc/echo.c new file mode 100644 index 0000000..93fd408 --- /dev/null +++ b/source4/torture/rpc/echo.c @@ -0,0 +1,474 @@ +/* + Unix SMB/CIFS implementation. + test suite for echo rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2005 + Copyright (C) Jelmer Vernooij 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "lib/events/events.h" +#include "librpc/gen_ndr/ndr_echo_c.h" + + +/* + test the AddOne interface +*/ +#define TEST_ADDONE(tctx, value) do { \ + n = i = value; \ + r.in.in_data = n; \ + r.out.out_data = &n; \ + torture_assert_ntstatus_ok(tctx, dcerpc_echo_AddOne_r(b, tctx, &r), \ + talloc_asprintf(tctx, "AddOne(%d) failed", i)); \ + torture_assert (tctx, n == i+1, talloc_asprintf(tctx, "%d + 1 != %u (should be %u)\n", i, n, i+1)); \ + torture_comment (tctx, "%d + 1 = %u\n", i, n); \ +} while(0) + +static bool test_addone(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint32_t i; + uint32_t n; + struct echo_AddOne r; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0;i<10;i++) { + TEST_ADDONE(tctx, i); + } + + TEST_ADDONE(tctx, 0x7FFFFFFE); + TEST_ADDONE(tctx, 0xFFFFFFFE); + TEST_ADDONE(tctx, 0xFFFFFFFF); + TEST_ADDONE(tctx, random() & 0xFFFFFFFF); + return true; +} + +/* + test the EchoData interface +*/ +static bool test_echodata(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + uint8_t *data_in, *data_out; + int len; + struct echo_EchoData r; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 1 + (random() % 500); + } else { + len = 1 + (random() % 5000); + } + + data_in = talloc_array(tctx, uint8_t, len); + data_out = talloc_array(tctx, uint8_t, len); + for (i=0;ibinding_handle; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 100 + (random() % 500); + } else { + len = 200000 + (random() % 5000); + } + + r.in.len = len; + + torture_assert_ntstatus_ok(tctx, dcerpc_echo_SourceData_r(b, tctx, &r), + talloc_asprintf(tctx, "SourceData(%d) failed", len)); + + for (i=0;ibinding_handle; + + if (torture_setting_bool(tctx, "quick", false) && + (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) { + len = 100 + (random() % 5000); + } else { + len = 200000 + (random() % 5000); + } + + data_in = talloc_array(tctx, uint8_t, len); + for (i=0;ibinding_handle; + + r.in.s1 = "input string"; + r.out.s2 = &s; + + torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestCall_r(b, tctx, &r), + "TestCall failed"); + + torture_assert_str_equal(tctx, s, "input string", "Didn't receive back same string"); + + return true; +} + +/* + test the testcall interface +*/ +static bool test_testcall2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct echo_TestCall2 r; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=1;i<=7;i++) { + r.in.level = i; + r.out.info = talloc(tctx, union echo_Info); + + torture_comment(tctx, "Testing TestCall2 level %d\n", i); + torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestCall2_r(b, tctx, &r), + "TestCall2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "TestCall2 failed"); + } + return true; +} + +static void test_sleep_done(struct tevent_req *subreq) +{ + bool *done1 = (bool *)tevent_req_callback_data_void(subreq); + *done1 = true; +} + +/* + test the TestSleep interface +*/ +static bool test_sleep(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; +#define ASYNC_COUNT 3 + struct tevent_req *req[ASYNC_COUNT]; + struct echo_TestSleep r[ASYNC_COUNT]; + bool done1[ASYNC_COUNT]; + bool done2[ASYNC_COUNT]; + struct timeval snd[ASYNC_COUNT]; + struct timeval rcv[ASYNC_COUNT]; + struct timeval diff[ASYNC_COUNT]; + int total_done = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + struct dcerpc_pipe *p2 = NULL; + NTSTATUS status; + + if (torture_setting_bool(tctx, "quick", false)) { + torture_skip(tctx, "TestSleep disabled - use \"torture:quick=no\" to enable\n"); + } + torture_comment(tctx, "Testing TestSleep - use \"torture:quick=yes\" to disable\n"); + + transport = dcerpc_binding_get_transport(p->binding); + assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding); + + torture_comment(tctx, "connect echo connection 2 with " + "DCERPC_CONCURRENT_MULTIPLEX\n"); + status = torture_rpc_connection_transport(tctx, &p2, + &ndr_table_rpcecho, + transport, + assoc_group_id, + DCERPC_CONCURRENT_MULTIPLEX); + torture_assert_ntstatus_ok(tctx, status, "opening echo connection 2"); + b = p2->binding_handle; + + for (i=0;iev, b, &r[i]); + torture_assert(tctx, req[i], "Failed to send async sleep request\n"); + tevent_req_set_callback(req[i], test_sleep_done, &done1[i]); + } + + while (total_done < ASYNC_COUNT) { + torture_assert(tctx, tevent_loop_once(tctx->ev) == 0, + "Event context loop failed"); + for (i=0;ibinding_handle; + + r.in.foo1 = &v; + r.in.foo2 = &e2; + r.in.foo3 = &e3; + r.out.foo1 = &v; + r.out.foo2 = &e2; + r.out.foo3 = &e3; + + e2.e1 = 76; + e2.e2 = ECHO_ENUM1_32; + e3.e1 = ECHO_ENUM2; + + torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestEnum_r(b, tctx, &r), + "TestEnum failed"); + return true; +} + +/* + test surrounding conformant array handling +*/ +static bool test_surrounding(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct echo_TestSurrounding r; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + r.in.data = talloc(tctx, struct echo_Surrounding); + + r.in.data->x = 20; + r.in.data->surrounding = talloc_zero_array(tctx, uint16_t, r.in.data->x); + + r.out.data = talloc(tctx, struct echo_Surrounding); + + torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestSurrounding_r(b, tctx, &r), + "TestSurrounding failed"); + + torture_assert(tctx, r.out.data->x == 2 * r.in.data->x, + "TestSurrounding did not make the array twice as large"); + + return true; +} + +/* + test multiple levels of pointers +*/ +static bool test_doublepointer(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct echo_TestDoublePointer r; + uint16_t value = 12; + uint16_t *pvalue = &value; + uint16_t **ppvalue = &pvalue; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + r.in.data = &ppvalue; + + torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestDoublePointer_r(b, tctx, &r), + "TestDoublePointer failed"); + + torture_assert_int_equal(tctx, value, r.out.result, + "TestDoublePointer did not return original value"); + return true; +} + + +/* + test request timeouts +*/ +#if 0 /* this test needs fixing to work over ncacn_np */ +static bool test_timeout(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct rpc_request *req; + struct echo_TestSleep r; + int timeout_saved = p->request_timeout; + + if (torture_setting_bool(tctx, "quick", false)) { + torture_skip(tctx, "timeout testing disabled - use \"torture:quick=no\" to enable\n"); + } + + torture_comment(tctx, "testing request timeouts\n"); + r.in.seconds = 2; + p->request_timeout = 1; + + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + req->ignore_timeout = true; + + status = dcerpc_echo_TestSleep_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT, + "request should have timed out"); + + torture_comment(tctx, "testing request destruction\n"); + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + talloc_free(req); + + req = dcerpc_echo_TestSleep_send(p, tctx, &r); + if (!req) { + torture_comment(tctx, "Failed to send async sleep request\n"); + goto failed; + } + req->ignore_timeout = true; + status = dcerpc_echo_TestSleep_recv(req); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT, + "request should have timed out"); + + p->request_timeout = timeout_saved; + + return test_addone(tctx, p); + +failed: + p->request_timeout = timeout_saved; + return false; +} +#endif + +struct torture_suite *torture_rpc_echo(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "echo"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "echo", + &ndr_table_rpcecho); + + torture_rpc_tcase_add_test(tcase, "addone", test_addone); + torture_rpc_tcase_add_test(tcase, "sinkdata", test_sinkdata); + torture_rpc_tcase_add_test(tcase, "echodata", test_echodata); + torture_rpc_tcase_add_test(tcase, "sourcedata", test_sourcedata); + torture_rpc_tcase_add_test(tcase, "testcall", test_testcall); + torture_rpc_tcase_add_test(tcase, "testcall2", test_testcall2); + torture_rpc_tcase_add_test(tcase, "enum", test_enum); + torture_rpc_tcase_add_test(tcase, "surrounding", test_surrounding); + torture_rpc_tcase_add_test(tcase, "doublepointer", test_doublepointer); + torture_rpc_tcase_add_test(tcase, "sleep", test_sleep); +#if 0 /* this test needs fixing to work over ncacn_np */ + torture_rpc_tcase_add_test(tcase, "timeout", test_timeout); +#endif + + return suite; +} diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c new file mode 100644 index 0000000..d1202c2 --- /dev/null +++ b/source4/torture/rpc/epmapper.c @@ -0,0 +1,679 @@ +/* + Unix SMB/CIFS implementation. + test suite for epmapper rpc operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_epmapper_c.h" +#include "librpc/ndr/ndr_table.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "torture/rpc/torture_rpc.h" +#include "lib/util/util_net.h" +#include "librpc/rpc/rpc_common.h" + +/* + display any protocol tower + */ +static void display_tower(struct torture_context *tctx, struct epm_tower *twr) +{ + int i; + + for (i = 0; i < twr->num_floors; i++) { + torture_comment(tctx, + " %s", + epm_floor_string(tctx, &twr->floors[i])); + } + torture_comment(tctx, "\n"); +} + +static bool test_Insert(struct torture_context *tctx, + struct dcerpc_binding_handle *h, + struct ndr_syntax_id object, + const char *annotation, + const struct dcerpc_binding *b) +{ + struct epm_Insert r; + NTSTATUS status; + + r.in.num_ents = 1; + r.in.entries = talloc_array(tctx, struct epm_entry_t, 1); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "Skip Insert test against Samba4"); + } + + /* FIXME zero */ + ZERO_STRUCT(r.in.entries[0].object); + r.in.entries[0].annotation = annotation; + + r.in.entries[0].tower = talloc(tctx, struct epm_twr_t); + + status = dcerpc_binding_build_tower(tctx, + b, + &r.in.entries[0].tower->tower); + + torture_assert_ntstatus_ok(tctx, + status, + "Unable to build tower from binding struct"); + r.in.replace = 0; + + /* shoot! */ + status = dcerpc_epm_Insert_r(h, tctx, &r); + + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, + "epm_Insert failed - %s\n", + nt_errstr(status)); + return false; + } + + if (r.out.result != EPMAPPER_STATUS_OK) { + torture_comment(tctx, + "epm_Insert failed - internal error: 0x%.4x\n", + r.out.result); + return false; + } + + return true; +} + +static bool test_Delete(struct torture_context *tctx, + struct dcerpc_binding_handle *h, + const char *annotation, + const struct dcerpc_binding *b) +{ + NTSTATUS status; + struct epm_Delete r; + + r.in.num_ents = 1; + r.in.entries = talloc_array(tctx, struct epm_entry_t, 1); + + ZERO_STRUCT(r.in.entries[0].object); + r.in.entries[0].annotation = annotation; + + r.in.entries[0].tower = talloc(tctx, struct epm_twr_t); + + status = dcerpc_binding_build_tower(tctx, + b, + &r.in.entries[0].tower->tower); + + torture_assert_ntstatus_ok(tctx, + status, + "Unable to build tower from binding struct"); + r.in.num_ents = 1; + + status = dcerpc_epm_Delete_r(h, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, + "epm_Delete failed - %s\n", + nt_errstr(status)); + return false; + } + + if (r.out.result != EPMAPPER_STATUS_OK) { + torture_comment(tctx, + "epm_Delete failed - internal error: 0x%.4x\n", + r.out.result); + return false; + } + + return true; +} + +static bool test_Map_tcpip(struct torture_context *tctx, + struct dcerpc_binding_handle *h, + struct ndr_syntax_id map_syntax) +{ + struct epm_Map r; + struct GUID uuid; + struct policy_handle entry_handle; + struct ndr_syntax_id syntax; + struct dcerpc_binding *map_binding; + struct epm_twr_t map_tower; + struct epm_twr_p_t towers[20]; + struct epm_tower t; + uint32_t num_towers; + uint32_t port; + uint32_t i; + long int p; + const char *tmp; + const char *ip; + char *ptr; + NTSTATUS status; + + torture_comment(tctx, "Testing epm_Map\n"); + + ZERO_STRUCT(uuid); + ZERO_STRUCT(entry_handle); + + r.in.object = &uuid; + r.in.map_tower = &map_tower; + r.in.entry_handle = &entry_handle; + r.out.entry_handle = &entry_handle; + r.in.max_towers = 10; + r.out.towers = towers; + r.out.num_towers = &num_towers; + + /* Create map tower */ + status = dcerpc_parse_binding(tctx, "ncacn_ip_tcp:[135]", &map_binding); + torture_assert_ntstatus_ok(tctx, status, + "epm_Map_tcpip failed: can't create map_binding"); + + status = dcerpc_binding_set_abstract_syntax(map_binding, &map_syntax); + torture_assert_ntstatus_ok(tctx, status, + "epm_Map_tcpip failed: set map_syntax"); + + status = dcerpc_binding_build_tower(tctx, map_binding, + &map_tower.tower); + torture_assert_ntstatus_ok(tctx, status, + "epm_Map_tcpip failed: can't create map_tower"); + + torture_comment(tctx, + "epm_Map request for '%s':\n", + ndr_interface_name(&map_syntax.uuid, map_syntax.if_version)); + display_tower(tctx, &r.in.map_tower->tower); + + status = dcerpc_epm_Map_r(h, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "epm_Map_simple failed"); + torture_assert(tctx, r.out.result == EPMAPPER_STATUS_OK, + "epm_Map_tcpip failed: result is not EPMAPPER_STATUS_OK"); + + /* Check the result */ + t = r.out.towers[0].twr->tower; + + /* Check if we got the correct RPC interface identifier */ + dcerpc_floor_get_uuid_full(&t.floors[0], &syntax); + torture_assert(tctx, ndr_syntax_id_equal(&syntax, &map_syntax), + "epm_Map_tcpip failed: Interface identifier mismatch"); + + torture_comment(tctx, + "epm_Map_tcpip response for '%s':\n", + ndr_interface_name(&syntax.uuid, syntax.if_version)); + + dcerpc_floor_get_uuid_full(&t.floors[1], &syntax); + torture_assert(tctx, ndr_syntax_id_equal(&syntax, &ndr_transfer_syntax_ndr), + "epm_Map_tcpip failed: floor 2 is not NDR encoded"); + + torture_assert(tctx, t.floors[2].lhs.protocol == EPM_PROTOCOL_NCACN, + "epm_Map_tcpip failed: floor 3 is not NCACN_IP_TCP"); + + tmp = dcerpc_floor_get_rhs_data(tctx, &t.floors[3]); + p = strtol(tmp, &ptr, 10); + port = p & 0xffff; + torture_assert(tctx, port > 1024 && port < 65535, "epm_Map_tcpip failed"); + + ip = dcerpc_floor_get_rhs_data(tctx, &t.floors[4]); + torture_assert(tctx, is_ipaddress(ip), "epm_Map_tcpip failed"); + + for (i = 0; i < *r.out.num_towers; i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &t); + } + } + + return true; +} + +static bool test_Map_full(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const struct ndr_syntax_id obj = { + { 0x8a885d04, 0x1ceb, 0x11c9, {0x9f, 0xe8}, {0x08,0x00,0x2b,0x10,0x48,0x60} }, + 2 + }; + struct dcerpc_binding_handle *h = p->binding_handle; + const char *annotation = "SMBTORTURE"; + struct dcerpc_binding *b; + NTSTATUS status; + bool ok; + + status = dcerpc_parse_binding(tctx, "ncacn_ip_tcp:216.83.154.106[41768]", &b); + torture_assert_ntstatus_ok(tctx, + status, + "Unable to generate dcerpc_binding struct"); + status = dcerpc_binding_set_abstract_syntax(b, &obj); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_binding_set_abstract_syntax"); + + ok = test_Insert(tctx, h, obj, annotation, b); + torture_assert(tctx, ok, "test_Insert failed"); + + ok = test_Map_tcpip(tctx, h, obj); + torture_assert(tctx, ok, "test_Map_tcpip failed"); + + ok = test_Delete(tctx, h, annotation, b); + torture_assert(tctx, ok, "test_Delete failed"); + + return true; +} + +static bool test_Map_display(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct epm_entry_t *entry) + +{ + NTSTATUS status; + struct epm_twr_t *twr = entry->tower; + struct epm_Map r; + struct GUID uuid = entry->object; + struct policy_handle handle; + struct ndr_syntax_id syntax; + uint32_t num_towers; + uint32_t i; + + ZERO_STRUCT(handle); + + r.in.object = &uuid; + r.in.map_tower = twr; + r.in.entry_handle = &handle; + r.out.entry_handle = &handle; + r.in.max_towers = 10; + r.out.num_towers = &num_towers; + + dcerpc_floor_get_uuid_full(&twr->tower.floors[0], &syntax); + + torture_comment(tctx, + "epm_Map results for '%s':\n", + ndr_interface_name(&syntax.uuid, syntax.if_version)); + + status = dcerpc_epm_Map_r(b, tctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &r.out.towers[i].twr->tower); + } + } + } + + /* RPC protocol identifier */ + twr->tower.floors[2].lhs.protocol = EPM_PROTOCOL_NCACN; + twr->tower.floors[2].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[2].rhs.ncacn.minor_version = 0; + + /* Port address */ + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_TCP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.tcp.port = 0; + + /* Transport */ + twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_IP; + twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[4].rhs.ip.ipaddr = "0.0.0.0"; + + status = dcerpc_epm_Map_r(b, tctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_HTTP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.http.port = 0; + + status = dcerpc_epm_Map_r(b, tctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_UDP; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.udp.port = 0; + + status = dcerpc_epm_Map_r(b, tctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i=0;i<*r.out.num_towers;i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &r.out.towers[i].twr->tower); + } + } + } + + twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_SMB; + twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[3].rhs.smb.unc = ""; + + twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_NETBIOS; + twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0); + twr->tower.floors[4].rhs.netbios.name = ""; + + status = dcerpc_epm_Map_r(b, tctx, &r); + if (NT_STATUS_IS_OK(status) && r.out.result == 0) { + for (i = 0; i < *r.out.num_towers; i++) { + if (r.out.towers[i].twr) { + display_tower(tctx, &r.out.towers[i].twr->tower); + } + } + } + + /* FIXME: Extend to do other protocols as well (ncacn_unix_stream, ncalrpc) */ + + return true; +} + +static bool test_Map_simple(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_Lookup r; + struct policy_handle entry_handle; + uint32_t num_ents = 0; + struct dcerpc_binding_handle *h = p->binding_handle; + + ZERO_STRUCT(entry_handle); + + torture_comment(tctx, "Testing epm_Map\n"); + + /* get all elements */ + r.in.inquiry_type = RPC_C_EP_ALL_ELTS; + r.in.object = NULL; + r.in.interface_id = NULL; + r.in.vers_option = RPC_C_VERS_ALL; + + r.in.entry_handle = &entry_handle; + r.in.max_ents = 10; + + r.out.entry_handle = &entry_handle; + r.out.num_ents = &num_ents; + + do { + int i; + + status = dcerpc_epm_Lookup_r(h, tctx, &r); + if (!NT_STATUS_IS_OK(status) || + r.out.result != EPMAPPER_STATUS_OK) { + break; + } + + for (i = 0; i < *r.out.num_ents; i++) { + if (r.out.entries[i].tower->tower.num_floors == 5) { + test_Map_display(h, tctx, &r.out.entries[i]); + } + } + } while (NT_STATUS_IS_OK(status) && + r.out.result == EPMAPPER_STATUS_OK && + *r.out.num_ents == r.in.max_ents && + !ndr_policy_handle_empty(&entry_handle)); + + torture_assert_ntstatus_ok(tctx, status, "epm_Map_simple failed"); + + torture_assert(tctx, + ndr_policy_handle_empty(&entry_handle), + "epm_Map_simple failed - The policy handle should be empty."); + + return true; +} + +static bool test_LookupHandleFree(struct torture_context *tctx, + struct dcerpc_binding_handle *h, + struct policy_handle *entry_handle) { + NTSTATUS status; + struct epm_LookupHandleFree r; + + if (ndr_policy_handle_empty(entry_handle)) { + torture_comment(tctx, + "epm_LookupHandleFree failed - empty policy_handle\n"); + return false; + } + + r.in.entry_handle = entry_handle; + r.out.entry_handle = entry_handle; + + status = dcerpc_epm_LookupHandleFree_r(h, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, + "epm_LookupHandleFree failed - %s\n", + nt_errstr(status)); + return false; + } + + if (r.out.result != EPMAPPER_STATUS_OK) { + torture_comment(tctx, + "epm_LookupHandleFree failed - internal error: " + "0x%.4x\n", + r.out.result); + return false; + } + + return true; +} + +static bool test_Lookup_simple(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_Lookup r; + struct policy_handle entry_handle; + uint32_t num_ents = 0; + struct dcerpc_binding_handle *h = p->binding_handle; + + ZERO_STRUCT(entry_handle); + + torture_comment(tctx, "Testing epm_Lookup\n"); + + /* get all elements */ + r.in.inquiry_type = RPC_C_EP_ALL_ELTS; + r.in.object = NULL; + r.in.interface_id = NULL; + r.in.vers_option = RPC_C_VERS_ALL; + + r.in.entry_handle = &entry_handle; + r.in.max_ents = 10; + + r.out.entry_handle = &entry_handle; + r.out.num_ents = &num_ents; + + do { + int i; + + status = dcerpc_epm_Lookup_r(h, tctx, &r); + if (!NT_STATUS_IS_OK(status) || + r.out.result != EPMAPPER_STATUS_OK) { + break; + } + + torture_comment(tctx, + "epm_Lookup returned %d events, entry_handle: %s\n", + *r.out.num_ents, + GUID_string(tctx, &entry_handle.uuid)); + + for (i = 0; i < *r.out.num_ents; i++) { + torture_comment(tctx, + "\n Found '%s' Object[%s]\n", + r.out.entries[i].annotation, + GUID_string(tctx, &r.out.entries[i].object)); + + display_tower(tctx, &r.out.entries[i].tower->tower); + } + } while (NT_STATUS_IS_OK(status) && + r.out.result == EPMAPPER_STATUS_OK && + *r.out.num_ents == r.in.max_ents && + !ndr_policy_handle_empty(&entry_handle)); + + torture_assert_ntstatus_ok(tctx, status, "epm_Lookup failed"); + torture_assert(tctx, r.out.result == EPMAPPER_STATUS_NO_MORE_ENTRIES, "epm_Lookup failed"); + + torture_assert(tctx, + ndr_policy_handle_empty(&entry_handle), + "epm_Lookup failed - The policy handle should be empty."); + + return true; +} + +/* + * This test starts a epm_Lookup request, but doesn't finish the + * call terminates the search. So it will call epm_LookupHandleFree. + */ +static bool test_Lookup_terminate_search(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + bool ok; + NTSTATUS status; + struct epm_Lookup r; + struct policy_handle entry_handle; + uint32_t i, num_ents = 0; + struct dcerpc_binding_handle *h = p->binding_handle; + + ZERO_STRUCT(entry_handle); + + torture_comment(tctx, "Testing epm_Lookup and epm_LookupHandleFree\n"); + + /* get all elements */ + r.in.inquiry_type = RPC_C_EP_ALL_ELTS; + r.in.object = NULL; + r.in.interface_id = NULL; + r.in.vers_option = RPC_C_VERS_ALL; + + r.in.entry_handle = &entry_handle; + r.in.max_ents = 2; + + r.out.entry_handle = &entry_handle; + r.out.num_ents = &num_ents; + + status = dcerpc_epm_Lookup_r(h, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "epm_Lookup failed"); + torture_assert(tctx, r.out.result == EPMAPPER_STATUS_OK, "epm_Lookup failed"); + + torture_comment(tctx, + "epm_Lookup returned %d events, entry_handle: %s\n", + *r.out.num_ents, + GUID_string(tctx, &entry_handle.uuid)); + + for (i = 0; i < *r.out.num_ents; i++) { + torture_comment(tctx, + "\n Found '%s'\n", + r.out.entries[i].annotation); + } + + ok = test_LookupHandleFree(tctx, + h, + &entry_handle); + if (!ok) { + return false; + } + + return true; +} + +static bool test_Insert_noreplace(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + bool ok; + NTSTATUS status; + struct epm_Insert r; + struct dcerpc_binding *b; + struct dcerpc_binding_handle *h = p->binding_handle; + + torture_comment(tctx, "Testing epm_Insert(noreplace) and epm_Delete\n"); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "Skip Insert test against Samba4"); + } + + r.in.num_ents = 1; + r.in.entries = talloc_array(tctx, struct epm_entry_t, 1); + + ZERO_STRUCT(r.in.entries[0].object); + r.in.entries[0].annotation = "smbtorture endpoint"; + + status = dcerpc_parse_binding(tctx, "ncalrpc:[SMBTORTURE]", &b); + torture_assert_ntstatus_ok(tctx, + status, + "Unable to generate dcerpc_binding struct"); + + r.in.entries[0].tower = talloc(tctx, struct epm_twr_t); + + status = dcerpc_binding_build_tower(tctx, + b, + &r.in.entries[0].tower->tower); + torture_assert_ntstatus_ok(tctx, + status, + "Unable to build tower from binding struct"); + r.in.replace = 0; + + status = dcerpc_epm_Insert_r(h, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "epm_Insert failed"); + + torture_assert(tctx, r.out.result == 0, "epm_Insert failed"); + + ok = test_Delete(tctx, h, "smbtorture", b); + if (!ok) { + return false; + } + + return true; +} + +#if 0 +/* + * The MS-RPCE documentation states that this function isn't implemented and + * SHOULD NOT be called by a client. + */ +static bool test_InqObject(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct epm_InqObject r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.epm_object = talloc(tctx, struct GUID); + *r.in.epm_object = ndr_table_epmapper.syntax_id.uuid; + + status = dcerpc_epm_InqObject_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "InqObject failed"); + + return true; +} +#endif + +struct torture_suite *torture_rpc_epmapper(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "epmapper"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, + "epmapper", + &ndr_table_epmapper); + + /* This is a stack */ + torture_rpc_tcase_add_test(tcase, + "Map_simple", + test_Map_simple); + torture_rpc_tcase_add_test(tcase, + "Map_full", + test_Map_full); + torture_rpc_tcase_add_test(tcase, + "Lookup_simple", + test_Lookup_simple); + torture_rpc_tcase_add_test(tcase, + "Lookup_terminate_search", + test_Lookup_terminate_search); + torture_rpc_tcase_add_test(tcase, + "Insert_noreplace", + test_Insert_noreplace); + + return suite; +} + +/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */ diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c new file mode 100644 index 0000000..87dc6a2 --- /dev/null +++ b/source4/torture/rpc/eventlog.c @@ -0,0 +1,501 @@ +/* + Unix SMB/CIFS implementation. + test suite for eventlog rpc operations + + Copyright (C) Tim Potter 2003,2005 + Copyright (C) Jelmer Vernooij 2004 + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_eventlog.h" +#include "librpc/gen_ndr/ndr_eventlog_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + +#define TEST_BACKUP_NAME "samrtorturetest" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; + name->length = 2*strlen_m(s); + name->size = name->length; +} + +static bool get_policy_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct eventlog_OpenEventLogW r; + struct eventlog_OpenUnknown0 unknown0; + struct lsa_String logname, servername; + + unknown0.unknown0 = 0x005c; + unknown0.unknown1 = 0x0001; + + r.in.unknown0 = &unknown0; + init_lsa_String(&logname, "dns server"); + init_lsa_String(&servername, NULL); + r.in.logname = &logname; + r.in.servername = &servername; + r.in.major_version = 0x00000001; + r.in.minor_version = 0x00000001; + r.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_OpenEventLogW_r(b, tctx, &r), + "OpenEventLog failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "OpenEventLog failed"); + + return true; +} + + + +static bool test_GetNumRecords(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct eventlog_GetNumRecords r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + uint32_t number = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + ZERO_STRUCT(r); + r.in.handle = &handle; + r.out.number = &number; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_GetNumRecords_r(b, tctx, &r), + "GetNumRecords failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "GetNumRecords failed"); + torture_comment(tctx, "%d records\n", *r.out.number); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + return true; +} + +static bool test_ReadEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct eventlog_ReadEventLogW r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t sent_size = 0; + uint32_t real_size = 0; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + ZERO_STRUCT(r); + r.in.offset = 0; + r.in.handle = &handle; + r.in.flags = 0; + r.out.data = NULL; + r.out.sent_size = &sent_size; + r.out.real_size = &real_size; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r), + "ReadEventLog failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, + "ReadEventLog failed"); + + while (1) { + struct EVENTLOGRECORD rec; + enum ndr_err_code ndr_err; + uint32_t size = 0; + uint32_t pos = 0; + + /* Read first for number of bytes in record */ + + r.in.number_of_bytes = 0; + r.in.flags = EVENTLOG_BACKWARDS_READ|EVENTLOG_SEQUENTIAL_READ; + r.out.data = NULL; + r.out.sent_size = &sent_size; + r.out.real_size = &real_size; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r), + "ReadEventLogW failed"); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_END_OF_FILE)) { + /* FIXME: still need to decode then */ + break; + } + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_BUFFER_TOO_SMALL, + "ReadEventLog failed"); + + /* Now read the actual record */ + + r.in.number_of_bytes = *r.out.real_size; + r.out.data = talloc_array(tctx, uint8_t, r.in.number_of_bytes); + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r), + "ReadEventLogW failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "ReadEventLog failed"); + + /* Decode a user-marshalled record */ + size = IVAL(r.out.data, pos); + + while (size > 0) { + DATA_BLOB blob = data_blob_const( + r.out.data + pos, size); + dump_data(0, blob.data, blob.length); + + ndr_err = ndr_pull_struct_blob_all(&blob, tctx, &rec, + (ndr_pull_flags_fn_t)ndr_pull_EVENTLOGRECORD); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + torture_assert_ntstatus_ok(tctx, status, + "ReadEventLog failed parsing event log record"); + } + + NDR_PRINT_DEBUG(EVENTLOGRECORD, &rec); + + pos += size; + + if (pos + 4 > *r.out.sent_size) { + break; + } + + size = IVAL(r.out.data, pos); + } + + torture_assert_ntstatus_ok(tctx, r.out.result, + "ReadEventLog failed parsing event log record"); + + r.in.offset++; + } + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +static bool test_ReportEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_ReportEventW r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t record_number = 0; + time_t time_written = 0; + struct lsa_String servername, *strings; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + init_lsa_String(&servername, NULL); + + strings = talloc_array(tctx, struct lsa_String, 1); + init_lsa_String(&strings[0], "Currently tortured by samba 4"); + + ZERO_STRUCT(r); + + r.in.handle = &handle; + r.in.timestamp = time(NULL); + r.in.event_type = EVENTLOG_INFORMATION_TYPE; + r.in.event_category = 0; + r.in.event_id = 0; + r.in.num_of_strings = 1; + r.in.data_size = 0; + r.in.servername = &servername; + r.in.user_sid = NULL; + r.in.strings = &strings; + r.in.data = NULL; + r.in.flags = 0; + r.in.record_number = r.out.record_number = &record_number; + r.in.time_written = r.out.time_written = &time_written; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_ReportEventW_r(b, tctx, &r), + "ReportEventW failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "ReportEventW failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +static bool test_FlushEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_FlushEventLog r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + r.in.handle = &handle; + + /* Huh? Does this RPC always return access denied? */ + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_FlushEventLog_r(b, tctx, &r), + "FlushEventLog failed"); + + torture_assert_ntstatus_equal(tctx, + r.out.result, + NT_STATUS_ACCESS_DENIED, + "FlushEventLog failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +static bool test_ClearEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_ClearEventLogW r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + r.in.handle = &handle; + r.in.backupfile = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_ClearEventLogW_r(b, tctx, &r), + "ClearEventLog failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "ClearEventLog failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +static bool test_GetLogInformation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct eventlog_GetLogInformation r; + struct eventlog_CloseEventLog cr; + struct policy_handle handle; + uint32_t bytes_needed = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + r.in.handle = &handle; + r.in.level = 1; + r.in.buf_size = 0; + r.out.buffer = NULL; + r.out.bytes_needed = &bytes_needed; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r), + "GetLogInformation failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_LEVEL, + "GetLogInformation failed"); + + r.in.level = 0; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r), + "GetLogInformation failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_BUFFER_TOO_SMALL, + "GetLogInformation failed"); + + r.in.buf_size = bytes_needed; + r.out.buffer = talloc_array(tctx, uint8_t, bytes_needed); + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r), + "GetLogInformation failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "GetLogInformation failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + + +static bool test_OpenEventLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle handle; + struct eventlog_CloseEventLog cr; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +static bool test_BackupLog(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle handle, backup_handle; + struct eventlog_BackupEventLogW r; + struct eventlog_OpenBackupEventLogW br; + struct eventlog_CloseEventLog cr; + const char *tmp; + struct lsa_String backup_filename; + struct eventlog_OpenUnknown0 unknown0; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skipping BackupLog test against samba"); + } + + if (!get_policy_handle(tctx, b, &handle)) + return false; + + tmp = talloc_asprintf(tctx, "C:\\%s", TEST_BACKUP_NAME); + init_lsa_String(&backup_filename, tmp); + + r.in.handle = &handle; + r.in.backup_filename = &backup_filename; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r), + "BackupEventLogW failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, + NT_STATUS_OBJECT_PATH_SYNTAX_BAD, "BackupEventLogW failed"); + + tmp = talloc_asprintf(tctx, "\\??\\C:\\%s", TEST_BACKUP_NAME); + init_lsa_String(&backup_filename, tmp); + + r.in.handle = &handle; + r.in.backup_filename = &backup_filename; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r), + "BackupEventLogW failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "BackupEventLogW failed"); + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r), + "BackupEventLogW failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, + NT_STATUS_OBJECT_NAME_COLLISION, "BackupEventLogW failed"); + + cr.in.handle = cr.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "BackupLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "BackupLog failed"); + + unknown0.unknown0 = 0x005c; + unknown0.unknown1 = 0x0001; + + br.in.unknown0 = &unknown0; + br.in.backup_logname = &backup_filename; + br.in.major_version = 1; + br.in.minor_version = 1; + br.out.handle = &backup_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_OpenBackupEventLogW_r(b, tctx, &br), + "OpenBackupEventLogW failed"); + + torture_assert_ntstatus_ok(tctx, br.out.result, "OpenBackupEventLogW failed"); + + cr.in.handle = cr.out.handle = &backup_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr), + "CloseEventLog failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, + "CloseEventLog failed"); + + return true; +} + +struct torture_suite *torture_rpc_eventlog(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + suite = torture_suite_create(mem_ctx, "eventlog"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "eventlog", + &ndr_table_eventlog); + + torture_rpc_tcase_add_test(tcase, "OpenEventLog", test_OpenEventLog); + test = torture_rpc_tcase_add_test(tcase, "ClearEventLog", + test_ClearEventLog); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "GetNumRecords", test_GetNumRecords); + torture_rpc_tcase_add_test(tcase, "ReadEventLog", test_ReadEventLog); + torture_rpc_tcase_add_test(tcase, "ReportEventLog", test_ReportEventLog); + torture_rpc_tcase_add_test(tcase, "FlushEventLog", test_FlushEventLog); + torture_rpc_tcase_add_test(tcase, "GetLogIntormation", test_GetLogInformation); + torture_rpc_tcase_add_test(tcase, "BackupLog", test_BackupLog); + + return suite; +} diff --git a/source4/torture/rpc/forest_trust.c b/source4/torture/rpc/forest_trust.c new file mode 100644 index 0000000..e7b641e --- /dev/null +++ b/source4/torture/rpc/forest_trust.c @@ -0,0 +1,914 @@ +/* + Unix SMB/CIFS implementation. + test suite for forest trust + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004-2005 + Copyright (C) Sumit Bose 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "libcli/security/security.h" +#include "libcli/auth/credentials.h" +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + +#include +#include + +#undef strcasecmp + +#define TEST_DOM "torturedom" +#define TEST_DOM_DNS "torturedom.samba.example.com" +#define TEST_DOM_SID "S-1-5-21-97398-379795-10000" +#define TEST_MACHINE_NAME "lsatestmach" + + +static bool test_get_policy_handle(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t access_mask, + struct policy_handle **handle ) +{ + struct policy_handle *h; + struct lsa_OpenPolicy2 pr; + struct lsa_ObjectAttribute attr; + NTSTATUS status; + + h = talloc(tctx, struct policy_handle); + torture_assert(tctx, h != NULL, "talloc(tctx, struct policy_handle)"); + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = NULL; + + pr.in.system_name = "\\"; + pr.in.attr = &attr; + pr.in.access_mask = access_mask; + pr.out.handle = h; + + status = dcerpc_lsa_OpenPolicy2_r(p->binding_handle, tctx, &pr); + torture_assert_ntstatus_ok(tctx, status, "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, pr.out.result, "OpenPolicy2 failed"); + + *handle = h; + return true; +} + +static bool test_create_trust_and_set_info(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *trust_name, + const char *trust_name_dns, + struct dom_sid *domsid, + struct lsa_TrustDomainInfoAuthInfoInternal *authinfo) +{ + struct policy_handle *handle; + struct lsa_lsaRSetForestTrustInformation fti; + struct lsa_ForestTrustCollisionInfo *collision_info = NULL; + struct lsa_Close cr; + struct policy_handle closed_handle; + struct lsa_CreateTrustedDomainEx2 r; + struct lsa_TrustDomainInfoInfoEx trustinfo; + struct policy_handle trustdom_handle; + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info = NULL; + + if (!test_get_policy_handle(tctx, p, + (LSA_POLICY_VIEW_LOCAL_INFORMATION | + LSA_POLICY_TRUST_ADMIN | + LSA_POLICY_CREATE_SECRET), &handle)) { + return false; + } + + torture_comment(tctx, "\nTesting CreateTrustedDomainEx2\n"); + + trustinfo.sid = domsid; + trustinfo.netbios_name.string = trust_name; + trustinfo.domain_name.string = trust_name_dns; + + trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND | + LSA_TRUST_DIRECTION_OUTBOUND; + + trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL; + + /* + * MS-LSAD: Section 3.1.4.7.10 makes it clear that Win2k3 + * functional level and above return + * NT_STATUS_INVALID_DOMAIN_STATE if + * TRUST_ATTRIBUTE_FOREST_TRANSITIVE or + * TRUST_ATTRIBUTE_CROSS_ORGANIZATION is set here. + * + * But we really want to test forest trusts here. + */ + trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE; + + r.in.policy_handle = handle; + r.in.info = &trustinfo; + r.in.auth_info_internal = authinfo; + /* LSA_TRUSTED_QUERY_DOMAIN_NAME is needed for for following + * QueryTrustedDomainInfo call, although it seems that Windows does not + * expect this */ + r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_QUERY_DOMAIN_NAME; + r.out.trustdom_handle = &trustdom_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_CreateTrustedDomainEx2_r(p->binding_handle, tctx, &r), + "CreateTrustedDomainEx2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "CreateTrustedDomainEx2 failed"); + + q.in.trustdom_handle = &trustdom_handle; + q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_QueryTrustedDomainInfo_r(p->binding_handle, tctx, &q), + "QueryTrustedDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, q.out.result, "QueryTrustedDomainInfo level 1"); + torture_assert(tctx, q.out.info != NULL, "QueryTrustedDomainInfo level 1 failed to return an info pointer"); + torture_assert_str_equal(tctx, info->info_ex.netbios_name.string, + trustinfo.netbios_name.string, + "QueryTrustedDomainInfo returned inconsistent short name"); + torture_assert_int_equal(tctx, info->info_ex.trust_type, trustinfo.trust_type, + "QueryTrustedDomainInfo returned incorrect trust type"); + torture_assert_int_equal(tctx, info->info_ex.trust_attributes, trustinfo.trust_attributes, + "QueryTrustedDomainInfo of returned incorrect trust attributes"); + torture_assert_int_equal(tctx, info->info_ex.trust_direction, trustinfo.trust_direction, + "QueryTrustedDomainInfo of returned incorrect trust direction"); + + fti.in.handle = handle; + fti.in.trusted_domain_name = talloc_zero(tctx, struct lsa_StringLarge); + fti.in.trusted_domain_name->string = trust_name_dns; + fti.in.highest_record_type = 2; + fti.in.forest_trust_info = talloc_zero(tctx, struct lsa_ForestTrustInformation); + fti.in.forest_trust_info->count = 2; + fti.in.forest_trust_info->entries = talloc_array(tctx, struct lsa_ForestTrustRecord *, 2); + fti.in.forest_trust_info->entries[0] = talloc_zero(tctx, struct lsa_ForestTrustRecord); + fti.in.forest_trust_info->entries[0]->flags = 0; + fti.in.forest_trust_info->entries[0]->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME; + fti.in.forest_trust_info->entries[0]->time = 0; + fti.in.forest_trust_info->entries[0]->forest_trust_data.top_level_name.string = trust_name_dns; + fti.in.forest_trust_info->entries[1] = talloc_zero(tctx, struct lsa_ForestTrustRecord); + fti.in.forest_trust_info->entries[1]->flags = 0; + fti.in.forest_trust_info->entries[1]->type = LSA_FOREST_TRUST_DOMAIN_INFO; + fti.in.forest_trust_info->entries[1]->time = 0; + fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.domain_sid = domsid; + fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.dns_domain_name.string = trust_name_dns; + fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.netbios_domain_name.string = trust_name; + fti.in.check_only = 0; + fti.out.collision_info = &collision_info; + + torture_comment(tctx, "\nTesting SetForestTrustInformation\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_lsaRSetForestTrustInformation_r(p->binding_handle, tctx, &fti), + "lsaRSetForestTrustInformation failed"); + torture_assert_ntstatus_ok(tctx, fti.out.result, "lsaRSetForestTrustInformation failed"); + + cr.in.handle = handle; + cr.out.handle = &closed_handle; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr), + "Close failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed"); + + return true; +} + +struct get_set_info { + enum lsa_TrustDomInfoEnum info_level; + NTSTATUS get_result; + NTSTATUS set_result; +}; + +static bool get_and_set_info(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *name) +{ + struct policy_handle *handle; + NTSTATUS status; + struct lsa_QueryTrustedDomainInfoByName qr; + struct lsa_SetTrustedDomainInfoByName sr; + union lsa_TrustedDomainInfo *info; + struct lsa_Close cr; + struct policy_handle closed_handle; + size_t c; + + struct get_set_info il[] = { + {LSA_TRUSTED_DOMAIN_INFO_NAME, NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER}, + /* {LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + {LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET, NT_STATUS_OK, NT_STATUS_OK}, + /* {LSA_TRUSTED_DOMAIN_INFO_PASSWORD, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + /* {LSA_TRUSTED_DOMAIN_INFO_BASIC, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + {LSA_TRUSTED_DOMAIN_INFO_INFO_EX, NT_STATUS_OK, NT_STATUS_OK}, + /* {LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO, NT_STATUS_OK, NT_STATUS_OK}, + /* {LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + /* {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + /* {LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */ + {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL, NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER}, + {LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES, NT_STATUS_OK, NT_STATUS_OK}, + { .info_level = -1, }, + }; + + torture_comment(tctx, "\nGetting/Setting dom info\n"); + + if(!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION, + &handle)) { + return false; + } + + qr.in.handle = handle; + qr.in.trusted_domain = talloc_zero(tctx, struct lsa_String); + qr.in.trusted_domain->string = name; + qr.out.info = &info; + + sr.in.handle = handle; + sr.in.trusted_domain = talloc_zero(tctx, struct lsa_String); + sr.in.trusted_domain->string = name; + sr.in.info = info; + + for (c = 0; il[c].info_level != -1; c++) { + torture_comment(tctx, "\nGetting/Setting dom info [%d]\n",il[c].info_level); + + qr.in.level = il[c].info_level; + status = dcerpc_lsa_QueryTrustedDomainInfoByName_r(p->binding_handle, + tctx, &qr); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "QueryTrustedDomainInfoByName failed"); + if (!NT_STATUS_EQUAL(qr.out.result, il[c].get_result)) { + torture_comment(tctx, "QueryTrustedDomainInfoByName did not return " + "%s but %s\n", + nt_errstr(il[c].get_result), + nt_errstr(qr.out.result)); + + /* We may be testing a server without support for this level */ + if (qr.in.level == LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES && NT_STATUS_EQUAL(qr.out.result, NT_STATUS_INVALID_PARAMETER)) { + return true; + } + return false; + } + + sr.in.level = il[c].info_level; + sr.in.info = info; + status = dcerpc_lsa_SetTrustedDomainInfoByName_r(p->binding_handle, + tctx, &sr); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "SetTrustedDomainInfoByName failed"); + if (!NT_STATUS_EQUAL(sr.out.result, il[c].set_result)) { + torture_comment(tctx, "SetTrustedDomainInfoByName did not return " + "%s but %s\n", + nt_errstr(il[c].set_result), + nt_errstr(sr.out.result)); + return false; + } + } + + cr.in.handle = handle; + cr.out.handle = &closed_handle; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr), + "Close failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed"); + + return true; +} + +static bool check_name(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *name) +{ + struct policy_handle *handle; + NTSTATUS status; + struct lsa_QueryTrustedDomainInfoByName qr; + union lsa_TrustedDomainInfo *info; + struct lsa_Close cr; + struct policy_handle closed_handle; + + torture_comment(tctx, "\nGetting LSA_TRUSTED_DOMAIN_INFO_FULL_INFO\n"); + + if(!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION, + &handle)) { + return false; + } + + qr.in.handle = handle; + qr.in.trusted_domain = talloc_zero(tctx, struct lsa_String); + qr.in.trusted_domain->string = name; + qr.in.level = LSA_TRUSTED_DOMAIN_INFO_FULL_INFO; + qr.out.info = &info; + status = dcerpc_lsa_QueryTrustedDomainInfoByName_r(p->binding_handle, + tctx, &qr); + torture_assert_ntstatus_ok(tctx, status, + "QueryInfoPolicy2 failed"); + torture_assert_ntstatus_equal(tctx, qr.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, + "QueryInfoPolicy2 did not return " + "NT_STATUS_OBJECT_NAME_NOT_FOUND"); + + cr.in.handle = handle; + cr.out.handle = &closed_handle; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr), + "Close failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed"); + + return true; +} + +static bool get_lsa_policy_info_dns(struct dcerpc_pipe *p, + struct torture_context *tctx, + union lsa_PolicyInformation **info) +{ + struct policy_handle *handle; + NTSTATUS status; + struct lsa_QueryInfoPolicy2 qr; + struct lsa_Close cr; + struct policy_handle closed_handle; + + torture_comment(tctx, "\nGetting LSA_POLICY_INFO_DNS\n"); + + if (!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION, + &handle)) { + return false; + } + + qr.in.handle = handle; + qr.in.level = LSA_POLICY_INFO_DNS; + qr.out.info = info; + status = dcerpc_lsa_QueryInfoPolicy2_r(p->binding_handle, tctx, &qr); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "QueryInfoPolicy2 failed"); + if (!NT_STATUS_IS_OK(qr.out.result)) { + torture_comment(tctx, "QueryInfoPolicy2 failed - %s\n", + nt_errstr(qr.out.result)); + return false; + } + + cr.in.handle = handle; + cr.out.handle = &closed_handle; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr), + "Close failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed"); + + return true; +} + +static bool delete_trusted_domain_by_sid(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct dom_sid *domsid) +{ + struct policy_handle *handle; + struct lsa_Close cr; + struct policy_handle closed_handle; + struct lsa_DeleteTrustedDomain dr; + + torture_comment(tctx, "\nDeleting trusted domain.\n"); + + /* Against a windows server it was sufficient to have + * LSA_POLICY_VIEW_LOCAL_INFORMATION although the documentations says + * otherwise. */ + if (!test_get_policy_handle(tctx, p, LSA_POLICY_TRUST_ADMIN, + &handle)) { + return false; + } + + dr.in.handle = handle; + dr.in.dom_sid = domsid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_DeleteTrustedDomain_r(p->binding_handle, tctx, &dr), + "DeleteTrustedDomain failed"); + torture_assert_ntstatus_ok(tctx, dr.out.result, "DeleteTrustedDomain failed"); + + cr.in.handle = handle; + cr.out.handle = &closed_handle; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr), + "Close failed"); + torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed"); + + return true; +} + +/* +static const uint8_t my_blob[] = { +0xa3,0x0b,0x32,0x45,0x8b,0x84,0x3b,0x01,0x68,0xe8,0x2b,0xbb,0x00,0x13,0x69,0x1f, +0x10,0x35,0x72,0xa9,0x4f,0x77,0xb7,0xeb,0x59,0x08,0x07,0xc3,0xe8,0x17,0x00,0xc5, +0xf2,0xa9,0x6d,0xb7,0x69,0x45,0x63,0x20,0xcb,0x44,0x44,0x22,0x02,0xe3,0x28,0x84, +0x9b,0xd5,0x43,0x6f,0x8d,0x36,0x9b,0x9b,0x3b,0x31,0x86,0x84,0x8b,0xf2,0x36,0xd4, +0xe8,0xc4,0xee,0x90,0x0c,0xcb,0x3e,0x11,0x2f,0x86,0xfe,0x87,0x6d,0xce,0xae,0x0c, +0x83,0xfb,0x21,0x22,0x6d,0x7f,0x5e,0x08,0x71,0x1a,0x35,0xf4,0x5a,0x76,0x9b,0xf7, +0x54,0x62,0xa5,0x4c,0xcd,0xf6,0xa5,0xb0,0x0b,0xc7,0x79,0xe1,0x6f,0x85,0x16,0x6f, +0x82,0xdd,0x15,0x11,0x4c,0x9d,0x26,0x01,0x74,0x7e,0xbb,0xec,0x88,0x1d,0x71,0x9e, +0x5f,0xb2,0x9c,0xab,0x66,0x20,0x08,0x3d,0xae,0x07,0x2d,0xbb,0xa6,0xfb,0xec,0xcc, +0x51,0x58,0x48,0x47,0x38,0x3b,0x47,0x66,0xe8,0x17,0xfa,0x54,0x5c,0x95,0x73,0x29, +0xdf,0x7e,0x4a,0xb4,0x45,0x30,0xf7,0xbf,0xc0,0x56,0x6d,0x80,0xf6,0x11,0x56,0x93, +0xeb,0x97,0xd5,0x10,0xd6,0xd6,0xf7,0x23,0xc3,0xc0,0x93,0xa7,0x5c,0xa9,0xc0,0x81, +0x55,0x3d,0xec,0x03,0x31,0x7e,0x9d,0xf9,0xd0,0x9e,0xb5,0xc7,0xef,0xa8,0x54,0xf6, +0x9c,0xdc,0x0d,0xd4,0xd7,0xee,0x8d,0x5f,0xbd,0x89,0x48,0x3b,0x63,0xff,0xe8,0xca, +0x10,0x64,0x61,0xdf,0xfd,0x50,0xff,0x51,0xa0,0x2c,0xd7,0x8a,0xf1,0x13,0x02,0x02, +0x71,0xe9,0xff,0x0d,0x03,0x48,0xf8,0x08,0x8d,0xd5,0xe6,0x31,0x9f,0xf0,0x26,0x07, +0x91,0x6d,0xd3,0x01,0x91,0x92,0xc7,0x28,0x18,0x58,0xd8,0xf6,0x1b,0x97,0x8d,0xd0, +0xd2,0xa1,0x7c,0xae,0xc1,0xca,0xfe,0x20,0x91,0x1c,0x4d,0x15,0x89,0x29,0x37,0xd5, +0xf5,0xca,0x40,0x2b,0x03,0x8f,0x7b,0xc2,0x10,0xb4,0xd3,0xe8,0x14,0xb0,0x9b,0x5d, +0x85,0x30,0xe5,0x13,0x24,0xf7,0x78,0xec,0xbe,0x0b,0x9a,0x3f,0xb5,0x76,0xd9,0x0d, +0x49,0x64,0xa4,0xa7,0x33,0x88,0xdd,0xe9,0xe2,0x5f,0x04,0x51,0xdd,0x89,0xe2,0x68, +0x5b,0x5f,0x64,0x35,0xe3,0x23,0x4a,0x0e,0x09,0x15,0xcc,0x97,0x47,0xf4,0xc2,0x4f, +0x06,0xc3,0x96,0xa9,0x2f,0xb3,0xde,0x29,0x10,0xc7,0xf5,0x16,0xc5,0x3c,0x84,0xd2, +0x9b,0x6b,0xaa,0x54,0x59,0x8d,0x94,0xde,0xd1,0x75,0xb6,0x08,0x0d,0x7d,0xf1,0x18, +0xc8,0xf5,0xdf,0xaa,0xcd,0xec,0xab,0xb6,0xd1,0xcb,0xdb,0xe7,0x75,0x5d,0xbe,0x76, +0xea,0x1d,0x01,0xc8,0x0b,0x2d,0x32,0xe9,0xa8,0x65,0xbb,0x4a,0xcb,0x72,0xbc,0xda, +0x04,0x7f,0x82,0xfb,0x04,0xeb,0xd8,0xe1,0xb9,0xb1,0x1e,0xdc,0xb3,0x60,0xf3,0x55, +0x1e,0xcf,0x90,0x6a,0x15,0x74,0x4d,0xff,0xb4,0xc7,0xc9,0xc2,0x4f,0x67,0x9e,0xeb, +0x00,0x61,0x02,0xe3,0x9e,0x59,0x88,0x20,0xf1,0x0c,0xbe,0xe0,0x26,0x69,0x63,0x67, +0x72,0x3c,0x06,0x00,0x9e,0x4f,0xc7,0xa6,0x4d,0x6c,0xbe,0x68,0x8e,0xf4,0x32,0x36, +0x2e,0x5f,0xa6,0xcf,0xa7,0x19,0x40,0x2b,0xbd,0xa2,0x22,0x73,0xc4,0xb6,0xe3,0x86, +0x64,0xeb,0xb1,0xc7,0x45,0x7d,0xd6,0xd9,0x36,0xf1,0x04,0xd4,0x61,0xdc,0x41,0xb7, +0x01,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, 0x30,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x02,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x34,0x00, +0x35,0x00,0x36,0x00,0x37,0x00,0x38,0x00,0x39,0x00,0x30,0x00,0x01,0x00,0x00,0x00, +0x0c,0x00,0x00,0x00, 0x30,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, +0x14,0x00,0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x34,0x00,0x35,0x00,0x36,0x00, +0x37,0x00,0x38,0x00,0x39,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00 +}; +*/ +static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx, + const char *password, + DATA_BLOB *auth_blob) +{ + struct trustDomainPasswords auth_struct; + struct AuthenticationInformation *auth_info_array; + enum ndr_err_code ndr_err; + size_t converted_size; + + generate_random_buffer(auth_struct.confounder, + sizeof(auth_struct.confounder)); + + auth_info_array = talloc_array(mem_ctx, + struct AuthenticationInformation, 1); + if (auth_info_array == NULL) { + return false; + } + + auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR; + if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password, + strlen(password), + &auth_info_array[0].AuthInfo.clear.password, + &converted_size)) { + return false; + } + + auth_info_array[0].AuthInfo.clear.size = converted_size; + + auth_struct.outgoing.count = 1; + auth_struct.outgoing.current.count = 1; + auth_struct.outgoing.current.array = auth_info_array; + auth_struct.outgoing.previous.count = 0; + auth_struct.outgoing.previous.array = NULL; + + auth_struct.incoming.count = 1; + auth_struct.incoming.current.count = 1; + auth_struct.incoming.current.array = auth_info_array; + auth_struct.incoming.previous.count = 0; + auth_struct.incoming.previous.array = NULL; + + ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct, + (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + + return true; +} + +static bool test_validate_trust(struct torture_context *tctx, + const char *binding, + const char *trusting_dom_name, + const char *trusting_dom_dns_name, + const char *trusted_dom_name, + const char *trusted_dom_dns_name, + const char *trust_password) +{ + struct netr_ServerGetTrustInfo r; + + struct netr_Authenticator a; + struct netr_Authenticator return_authenticator; + struct samr_Password new_owf_password; + struct samr_Password old_owf_password; + struct netr_TrustInfo *trust_info; + + struct netlogon_creds_CredentialState *creds; + + NTSTATUS status; + struct cli_credentials *credentials; + struct dcerpc_binding *b; + struct dcerpc_pipe *p1 = NULL; + struct dcerpc_pipe *p = NULL; + + struct netr_GetForestTrustInformation fr; + struct lsa_ForestTrustInformation *forest_trust_info; + struct lsa_ForestTrustRecord *tln = NULL; + struct lsa_ForestTrustRecord *di = NULL; + int i; + struct samr_Password *new_nt_hash; + struct samr_Password *old_nt_hash; + char *dummy; + uint32_t trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE; + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + credentials = cli_credentials_init(tctx); + torture_assert(tctx, credentials != NULL, "cli_credentials_init()"); + + dummy = talloc_asprintf(tctx, "%s$", trusted_dom_name); + cli_credentials_set_username(credentials, dummy, + CRED_SPECIFIED); + cli_credentials_set_domain(credentials, trusting_dom_name, + CRED_SPECIFIED); + cli_credentials_set_realm(credentials, trusting_dom_dns_name, + CRED_SPECIFIED); + cli_credentials_set_password(credentials, trust_password, CRED_SPECIFIED); + cli_credentials_set_old_password(credentials, trust_password, CRED_SPECIFIED); + cli_credentials_set_workstation(credentials, + trusted_dom_name, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(credentials, SEC_CHAN_DOMAIN); + + status = dcerpc_pipe_connect_b(tctx, &p1, b, + &ndr_table_netlogon, credentials, + tctx->ev, tctx->lp_ctx); + + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, "Failed to connect to remote server: %s with %s - %s\n", + binding, + cli_credentials_get_unparsed_name(credentials, tctx), + nt_errstr(status)); + return false; + } + + if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + credentials, &creds)) { + torture_comment(tctx, "test_SetupCredentials3 failed.\n"); + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + torture_comment(tctx, "test_SetupCredentialsPipe failed.\n"); + return false; + } + + netlogon_creds_client_authenticator(creds, &a); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", trusted_dom_name); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(credentials); + r.in.computer_name = trusted_dom_name; + r.in.credential = &a; + + r.out.return_authenticator = &return_authenticator; + r.out.new_owf_password = &new_owf_password; + r.out.old_owf_password = &old_owf_password; + r.out.trust_info = &trust_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_ServerGetTrustInfo_r(p->binding_handle, tctx, &r), + "ServerGetTrustInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "ServerGetTrustInfo failed"); + + torture_assert(tctx, trust_info != NULL, "ServerGetTrustInfo got no trust_info"); + torture_assert_int_equal(tctx, trust_info->count, 1, + "Unexpected number of results"); + torture_assert_int_equal(tctx, trust_info->data[0], trust_attributes, + "Unexpected trust_attributes"); + + new_nt_hash = cli_credentials_get_nt_hash(credentials, tctx); + torture_assert(tctx, new_nt_hash != NULL, "cli_credentials_get_nt_hash()"); + + old_nt_hash = cli_credentials_get_old_nt_hash(credentials, tctx); + torture_assert(tctx, old_nt_hash != NULL, "cli_credentials_get_old_nt_hash()"); + + netlogon_creds_des_decrypt(creds, &new_owf_password); + netlogon_creds_des_decrypt(creds, &old_owf_password); + + dump_data(1, new_owf_password.hash, 16); + dump_data(1, new_nt_hash->hash, 16); + dump_data(1, old_owf_password.hash, 16); + dump_data(1, old_nt_hash->hash, 16); + + torture_assert_mem_equal(tctx, new_owf_password.hash, new_nt_hash->hash, 16, + "received unexpected new owf password\n"); + + torture_assert_mem_equal(tctx, old_owf_password.hash, old_nt_hash->hash, 16, + "received unexpected old owf password\n"); + + netlogon_creds_client_authenticator(creds, &a); + + fr.in.server_name = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + fr.in.computer_name = trusted_dom_name; + fr.in.credential = &a; + fr.in.flags = 0; + fr.out.return_authenticator = &return_authenticator; + fr.out.forest_trust_info = &forest_trust_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_GetForestTrustInformation_r(p->binding_handle, tctx, &fr), + "netr_GetForestTrustInformation failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "netr_GetForestTrustInformation failed"); + + for(i = 0; i < forest_trust_info->count; i++) { + struct lsa_ForestTrustRecord *e = forest_trust_info->entries[i]; + + switch (e->type) { + case LSA_FOREST_TRUST_TOP_LEVEL_NAME: + if (strcmp(e->forest_trust_data.top_level_name.string, trusting_dom_dns_name) != 0) { + break; + } + + torture_assert(tctx, tln == NULL, "TOP_LEVEL_NAME found twice"); + + tln = e; + break; + + case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX: + break; + + case LSA_FOREST_TRUST_DOMAIN_INFO: + if (strcmp(e->forest_trust_data.domain_info.dns_domain_name.string, trusting_dom_dns_name) != 0) { + break; + } + + torture_assert(tctx, di == NULL, "DOMAIN_INFO found twice"); + + di = e; + break; + default: + torture_assert_int_equal(tctx, e->type, LSA_FOREST_TRUST_TOP_LEVEL_NAME, + "Unexpected LSA_FOREST_TRUST_* type"); + } + } + + torture_assert(tctx, tln != NULL, "TOP_LEVEL_NAME entry missing"); + torture_assert(tctx, di != NULL, "DOMAIN_INFO entry missing"); + + torture_assert_str_equal(tctx, di->forest_trust_data.domain_info.netbios_domain_name.string, + trusting_dom_name, + "netbios_domain_name mismatch"); + + return true; +} + +static bool test_setup_trust(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *netbios_name, + const char *dns_name, + struct dom_sid *sid, + DATA_BLOB *auth_blob) + +{ + DATA_BLOB session_key; + struct lsa_TrustDomainInfoAuthInfoInternal authinfo; + NTSTATUS status; + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t _session_key; + + if (!check_name(p, tctx, netbios_name)) { + return false; + } + if (!check_name(p, tctx, dns_name)) { + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n", + nt_errstr(status)); + return false; + } + + authinfo.auth_blob.data = talloc_memdup(tctx, auth_blob->data, + auth_blob->length); + if (authinfo.auth_blob.data == NULL) { + return false; + } + authinfo.auth_blob.size = auth_blob->length; + + _session_key = (gnutls_datum_t) { + .data = session_key.data, + .size = session_key.length, + }; + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &_session_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, + authinfo.auth_blob.data, + authinfo.auth_blob.size); + gnutls_cipher_deinit(cipher_hnd); + + if (!test_create_trust_and_set_info(p, tctx, netbios_name, + dns_name, sid, &authinfo)) { + return false; + } + + return true; +} + +static bool testcase_ForestTrusts(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char *dom2_binding_string; + const char * dom2_cred_string; + NTSTATUS status; + struct dom_sid *domsid; + DATA_BLOB auth_blob; + struct dcerpc_binding *dom2_binding; + struct dcerpc_pipe *dom2_p; + struct cli_credentials *dom2_credentials; + union lsa_PolicyInformation *dom1_info_dns = NULL; + union lsa_PolicyInformation *dom2_info_dns = NULL; + const char *binding = torture_setting_string(tctx, "binding", NULL); + char *test_password; + + torture_comment(tctx, "Testing Forest Trusts\n"); + + test_password = generate_random_password(tctx, 32, 64); + torture_assert(tctx, test_password != NULL, "test password must be generated"); + + if (!get_trust_domain_passwords_auth_blob(tctx, test_password, &auth_blob)) { + torture_comment(tctx, + "get_trust_domain_passwords_auth_blob failed\n"); + return false; + } + +#if 0 + /* Use the following if get_trust_domain_passwords_auth_blob() cannot + * generate a usable blob due to errors in the IDL */ + auth_blob.data = talloc_memdup(tctx, my_blob, sizeof(my_blob)); + auth_blob.length = sizeof(my_blob); + + test_password = "1234567890" +#endif + + domsid = dom_sid_parse_talloc(tctx, TEST_DOM_SID); + if (domsid == NULL) { + return false; + } + + if (!test_setup_trust(tctx, p, TEST_DOM, TEST_DOM_DNS, domsid, + &auth_blob)) { + return false; + } + + if (!get_lsa_policy_info_dns(p, tctx, &dom1_info_dns)) { + return false; + } + + if (!get_and_set_info(p, tctx, TEST_DOM)) { + return false; + } + + if (!test_validate_trust(tctx, binding, + dom1_info_dns->dns.name.string, + dom1_info_dns->dns.dns_domain.string, + TEST_DOM, TEST_DOM_DNS, test_password)) { + return false; + } + + if (!delete_trusted_domain_by_sid(p, tctx, domsid)) { + return false; + } + + dom2_binding_string = torture_setting_string(tctx, + "Forest_Trust_Dom2_Binding", + NULL); + if (dom2_binding_string == NULL) { + torture_skip(tctx, "torture:Forest_Trust_Dom2_Binding not specified\n"); + } + + status = dcerpc_parse_binding(tctx, dom2_binding_string, &dom2_binding); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_parse_binding()"); + + dom2_cred_string = torture_setting_string(tctx, + "Forest_Trust_Dom2_Creds", + NULL); + torture_assert(tctx, dom2_cred_string != NULL, "torture:Forest_Trust_Dom2_Creds missing"); + + dom2_credentials = cli_credentials_init(tctx); + torture_assert(tctx, dom2_credentials != NULL, "cli_credentials_init()"); + + cli_credentials_parse_string(dom2_credentials, dom2_cred_string, + CRED_SPECIFIED); + cli_credentials_set_workstation(dom2_credentials, + TEST_MACHINE_NAME, CRED_SPECIFIED); + + status = dcerpc_pipe_connect_b(tctx, &dom2_p, dom2_binding, + &ndr_table_lsarpc, dom2_credentials, + tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, + "Failed to connect to remote server: %s\n", + dcerpc_binding_string(tctx, dom2_binding))); + + if (!get_lsa_policy_info_dns(dom2_p, tctx, &dom2_info_dns)) { + return false; + } + + if (strcasecmp(dom1_info_dns->dns.name.string, + dom2_info_dns->dns.name.string) == 0 || + strcasecmp(dom1_info_dns->dns.dns_domain.string, + dom2_info_dns->dns.dns_domain.string) == 0) + { + torture_assert(tctx, false, talloc_asprintf(tctx, + "Trusting (%s;%s) and trusted domain (%s;%s) have the " + "same name", + dom1_info_dns->dns.name.string, + dom1_info_dns->dns.dns_domain.string, + dom2_info_dns->dns.name.string, + dom2_info_dns->dns.dns_domain.string)); + } + + if (!test_setup_trust(tctx, p, dom2_info_dns->dns.name.string, + dom2_info_dns->dns.dns_domain.string, + dom2_info_dns->dns.sid, &auth_blob)) { + return false; + } + if (!test_setup_trust(tctx, dom2_p, dom1_info_dns->dns.name.string, + dom1_info_dns->dns.dns_domain.string, + dom1_info_dns->dns.sid, &auth_blob)) { + return false; + } + + if (!test_validate_trust(tctx, binding, + dom1_info_dns->dns.name.string, + dom1_info_dns->dns.dns_domain.string, + dom2_info_dns->dns.name.string, + dom2_info_dns->dns.dns_domain.string, test_password)) { + return false; + } + + if (!test_validate_trust(tctx, dom2_binding_string, + dom2_info_dns->dns.name.string, + dom2_info_dns->dns.dns_domain.string, + dom1_info_dns->dns.name.string, + dom1_info_dns->dns.dns_domain.string, test_password)) { + return false; + } + + if (!delete_trusted_domain_by_sid(p, tctx, dom2_info_dns->dns.sid)) { + return false; + } + + if (!delete_trusted_domain_by_sid(dom2_p, tctx, dom1_info_dns->dns.sid)) { + return false; + } + + return true; +} + +/* By default this test creates a trust object in the destination server to a + * dummy domain. If a second server from a different domain is specified on the + * command line a trust is created between those two domains. + * + * Example: + * smbtorture ncacn_np:srv1.dom1.test[print] RPC-LSA-FOREST-TRUST \ + * -U 'dom1\testadm1%12345678' \ + * --option=torture:Forest_Trust_Dom2_Binding=ncacn_np:srv2.dom2.test[print] \ + * --option=torture:Forest_Trust_Dom2_Creds='dom2\testadm2%12345678' + */ + +struct torture_suite *torture_rpc_lsa_forest_trust(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "lsa.forest.trust"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa-forest-trust", + &ndr_table_lsarpc); + torture_rpc_tcase_add_test(tcase, "ForestTrust", testcase_ForestTrusts); + + return suite; +} diff --git a/source4/torture/rpc/frsapi.c b/source4/torture/rpc/frsapi.c new file mode 100644 index 0000000..710826a --- /dev/null +++ b/source4/torture/rpc/frsapi.c @@ -0,0 +1,276 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc frsapi operations + + Copyright (C) Guenther Deschner 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_frsapi_c.h" +#include "param/param.h" + +static bool test_GetDsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t *CurrentInterval, + uint32_t *DsPollingLongInterval, + uint32_t *DsPollingShortInterval) +{ + struct frsapi_GetDsPollingIntervalW r; + + ZERO_STRUCT(r); + + r.out.CurrentInterval = CurrentInterval; + r.out.DsPollingLongInterval = DsPollingLongInterval; + r.out.DsPollingShortInterval = DsPollingShortInterval; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_GetDsPollingIntervalW_r(b, tctx, &r), + "GetDsPollingIntervalW failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "GetDsPollingIntervalW failed"); + + return true; +} + +static bool test_SetDsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t CurrentInterval, + uint32_t DsPollingLongInterval, + uint32_t DsPollingShortInterval) +{ + struct frsapi_SetDsPollingIntervalW r; + + ZERO_STRUCT(r); + + r.in.CurrentInterval = CurrentInterval; + r.in.DsPollingLongInterval = DsPollingLongInterval; + r.in.DsPollingShortInterval = DsPollingShortInterval; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_SetDsPollingIntervalW_r(b, tctx, &r), + "SetDsPollingIntervalW failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "SetDsPollingIntervalW failed"); + + return true; +} + +static bool test_DsPollingIntervalW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t i1, i2, i3; + uint32_t k1, k2, k3; + + if (!test_GetDsPollingIntervalW(tctx, b, &i1, &i2, &i3)) { + return false; + } + + if (!test_SetDsPollingIntervalW(tctx, b, i1, i2, i3)) { + return false; + } + + k1 = i1; + k2 = k3 = 0; + + if (!test_SetDsPollingIntervalW(tctx, b, k1, k2, k3)) { + return false; + } + + if (!test_GetDsPollingIntervalW(tctx, b, &k1, &k2, &k3)) { + return false; + } + + if ((i1 != k1) || (i2 != k2) || (i3 != k3)) { + return false; + } + + return true; +} + +static bool test_IsPathReplicated_err(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *path, + uint32_t type, + WERROR werr) +{ + struct frsapi_IsPathReplicated r; + struct GUID guid; + uint32_t replicated, primary, root; + + ZERO_STRUCT(r); + + r.in.path = path; + r.in.replica_set_type = type; + r.out.replicated = &replicated; + r.out.primary = &primary; + r.out.root = &root; + r.out.replica_set_guid = &guid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_IsPathReplicated_r(b, tctx, &r), + "IsPathReplicated failed"); + + torture_assert_werr_equal(tctx, r.out.result, werr, + "GetDsPollingIntervalW failed"); + + return true; +} + +static bool _test_IsPathReplicated(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *path, + uint32_t type) +{ + return test_IsPathReplicated_err(tctx, b, path, type, WERR_OK); +} + +static bool test_IsPathReplicated(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + const uint32_t lvls[] = { + FRSAPI_REPLICA_SET_TYPE_0, + FRSAPI_REPLICA_SET_TYPE_DOMAIN, + FRSAPI_REPLICA_SET_TYPE_DFS }; + int i; + bool ret = true; + + if (!test_IsPathReplicated_err(tctx, b, NULL, 0, + WERR_FRS_ERR_INVALID_SERVICE_PARAMETER)) { + ret = false; + } + + for (i=0; ibinding_handle; + struct frsapi_ForceReplication r; + + ZERO_STRUCT(r); + + r.in.replica_set_guid = NULL; + r.in.connection_guid = NULL; + r.in.replica_set_name = lpcfg_dnsdomain(tctx->lp_ctx); + r.in.partner_dns_name = dcerpc_server_name(p); + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_ForceReplication_r(b, tctx, &r), + "ForceReplication failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "ForceReplication failed"); + + return true; +} + +static bool test_InfoW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + int i; + + for (i=0; i<10; i++) { + + struct frsapi_InfoW r; + struct frsapi_Info *info; + int d; + DATA_BLOB blob; + + ZERO_STRUCT(r); + + info = talloc_zero(tctx, struct frsapi_Info); + + r.in.length = 0x1000; + r.in.info = r.out.info = info; + + info->length = r.in.length; + info->length2 = r.in.length; + info->level = i; + info->offset = 0x2c; + info->blob_len = 0x2c; + + torture_assert_ntstatus_ok(tctx, + dcerpc_frsapi_InfoW_r(b, tctx, &r), + "InfoW failed"); + + torture_assert_werr_ok(tctx, r.out.result, "InfoW failed"); + + /* display the formatted blob text */ + blob = r.out.info->blob; + for (d = 0; d < blob.length; d++) { + if (blob.data[d]) { + printf("%c", blob.data[d]); + } + } + printf("\n"); + } + + return true; +} + +struct torture_suite *torture_rpc_frsapi(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "frsapi"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "frsapi", + &ndr_table_frsapi); + + torture_rpc_tcase_add_test(tcase, "DsPollingIntervalW", + test_DsPollingIntervalW); + + torture_rpc_tcase_add_test(tcase, "IsPathReplicated", + test_IsPathReplicated); + + torture_rpc_tcase_add_test(tcase, "ForceReplication", + test_ForceReplication); + + torture_rpc_tcase_add_test(tcase, "InfoW", + test_InfoW); + + return suite; +} diff --git a/source4/torture/rpc/fsrvp.c b/source4/torture/rpc/fsrvp.c new file mode 100644 index 0000000..1b38947 --- /dev/null +++ b/source4/torture/rpc/fsrvp.c @@ -0,0 +1,973 @@ +/* + Unix SMB/CIFS implementation. + + test suite for File Server Remote VSS Protocol operations + + Copyright (C) David Disseldorp 2012-2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * Windows Server "8" Beta is very picky in how it accepts FSRVP requests, the + * client must be a member of the same AD domain, ndr64 and signing must be + * negotiated for the DCE/RPC bind. E.g. + * + * smbtorture ncacn_np:LUTZE[/pipe/FssagentRpc,smb2,ndr64,sign] \ + * -U 'DOM\user%pw' rpc.fsrvp + * + * This test suite requires a snapshotable share named FSHARE (see #def below). + */ +#include "includes.h" +#include "lib/param/param.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" +#include "libcli/util/hresult.h" +#include "libcli/security/dom_sid.h" +#include "libcli/security/security_descriptor.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "librpc/gen_ndr/ndr_fsrvp_c.h" +#include "lib/cmdline/cmdline.h" + +#define FSHARE "fsrvp_share" +#define FNAME "testfss.dat" + +static bool test_fsrvp_is_path_supported(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fss_IsPathSupported r; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + + ZERO_STRUCT(r); + r.in.ShareName = talloc_asprintf(tctx,"\\\\%s\\%s\\", + dcerpc_server_name(p), + FSHARE); + status = dcerpc_fss_IsPathSupported_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "IsPathSupported failed"); + + torture_assert(tctx, *r.out.SupportedByThisProvider, + "path not supported"); + + torture_comment(tctx, "path %s is supported by fsrvp server %s\n", + r.in.ShareName, *r.out.OwnerMachineName); + + return true; +} + +static bool test_fsrvp_get_version(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fss_GetSupportedVersion r; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + + ZERO_STRUCT(r); + status = dcerpc_fss_GetSupportedVersion_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "GetSupportedVersion failed"); + + torture_comment(tctx, "got MinVersion %u\n", *r.out.MinVersion); + torture_comment(tctx, "got MaxVersion %u\n", *r.out.MaxVersion); + + return true; +} + +static bool test_fsrvp_set_ctx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fss_SetContext r; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + + ZERO_STRUCT(r); + r.in.Context = FSRVP_CTX_BACKUP; + status = dcerpc_fss_SetContext_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "SetContext failed"); + + return true; +} + +enum test_fsrvp_inject { + TEST_FSRVP_TOUT_NONE = 0, + TEST_FSRVP_TOUT_SET_CTX, + TEST_FSRVP_TOUT_START_SET, + TEST_FSRVP_TOUT_ADD_TO_SET, + TEST_FSRVP_TOUT_PREPARE, + TEST_FSRVP_TOUT_COMMIT, + + TEST_FSRVP_STOP_B4_EXPOSE, +}; + +static bool test_fsrvp_sc_create(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *share, + enum test_fsrvp_inject inject, + struct fssagent_share_mapping_1 **sc_map) +{ + struct fss_IsPathSupported r_pathsupport_get; + struct fss_GetSupportedVersion r_version_get; + struct fss_SetContext r_context_set; + struct fss_StartShadowCopySet r_scset_start; + struct fss_AddToShadowCopySet r_scset_add1; + struct fss_AddToShadowCopySet r_scset_add2; + struct fss_PrepareShadowCopySet r_scset_prep; + struct fss_CommitShadowCopySet r_scset_commit; + struct fss_ExposeShadowCopySet r_scset_expose; + struct fss_GetShareMapping r_sharemap_get; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + time_t start_time; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + struct fssagent_share_mapping_1 *map = NULL; + int sleep_time; + + /* + * PrepareShadowCopySet & CommitShadowCopySet often exceed the default + * 60 second dcerpc request timeout against Windows Server "8" Beta. + */ + dcerpc_binding_handle_set_timeout(b, 240); + + ZERO_STRUCT(r_pathsupport_get); + r_pathsupport_get.in.ShareName = share; + status = dcerpc_fss_IsPathSupported_r(b, tmp_ctx, &r_pathsupport_get); + torture_assert_ntstatus_ok(tctx, status, + "IsPathSupported failed"); + torture_assert_int_equal(tctx, r_pathsupport_get.out.result, 0, + "failed IsPathSupported response"); + torture_assert(tctx, r_pathsupport_get.out.SupportedByThisProvider, + "path not supported"); + + ZERO_STRUCT(r_version_get); + status = dcerpc_fss_GetSupportedVersion_r(b, tmp_ctx, &r_version_get); + torture_assert_ntstatus_ok(tctx, status, + "GetSupportedVersion failed"); + torture_assert_int_equal(tctx, r_version_get.out.result, 0, + "failed GetSupportedVersion response"); + + ZERO_STRUCT(r_context_set); + r_context_set.in.Context = FSRVP_CTX_BACKUP; + status = dcerpc_fss_SetContext_r(b, tmp_ctx, &r_context_set); + torture_assert_ntstatus_ok(tctx, status, "SetContext failed"); + torture_assert_int_equal(tctx, r_context_set.out.result, 0, + "failed SetContext response"); + + if (inject == TEST_FSRVP_TOUT_SET_CTX) { + sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss", + "sequence timeout", 180); + torture_comment(tctx, "sleeping for %d\n", sleep_time); + smb_msleep((sleep_time * 1000) + 500); + } + + ZERO_STRUCT(r_scset_start); + r_scset_start.in.ClientShadowCopySetId = GUID_random(); + status = dcerpc_fss_StartShadowCopySet_r(b, tmp_ctx, &r_scset_start); + torture_assert_ntstatus_ok(tctx, status, + "StartShadowCopySet failed"); + if (inject == TEST_FSRVP_TOUT_SET_CTX) { + /* expect error due to message sequence timeout after set_ctx */ + torture_assert_int_equal(tctx, r_scset_start.out.result, + FSRVP_E_BAD_STATE, + "StartShadowCopySet timeout response"); + goto done; + } + torture_assert_int_equal(tctx, r_scset_start.out.result, 0, + "failed StartShadowCopySet response"); + torture_comment(tctx, "%s: shadow-copy set created\n", + GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId)); + + if (inject == TEST_FSRVP_TOUT_START_SET) { + sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss", + "sequence timeout", 180); + torture_comment(tctx, "sleeping for %d\n", sleep_time); + smb_msleep((sleep_time * 1000) + 500); + } + + ZERO_STRUCT(r_scset_add1); + r_scset_add1.in.ClientShadowCopyId = GUID_random(); + r_scset_add1.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_scset_add1.in.ShareName = share; + status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add1); + torture_assert_ntstatus_ok(tctx, status, + "AddToShadowCopySet failed"); + if (inject == TEST_FSRVP_TOUT_START_SET) { + torture_assert_int_equal(tctx, r_scset_add1.out.result, + HRES_ERROR_V(HRES_E_INVALIDARG), + "AddToShadowCopySet timeout response"); + goto done; + } + torture_assert_int_equal(tctx, r_scset_add1.out.result, 0, + "failed AddToShadowCopySet response"); + torture_comment(tctx, "%s(%s): %s added to shadow-copy set\n", + GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId), + GUID_string(tmp_ctx, r_scset_add1.out.pShadowCopyId), + r_scset_add1.in.ShareName); + + /* attempts to add the same share twice should fail */ + ZERO_STRUCT(r_scset_add2); + r_scset_add2.in.ClientShadowCopyId = GUID_random(); + r_scset_add2.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_scset_add2.in.ShareName = share; + status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add2); + torture_assert_ntstatus_ok(tctx, status, + "AddToShadowCopySet failed"); + torture_assert_int_equal(tctx, r_scset_add2.out.result, + FSRVP_E_OBJECT_ALREADY_EXISTS, + "failed AddToShadowCopySet response"); + + if (inject == TEST_FSRVP_TOUT_ADD_TO_SET) { + sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss", + "sequence timeout", 1800); + torture_comment(tctx, "sleeping for %d\n", sleep_time); + smb_msleep((sleep_time * 1000) + 500); + } + + start_time = time_mono(NULL); + ZERO_STRUCT(r_scset_prep); + r_scset_prep.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; +// r_scset_prep.in.TimeOutInMilliseconds = (1800 * 1000); /* win8 */ + r_scset_prep.in.TimeOutInMilliseconds = (240 * 1000); + status = dcerpc_fss_PrepareShadowCopySet_r(b, tmp_ctx, &r_scset_prep); + torture_assert_ntstatus_ok(tctx, status, + "PrepareShadowCopySet failed"); + if (inject == TEST_FSRVP_TOUT_ADD_TO_SET) { + torture_assert_int_equal(tctx, r_scset_prep.out.result, + HRES_ERROR_V(HRES_E_INVALIDARG), + "PrepareShadowCopySet tout response"); + goto done; + } + torture_assert_int_equal(tctx, r_scset_prep.out.result, 0, + "failed PrepareShadowCopySet response"); + torture_comment(tctx, "%s: prepare completed in %llu secs\n", + GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId), + (unsigned long long)(time_mono(NULL) - start_time)); + + if (inject == TEST_FSRVP_TOUT_PREPARE) { + sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss", + "sequence timeout", 1800); + torture_comment(tctx, "sleeping for %d\n", sleep_time); + smb_msleep((sleep_time * 1000) + 500); + } + + start_time = time_mono(NULL); + ZERO_STRUCT(r_scset_commit); + r_scset_commit.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_scset_commit.in.TimeOutInMilliseconds = (180 * 1000); /* win8 */ + status = dcerpc_fss_CommitShadowCopySet_r(b, tmp_ctx, &r_scset_commit); + torture_assert_ntstatus_ok(tctx, status, + "CommitShadowCopySet failed"); + if (inject == TEST_FSRVP_TOUT_PREPARE) { + torture_assert_int_equal(tctx, r_scset_commit.out.result, + HRES_ERROR_V(HRES_E_INVALIDARG), + "CommitShadowCopySet tout response"); + goto done; + } + torture_assert_int_equal(tctx, r_scset_commit.out.result, 0, + "failed CommitShadowCopySet response"); + torture_comment(tctx, "%s: commit completed in %llu secs\n", + GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId), + (unsigned long long)(time_mono(NULL) - start_time)); + + if (inject == TEST_FSRVP_TOUT_COMMIT) { + sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss", + "sequence timeout", 180); + torture_comment(tctx, "sleeping for %d\n", sleep_time); + smb_msleep((sleep_time * 1000) + 500); + } else if (inject == TEST_FSRVP_STOP_B4_EXPOSE) { + /* return partial snapshot information */ + map = talloc_zero(tctx, struct fssagent_share_mapping_1); + map->ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + map->ShadowCopyId = *r_scset_add1.out.pShadowCopyId; + goto done; + } + + start_time = time_mono(NULL); + ZERO_STRUCT(r_scset_expose); + r_scset_expose.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_scset_expose.in.TimeOutInMilliseconds = (120 * 1000); /* win8 */ + status = dcerpc_fss_ExposeShadowCopySet_r(b, tmp_ctx, &r_scset_expose); + torture_assert_ntstatus_ok(tctx, status, + "ExposeShadowCopySet failed"); + if (inject == TEST_FSRVP_TOUT_COMMIT) { + torture_assert_int_equal(tctx, r_scset_expose.out.result, + HRES_ERROR_V(HRES_E_INVALIDARG), + "ExposeShadowCopySet tout response"); + goto done; + } + torture_assert_int_equal(tctx, r_scset_expose.out.result, 0, + "failed ExposeShadowCopySet response"); + torture_comment(tctx, "%s: expose completed in %llu secs\n", + GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId), + (unsigned long long)(time_mono(NULL) - start_time)); + + ZERO_STRUCT(r_sharemap_get); + r_sharemap_get.in.ShadowCopyId = *r_scset_add1.out.pShadowCopyId; + r_sharemap_get.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_sharemap_get.in.ShareName = r_scset_add1.in.ShareName; + r_sharemap_get.in.Level = 1; + status = dcerpc_fss_GetShareMapping_r(b, tmp_ctx, &r_sharemap_get); + torture_assert_ntstatus_ok(tctx, status, "GetShareMapping failed"); + torture_assert_int_equal(tctx, r_sharemap_get.out.result, 0, + "failed GetShareMapping response"); + torture_comment(tctx, "%s(%s): %s is a snapshot of %s at %s\n", + GUID_string(tmp_ctx, &r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopySetId), + GUID_string(tmp_ctx, &r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyId), + r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyShareName, + r_sharemap_get.out.ShareMapping->ShareMapping1->ShareNameUNC, + nt_time_string(tmp_ctx, r_sharemap_get.out.ShareMapping->ShareMapping1->tstamp)); + + map = talloc_zero(tctx, struct fssagent_share_mapping_1); + map->ShadowCopySetId = r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopySetId; + map->ShadowCopyId = r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyId; + map->ShadowCopyShareName + = talloc_strdup(tctx, r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyShareName); + map->ShareNameUNC + = talloc_strdup(tctx, r_sharemap_get.out.ShareMapping->ShareMapping1->ShareNameUNC); + map->tstamp = r_sharemap_get.out.ShareMapping->ShareMapping1->tstamp; + + torture_assert(tctx, !GUID_compare(&r_sharemap_get.in.ShadowCopySetId, + &map->ShadowCopySetId), + "sc_set GUID mismatch in GetShareMapping"); + torture_assert(tctx, !GUID_compare(&r_sharemap_get.in.ShadowCopyId, + &map->ShadowCopyId), + "sc GUID mismatch in GetShareMapping"); + +done: + talloc_free(tmp_ctx); + *sc_map = map; + + return true; +} + +static bool test_fsrvp_sc_delete(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct fssagent_share_mapping_1 *sc_map) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct fss_DeleteShareMapping r_sharemap_del; + NTSTATUS status; + + ZERO_STRUCT(r_sharemap_del); + r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId; + r_sharemap_del.in.ShadowCopyId = sc_map->ShadowCopyId; + r_sharemap_del.in.ShareName = sc_map->ShareNameUNC; + status = dcerpc_fss_DeleteShareMapping_r(b, tctx, &r_sharemap_del); + torture_assert_ntstatus_ok(tctx, status, "DeleteShareMapping failed"); + torture_assert_int_equal(tctx, r_sharemap_del.out.result, 0, + "failed DeleteShareMapping response"); + + return true; +} + +static bool test_fsrvp_sc_create_simple(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fssagent_share_mapping_1 *sc_map; + /* no trailing backslash - should work. See note in cmd_fss.c */ + char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s", + dcerpc_server_name(p), FSHARE); + + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map), + "sc create"); + + torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del"); + + return true; +} + +static bool test_fsrvp_sc_set_abort(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s\\", + dcerpc_server_name(p), FSHARE); + struct dcerpc_binding_handle *b = p->binding_handle; + struct fss_IsPathSupported r_pathsupport_get; + struct fss_GetSupportedVersion r_version_get; + struct fss_SetContext r_context_set; + struct fss_StartShadowCopySet r_scset_start; + struct fss_AbortShadowCopySet r_scset_abort; + struct fss_AddToShadowCopySet r_scset_add; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + ZERO_STRUCT(r_pathsupport_get); + r_pathsupport_get.in.ShareName = share_unc; + status = dcerpc_fss_IsPathSupported_r(b, tmp_ctx, &r_pathsupport_get); + torture_assert_ntstatus_ok(tctx, status, + "IsPathSupported failed"); + torture_assert(tctx, r_pathsupport_get.out.SupportedByThisProvider, + "path not supported"); + + ZERO_STRUCT(r_version_get); + status = dcerpc_fss_GetSupportedVersion_r(b, tmp_ctx, &r_version_get); + torture_assert_ntstatus_ok(tctx, status, + "GetSupportedVersion failed"); + + ZERO_STRUCT(r_context_set); + r_context_set.in.Context = FSRVP_CTX_BACKUP; + status = dcerpc_fss_SetContext_r(b, tmp_ctx, &r_context_set); + torture_assert_ntstatus_ok(tctx, status, "SetContext failed"); + + ZERO_STRUCT(r_scset_start); + r_scset_start.in.ClientShadowCopySetId = GUID_random(); + status = dcerpc_fss_StartShadowCopySet_r(b, tmp_ctx, &r_scset_start); + torture_assert_ntstatus_ok(tctx, status, + "StartShadowCopySet failed"); + + ZERO_STRUCT(r_scset_abort); + r_scset_abort.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + status = dcerpc_fss_AbortShadowCopySet_r(b, tmp_ctx, &r_scset_abort); + torture_assert_ntstatus_ok(tctx, status, + "AbortShadowCopySet failed"); + + ZERO_STRUCT(r_scset_add); + r_scset_add.in.ClientShadowCopyId = GUID_random(); + r_scset_add.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId; + r_scset_add.in.ShareName = share_unc; + status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add); + torture_assert_ntstatus_ok(tctx, status, "AddToShadowCopySet failed " + "following abort"); + /* + * XXX Windows 8 server beta returns FSRVP_E_BAD_STATE here rather than + * FSRVP_E_BAD_ID / HRES_E_INVALIDARG. + */ + torture_assert(tctx, (r_scset_add.out.result != 0), + "incorrect AddToShadowCopySet response following abort"); + + talloc_free(tmp_ctx); + return true; +} + +static bool test_fsrvp_bad_id(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fssagent_share_mapping_1 *sc_map; + struct dcerpc_binding_handle *b = p->binding_handle; + struct fss_DeleteShareMapping r_sharemap_del; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s\\", + dcerpc_server_name(p), FSHARE); + + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map), + "sc create"); + + ZERO_STRUCT(r_sharemap_del); + r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId; + r_sharemap_del.in.ShadowCopySetId.time_low++; /* bogus */ + r_sharemap_del.in.ShadowCopyId = sc_map->ShadowCopyId; + r_sharemap_del.in.ShareName = sc_map->ShareNameUNC; + status = dcerpc_fss_DeleteShareMapping_r(b, tmp_ctx, &r_sharemap_del); + torture_assert_ntstatus_ok(tctx, status, + "DeleteShareMapping failed"); + torture_assert_int_equal(tctx, r_sharemap_del.out.result, + FSRVP_E_OBJECT_NOT_FOUND, + "incorrect DeleteShareMapping response"); + + r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId; + r_sharemap_del.in.ShadowCopyId.time_mid++; /* bogus */ + status = dcerpc_fss_DeleteShareMapping_r(b, tmp_ctx, &r_sharemap_del); + torture_assert_ntstatus_ok(tctx, status, + "DeleteShareMapping failed"); + torture_assert_int_equal(tctx, r_sharemap_del.out.result, + HRES_ERROR_V(HRES_E_INVALIDARG), + "incorrect DeleteShareMapping response"); + + torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del"); + + talloc_free(sc_map); + talloc_free(tmp_ctx); + + return true; +} + +static bool test_fsrvp_sc_share_io(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fssagent_share_mapping_1 *sc_map; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s", + dcerpc_server_name(p), FSHARE); + struct smb2_tree *tree_base; + struct smb2_tree *tree_snap; + struct smbcli_options options; + struct smb2_handle base_fh; + struct smb2_read r; + struct smb2_create io; + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + status = smb2_connect(tmp_ctx, + dcerpc_server_name(p), + lpcfg_smb_ports(tctx->lp_ctx), + FSHARE, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree_base, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect to SMB2 share"); + + smb2_util_unlink(tree_base, FNAME); + status = torture_smb2_testfile(tree_base, FNAME, &base_fh); + torture_assert_ntstatus_ok(tctx, status, "base write open"); + + status = smb2_util_write(tree_base, base_fh, "pre-snap", 0, + sizeof("pre-snap")); + torture_assert_ntstatus_ok(tctx, status, "src write"); + + + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map), + "sc create"); + + status = smb2_util_write(tree_base, base_fh, "post-snap", 0, + sizeof("post-snap")); + torture_assert_ntstatus_ok(tctx, status, "base write"); + + /* connect to snapshot share and verify pre-snapshot data */ + status = smb2_connect(tmp_ctx, + dcerpc_server_name(p), + lpcfg_smb_ports(tctx->lp_ctx), + sc_map->ShadowCopyShareName, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree_snap, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect to SMB2 shadow-copy share"); + /* Windows server 8 allows RW open to succeed here for a ro snapshot */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_RIGHTS_FILE_READ; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = FNAME; + status = smb2_create(tree_snap, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "snap read open"); + + ZERO_STRUCT(r); + r.in.file.handle = io.out.file.handle; + r.in.length = sizeof("pre-snap"); + status = smb2_read(tree_snap, tmp_ctx, &r); + torture_assert_ntstatus_ok(tctx, status, "read"); + torture_assert_u64_equal(tctx, r.out.data.length, r.in.length, + "read data len mismatch"); + torture_assert_str_equal(tctx, (char *)r.out.data.data, "pre-snap", + "bad snapshot data"); + + torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del"); + + talloc_free(sc_map); + talloc_free(tmp_ctx); + + return true; +} + +static bool test_fsrvp_enum_snaps(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + int *_count) +{ + struct smb2_ioctl io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.level = RAW_IOCTL_SMB2; + io.in.file.handle = fh; + io.in.function = FSCTL_SRV_ENUM_SNAPS; + io.in.max_output_response = 16; + io.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, mem_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "enum ioctl"); + + *_count = IVAL(io.out.out.data, 0); + + /* with max_output_response=16, no labels should be sent */ + torture_assert_int_equal(tctx, IVAL(io.out.out.data, 4), 0, + "enum snaps labels"); + + /* TODO with 0 snaps, needed_data_count should be 0? */ + if (*_count != 0) { + torture_assert(tctx, IVAL(io.out.out.data, 8) != 0, + "enum snaps needed non-zero"); + } + + return true; +} + +static bool test_fsrvp_enum_created(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct fssagent_share_mapping_1 *sc_map; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s\\", + dcerpc_server_name(p), FSHARE); + struct smb2_tree *tree_base; + struct smbcli_options options; + struct smb2_handle base_fh; + int count; + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + status = smb2_connect(tmp_ctx, + dcerpc_server_name(p), + lpcfg_smb_ports(tctx->lp_ctx), + FSHARE, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree_base, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect to SMB2 share"); + + smb2_util_unlink(tree_base, FNAME); + status = torture_smb2_testfile(tree_base, FNAME, &base_fh); + torture_assert_ntstatus_ok(tctx, status, "base write open"); + + status = smb2_util_write(tree_base, base_fh, "pre-snap", 0, + sizeof("pre-snap")); + torture_assert_ntstatus_ok(tctx, status, "src write"); + + torture_assert(tctx, + test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh, + &count), + "count"); + torture_assert_int_equal(tctx, count, 0, "num snaps"); + + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map), + "sc create"); + talloc_free(sc_map); + + torture_assert(tctx, + test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh, + &count), + "count"); + /* + * Snapshots created via FSRVP on Windows Server 2012 are not added to + * the previous versions list, so it will fail here... + */ + torture_assert_int_equal(tctx, count, 1, "num snaps"); + + smb_msleep(1100); /* @GMT tokens have a 1 second resolution */ + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map), + "sc create"); + talloc_free(sc_map); + + torture_assert(tctx, + test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh, + &count), + "count"); + torture_assert_int_equal(tctx, count, 2, "num snaps"); + + smb2_util_close(tree_base, base_fh); + ZERO_STRUCT(base_fh); + + smb2_util_unlink(tree_base, FNAME); + + talloc_free(tmp_ctx); + + return true; +} + +static bool test_fsrvp_seq_timeout(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + int i; + struct fssagent_share_mapping_1 *sc_map; + char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s", + dcerpc_server_name(p), FSHARE); + + for (i = TEST_FSRVP_TOUT_NONE; i <= TEST_FSRVP_TOUT_COMMIT; i++) { + torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, + i, &sc_map), + "sc create"); + + /* only need to delete if create process didn't timeout */ + if (i == TEST_FSRVP_TOUT_NONE) { + torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), + "sc del"); + } + } + + return true; +} + +static bool test_fsrvp_share_sd(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct dcerpc_pipe *srvsvc_p; + struct srvsvc_NetShareGetInfo q; + struct srvsvc_NetShareSetInfo s; + struct srvsvc_NetShareInfo502 *info502; + struct fssagent_share_mapping_1 *sc_map; + struct fss_ExposeShadowCopySet r_scset_expose; + struct fss_GetShareMapping r_sharemap_get; + struct security_descriptor *sd_old; + struct security_descriptor *sd_base; + struct security_descriptor *sd_snap; + struct security_ace *ace; + int i; + int aces_found; + char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s", + dcerpc_server_name(p), FSHARE); + ZERO_STRUCT(q); + q.in.server_unc = dcerpc_server_name(p); + q.in.share_name = FSHARE; + q.in.level = 502; + + status = torture_rpc_connection(tctx, &srvsvc_p, &ndr_table_srvsvc); + torture_assert_ntstatus_ok(tctx, status, "srvsvc rpc conn failed"); + + /* ensure srvsvc out pointers are allocated during unmarshalling */ + srvsvc_p->conn->flags |= DCERPC_NDR_REF_ALLOC; + + /* obtain the existing DACL for the base share */ + status = dcerpc_srvsvc_NetShareGetInfo_r(srvsvc_p->binding_handle, + tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed"); + + info502 = q.out.info->info502; + + /* back up the existing share SD, so it can be restored on completion */ + sd_old = info502->sd_buf.sd; + sd_base = security_descriptor_copy(tctx, info502->sd_buf.sd); + torture_assert(tctx, sd_base != NULL, "sd dup"); + torture_assert(tctx, sd_base->dacl != NULL, "no existing share DACL"); + + /* the Builtin_X_Operators placeholder ACEs need to be unique */ + for (i = 0; i < sd_base->dacl->num_aces; i++) { + ace = &sd_base->dacl->aces[i]; + if (dom_sid_equal(&ace->trustee, + &global_sid_Builtin_Backup_Operators) + || dom_sid_equal(&ace->trustee, + &global_sid_Builtin_Print_Operators)) { + torture_skip(tctx, "placeholder ACE already exists\n"); + } + } + + /* add Backup_Operators placeholder ACE and set base share DACL */ + ace = talloc_zero(tctx, struct security_ace); + ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace->access_mask = SEC_STD_SYNCHRONIZE; + ace->trustee = global_sid_Builtin_Backup_Operators; + + status = security_descriptor_dacl_add(sd_base, ace); + torture_assert_ntstatus_ok(tctx, status, + "failed to add placeholder ACE to DACL"); + + info502->sd_buf.sd = sd_base; + info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_base, 0); + + ZERO_STRUCT(s); + s.in.server_unc = dcerpc_server_name(p); + s.in.share_name = FSHARE; + s.in.level = 502; + s.in.info = q.out.info; + + status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle, + tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed"); + torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed"); + + /* create a snapshot, but don't expose yet */ + torture_assert(tctx, + test_fsrvp_sc_create(tctx, p, share_unc, + TEST_FSRVP_STOP_B4_EXPOSE, &sc_map), + "sc create"); + + /* + * Add another unique placeholder ACE. + * By changing the share DACL between snapshot creation and exposure we + * can determine at which point the server clones the base share DACL. + */ + ace = talloc_zero(tctx, struct security_ace); + ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace->access_mask = SEC_STD_SYNCHRONIZE; + ace->trustee = global_sid_Builtin_Print_Operators; + + status = security_descriptor_dacl_add(sd_base, ace); + torture_assert_ntstatus_ok(tctx, status, + "failed to add placeholder ACE to DACL"); + + info502->sd_buf.sd = sd_base; + info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_base, 0); + + ZERO_STRUCT(s); + s.in.server_unc = dcerpc_server_name(p); + s.in.share_name = FSHARE; + s.in.level = 502; + s.in.info = q.out.info; + + status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle, + tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed"); + torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed"); + + /* expose the snapshot share and get the new share details */ + ZERO_STRUCT(r_scset_expose); + r_scset_expose.in.ShadowCopySetId = sc_map->ShadowCopySetId; + r_scset_expose.in.TimeOutInMilliseconds = (120 * 1000); /* win8 */ + status = dcerpc_fss_ExposeShadowCopySet_r(p->binding_handle, tctx, + &r_scset_expose); + torture_assert_ntstatus_ok(tctx, status, + "ExposeShadowCopySet failed"); + torture_assert_int_equal(tctx, r_scset_expose.out.result, 0, + "failed ExposeShadowCopySet response"); + + ZERO_STRUCT(r_sharemap_get); + r_sharemap_get.in.ShadowCopyId = sc_map->ShadowCopyId; + r_sharemap_get.in.ShadowCopySetId = sc_map->ShadowCopySetId; + r_sharemap_get.in.ShareName = share_unc; + r_sharemap_get.in.Level = 1; + status = dcerpc_fss_GetShareMapping_r(p->binding_handle, tctx, + &r_sharemap_get); + torture_assert_ntstatus_ok(tctx, status, "GetShareMapping failed"); + torture_assert_int_equal(tctx, r_sharemap_get.out.result, 0, + "failed GetShareMapping response"); + talloc_free(sc_map); + sc_map = r_sharemap_get.out.ShareMapping->ShareMapping1; + + /* restore the original base share ACL */ + info502->sd_buf.sd = sd_old; + info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_old, 0); + status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle, + tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed"); + torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed"); + + /* check for placeholder ACEs in the snapshot share DACL */ + ZERO_STRUCT(q); + q.in.server_unc = dcerpc_server_name(p); + q.in.share_name = sc_map->ShadowCopyShareName; + q.in.level = 502; + status = dcerpc_srvsvc_NetShareGetInfo_r(srvsvc_p->binding_handle, + tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed"); + info502 = q.out.info->info502; + + sd_snap = info502->sd_buf.sd; + torture_assert(tctx, sd_snap != NULL, "sd"); + torture_assert(tctx, sd_snap->dacl != NULL, "no snap share DACL"); + + aces_found = 0; + for (i = 0; i < sd_snap->dacl->num_aces; i++) { + ace = &sd_snap->dacl->aces[i]; + if (dom_sid_equal(&ace->trustee, + &global_sid_Builtin_Backup_Operators)) { + torture_comment(tctx, + "found share ACE added before snapshot\n"); + aces_found++; + } else if (dom_sid_equal(&ace->trustee, + &global_sid_Builtin_Print_Operators)) { + torture_comment(tctx, + "found share ACE added after snapshot\n"); + aces_found++; + } + } + /* + * Expect snapshot share to match the base share DACL at the time of + * exposure, not at the time of snapshot creation. This is in line with + * Windows Server 2012 behaviour. + */ + torture_assert_int_equal(tctx, aces_found, 2, + "placeholder ACE missing from snap share DACL"); + + torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del"); + + return true; +} + +static bool fsrvp_rpc_setup(struct torture_context *tctx, void **data) +{ + NTSTATUS status; + struct torture_rpc_tcase *tcase = talloc_get_type( + tctx->active_tcase, struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = samba_cmdline_get_creds(); + + status = torture_rpc_connection(tctx, + &(tcase_data->pipe), + tcase->table); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + /* XXX required, otherwise ndr out ptrs are not allocated */ + tcase_data->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC; + + return true; +} + +/* + testing of FSRVP (FSS agent) +*/ +struct torture_suite *torture_rpc_fsrvp(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "fsrvp"); + + struct torture_rpc_tcase *tcase + = torture_suite_add_rpc_iface_tcase(suite, "fsrvp", + &ndr_table_FileServerVssAgent); + /* override torture_rpc_setup() to set DCERPC_NDR_REF_ALLOC */ + tcase->tcase.setup = fsrvp_rpc_setup; + + torture_rpc_tcase_add_test(tcase, "share_sd", + test_fsrvp_share_sd); + torture_rpc_tcase_add_test(tcase, "enum_created", + test_fsrvp_enum_created); + torture_rpc_tcase_add_test(tcase, "sc_share_io", + test_fsrvp_sc_share_io); + torture_rpc_tcase_add_test(tcase, "bad_id", + test_fsrvp_bad_id); + torture_rpc_tcase_add_test(tcase, "sc_set_abort", + test_fsrvp_sc_set_abort); + torture_rpc_tcase_add_test(tcase, "create_simple", + test_fsrvp_sc_create_simple); + torture_rpc_tcase_add_test(tcase, "set_ctx", + test_fsrvp_set_ctx); + torture_rpc_tcase_add_test(tcase, "get_version", + test_fsrvp_get_version); + torture_rpc_tcase_add_test(tcase, "is_path_supported", + test_fsrvp_is_path_supported); + torture_rpc_tcase_add_test(tcase, "seq_timeout", + test_fsrvp_seq_timeout); + + return suite; +} diff --git a/source4/torture/rpc/handles.c b/source4/torture/rpc/handles.c new file mode 100644 index 0000000..7c108e5 --- /dev/null +++ b/source4/torture/rpc/handles.c @@ -0,0 +1,622 @@ +/* + Unix SMB/CIFS implementation. + + test suite for behaviour of rpc policy handles + + Copyright (C) Andrew Tridgell 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_drsuapi_c.h" +#include "torture/rpc/torture_rpc.h" + +/* + this tests the use of policy handles between connections +*/ + +static bool test_handles_lsa(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct dcerpc_binding_handle *b1, *b2; + struct policy_handle handle; + struct policy_handle handle2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + struct lsa_Close c; + uint16_t system_name = '\\'; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-LSARPC\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + b1 = p1->binding_handle; + + status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + b2 = p2->binding_handle; + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r), + "OpenPolicy failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + c.in.handle = &handle; + c.out.handle = &handle2; + + status = dcerpc_lsa_Close_r(b2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b1, mem_ctx, &c), + "Close failed"); + torture_assert_ntstatus_ok(torture, c.out.result, "closing policy handle on p1"); + + status = dcerpc_lsa_Close_r(b1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_lsa_shared(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5; + struct dcerpc_binding_handle *b1, *b2, *b3, *b4; + struct policy_handle handle; + struct policy_handle handle2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + struct lsa_Close c; + struct lsa_QuerySecurity qsec; + struct sec_desc_buf *sdbuf = NULL; + uint16_t system_name = '\\'; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-LSARPC-SHARED\n"); + + torture_comment(torture, "connect lsa pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + b1 = p1->binding_handle; + + transport = p1->conn->transport.transport; + assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding); + + torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id); + + torture_comment(torture, "connect lsa pipe2\n"); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2"); + b2 = p2->binding_handle; + + torture_comment(torture, "got assoc_group_id[0x%08X] for p2\n", + dcerpc_binding_get_assoc_group_id(p2->binding)); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_comment(torture, "open lsa policy handle\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r), + "OpenPolicy failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + /* + * connect p3 after the policy handle is opened + */ + torture_comment(torture, "connect lsa pipe3 after the policy handle is opened\n"); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe3"); + b3 = p3->binding_handle; + + qsec.in.handle = &handle; + qsec.in.sec_info = 0; + qsec.out.sdbuf = &sdbuf; + c.in.handle = &handle; + c.out.handle = &handle2; + + /* + * use policy handle on all 3 connections + */ + torture_comment(torture, "use the policy handle on p1,p2,p3\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "use policy handle on p1"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "use policy handle on p2"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "use policy handle on p3"); + + /* + * close policy handle on connection 2 and the others get a fault + */ + torture_comment(torture, "close the policy handle on p2 others get a fault\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b2, mem_ctx, &c), + "Close failed"); + torture_assert_ntstatus_equal(torture, c.out.result, NT_STATUS_OK, + "closing policy handle on p2"); + + status = dcerpc_lsa_Close_r(b1, mem_ctx, &c); + + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + status = dcerpc_lsa_Close_r(b3, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p3"); + + status = dcerpc_lsa_Close_r(b2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p2 again"); + + /* + * open a new policy handle on p3 + */ + torture_comment(torture, "open a new policy handle on p3\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b3, mem_ctx, &r), + "OpenPolicy failed"); + torture_assert_ntstatus_equal(torture, r.out.result, NT_STATUS_OK, + "open policy handle on p3"); + + /* + * use policy handle on all 3 connections + */ + torture_comment(torture, "use the policy handle on p1,p2,p3\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec), + "Query Security failed"); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p1"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec), + "Query Security failed"); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p2"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec), + "Query Security failed"); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "use policy handle on p3"); + + /* + * close policy handle on connection 2 and the others get a fault + */ + torture_comment(torture, "close the policy handle on p2 others get a fault\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b2, mem_ctx, &c), + "Close failed"); + torture_assert_ntstatus_equal(torture, c.out.result, NT_STATUS_OK, + "closing policy handle on p2"); + + status = dcerpc_lsa_Close_r(b1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + status = dcerpc_lsa_Close_r(b3, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p3"); + + status = dcerpc_lsa_Close_r(b2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p2 again"); + + /* + * open a new policy handle + */ + torture_comment(torture, "open a new policy handle on p1 and use it\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r), + "OpenPolicy failed"); + torture_assert_ntstatus_equal(torture, r.out.result, NT_STATUS_OK, + "open 2nd policy handle on p1"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "QuerySecurity handle on p1"); + + /* close first connection */ + torture_comment(torture, "disconnect p1\n"); + talloc_free(p1); + smb_msleep(5); + + /* + * and it's still available on p2,p3 + */ + torture_comment(torture, "use policy handle on p2,p3\n"); + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "QuerySecurity handle on p2 after p1 was disconnected"); + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "QuerySecurity handle on p3 after p1 was disconnected"); + + /* + * now open p4 + * and use the handle on it + */ + torture_comment(torture, "connect lsa pipe4 and use policy handle\n"); + status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe4"); + b4 = p4->binding_handle; + + torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b4, mem_ctx, &qsec), + "QuerySecurity failed"); + torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK, + "using policy handle on p4"); + + /* + * now close p2,p3,p4 + * without closing the policy handle + */ + torture_comment(torture, "disconnect p2,p3,p4\n"); + talloc_free(p2); + talloc_free(p3); + talloc_free(p4); + smb_msleep(10); + + /* + * now open p5 + */ + torture_comment(torture, "connect lsa pipe5 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p5, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe5"); + + talloc_free(mem_ctx); + + return true; +} + + +static bool test_handles_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct dcerpc_binding_handle *b1, *b2; + struct policy_handle handle; + struct policy_handle handle2; + struct samr_Connect r; + struct samr_Close c; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-SAMR\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + b1 = p1->binding_handle; + + status = torture_rpc_connection(torture, &p2, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe2"); + b2 = p2->binding_handle; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &handle; + + torture_assert_ntstatus_ok(torture, dcerpc_samr_Connect_r(b1, mem_ctx, &r), + "Connect failed"); + torture_assert_ntstatus_ok(torture, r.out.result, "opening policy handle on p1"); + + c.in.handle = &handle; + c.out.handle = &handle2; + + status = dcerpc_samr_Close_r(b2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + torture_assert_ntstatus_ok(torture, dcerpc_samr_Close_r(b1, mem_ctx, &c), + "Close failed"); + torture_assert_ntstatus_ok(torture, c.out.result, "closing policy handle on p1"); + + status = dcerpc_samr_Close_r(b1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_mixed_shared(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5, *p6; + struct dcerpc_binding_handle *b1, *b2; + struct policy_handle handle; + struct policy_handle handle2; + struct samr_Connect r; + struct lsa_Close lc; + struct samr_Close sc; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-MIXED-SHARED\n"); + + torture_comment(torture, "connect samr pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + b1 = p1->binding_handle; + + transport = p1->conn->transport.transport; + assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding); + + torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id); + + torture_comment(torture, "connect lsa pipe2\n"); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2"); + b2 = p2->binding_handle; + + torture_comment(torture, "got assoc_group_id[0x%08X] for p2\n", + dcerpc_binding_get_assoc_group_id(p2->binding)); + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &handle; + + torture_comment(torture, "samr_Connect to open a policy handle on samr p1\n"); + torture_assert_ntstatus_ok(torture, dcerpc_samr_Connect_r(b1, mem_ctx, &r), + "Connect failed"); + torture_assert_ntstatus_ok(torture, r.out.result, "opening policy handle on p1"); + + lc.in.handle = &handle; + lc.out.handle = &handle2; + sc.in.handle = &handle; + sc.out.handle = &handle2; + + torture_comment(torture, "use policy handle on lsa p2 - should fail\n"); + status = dcerpc_lsa_Close_r(b2, mem_ctx, &lc); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing handle on lsa p2"); + + torture_comment(torture, "closing policy handle on samr p1\n"); + torture_assert_ntstatus_ok(torture, dcerpc_samr_Close_r(b1, mem_ctx, &sc), + "Close failed"); + torture_assert_ntstatus_ok(torture, sc.out.result, "closing policy handle on p1"); + + talloc_free(p1); + talloc_free(p2); + smb_msleep(10); + + torture_comment(torture, "connect samr pipe3 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe3"); + + torture_comment(torture, "connect lsa pipe4 - should fail\n"); + status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe4"); + + /* + * We use ~assoc_group_id instead of p1->assoc_group_id, because + * this way we are less likely to use an id which is already in use. + */ + assoc_group_id = ~assoc_group_id; + torture_comment(torture, "connect samr pipe5 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p5, &ndr_table_samr, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe5"); + + torture_comment(torture, "connect lsa pipe6 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p6, &ndr_table_lsarpc, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening lsa pipe6"); + + talloc_free(mem_ctx); + + return true; +} + +static bool test_handles_random_assoc(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2, *p3; + TALLOC_CTX *mem_ctx = talloc_new(torture); + enum dcerpc_transport_t transport; + uint32_t assoc_group_id; + + torture_comment(torture, "RPC-HANDLE-RANDOM-ASSOC\n"); + + torture_comment(torture, "connect samr pipe1\n"); + status = torture_rpc_connection(torture, &p1, &ndr_table_samr); + torture_assert_ntstatus_ok(torture, status, "opening samr pipe1"); + + torture_comment(torture, "pipe1 uses assoc_group_id[0x%08X]\n", + dcerpc_binding_get_assoc_group_id(p1->binding)); + + transport = p1->conn->transport.transport; + /* + * We use ~p1->assoc_group_id instead of p1->assoc_group_id, because + * this way we are less likely to use an id which is already in use. + * + * And make sure it doesn't wrap. + */ + assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding); + assoc_group_id = ~MIN(assoc_group_id, UINT32_MAX - 3); + + torture_comment(torture, "connect samr pipe2 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p2, &ndr_table_samr, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe2"); + + torture_comment(torture, "connect samr pipe3 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id); + status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr, + transport, + assoc_group_id, + 0); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL, + "opening samr pipe3"); + + talloc_free(mem_ctx); + + return true; +} + + +static bool test_handles_drsuapi(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct dcerpc_binding_handle *b1, *b2; + struct policy_handle handle; + struct policy_handle handle2; + struct GUID bind_guid; + struct drsuapi_DsBind r; + struct drsuapi_DsUnbind c; + TALLOC_CTX *mem_ctx = talloc_new(torture); + + torture_comment(torture, "RPC-HANDLE-DRSUAPI\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_drsuapi); + torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); + b1 = p1->binding_handle; + + status = torture_rpc_connection(torture, &p2, &ndr_table_drsuapi); + torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); + b2 = p2->binding_handle; + + GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); + + r.in.bind_guid = &bind_guid; + r.in.bind_info = NULL; + r.out.bind_handle = &handle; + + status = dcerpc_drsuapi_DsBind_r(b1, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "drsuapi_DsBind not supported - skipping\n"); + talloc_free(mem_ctx); + return true; + } + + c.in.bind_handle = &handle; + c.out.bind_handle = &handle2; + + status = dcerpc_drsuapi_DsUnbind_r(b2, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p2"); + + status = dcerpc_drsuapi_DsUnbind_r(b1, mem_ctx, &c); + torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); + + status = dcerpc_drsuapi_DsUnbind_r(b1, mem_ctx, &c); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "closing policy handle on p1 again"); + + talloc_free(mem_ctx); + + return true; +} + +struct torture_suite *torture_rpc_handles(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + + suite = torture_suite_create(mem_ctx, "handles"); + torture_suite_add_simple_test(suite, "lsarpc", test_handles_lsa); + torture_suite_add_simple_test(suite, "lsarpc-shared", test_handles_lsa_shared); + torture_suite_add_simple_test(suite, "samr", test_handles_samr); + torture_suite_add_simple_test(suite, "mixed-shared", test_handles_mixed_shared); + torture_suite_add_simple_test(suite, "random-assoc", test_handles_random_assoc); + torture_suite_add_simple_test(suite, "drsuapi", test_handles_drsuapi); + return suite; +} diff --git a/source4/torture/rpc/initshutdown.c b/source4/torture/rpc/initshutdown.c new file mode 100644 index 0000000..28eaacd --- /dev/null +++ b/source4/torture/rpc/initshutdown.c @@ -0,0 +1,116 @@ +/* + Unix SMB/CIFS implementation. + test suite for initshutdown operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Jelmer Vernooij 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_initshutdown_c.h" +#include "torture/rpc/torture_rpc.h" + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} + + +static bool test_Abort(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_Abort r; + NTSTATUS status; + uint16_t server = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server = &server; + + status = dcerpc_initshutdown_Abort_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, + "initshutdown_Abort failed"); + + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Abort failed"); + + return true; +} + +static bool test_Init(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_Init r; + NTSTATUS status; + uint16_t hostname = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.do_reboot = 1; + + status = dcerpc_initshutdown_Init_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "initshutdown_Init failed"); + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Init failed"); + + return test_Abort(tctx, p); +} + +static bool test_InitEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct initshutdown_InitEx r; + NTSTATUS status; + uint16_t hostname = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.do_reboot = 1; + r.in.reason = 0; + + status = dcerpc_initshutdown_InitEx_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "initshutdown_InitEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "initshutdown_InitEx failed"); + + return test_Abort(tctx, p); +} + + +struct torture_suite *torture_rpc_initshutdown(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "initshutdown"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "initshutdown", + &ndr_table_initshutdown); + + test = torture_rpc_tcase_add_test(tcase, "Init", test_Init); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "InitEx", test_InitEx); + test->dangerous = true; + + return suite; +} diff --git a/source4/torture/rpc/iremotewinspool.c b/source4/torture/rpc/iremotewinspool.c new file mode 100644 index 0000000..3a7ee64 --- /dev/null +++ b/source4/torture/rpc/iremotewinspool.c @@ -0,0 +1,1090 @@ +/* + Unix SMB/CIFS implementation. + test suite for iremotewinspool rpc operations + + Copyright (C) Guenther Deschner 2013,2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_winspool.h" +#include "librpc/gen_ndr/ndr_winspool_c.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/registry/util_reg.h" +#include "torture/rpc/iremotewinspool_common.h" + +static bool torture_rpc_iremotewinspool_setup_common(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + const char *printer_name; + struct spoolss_UserLevel1 client_info; + struct dcerpc_binding *binding; + + torture_assert_ntstatus_ok(tctx, + GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &t->object_uuid), + "failed to parse GUID"); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to retrieve torture binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_object(binding, t->object_uuid), + "failed to set object_uuid"); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection_with_binding(tctx, binding, &t->iremotewinspool_pipe, &ndr_table_iremotewinspool), + "Error connecting to server"); + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(t->iremotewinspool_pipe)); + + client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user"); + + torture_assert(tctx, + test_AsyncOpenPrinter_byprinter(tctx, t, + t->iremotewinspool_pipe, printer_name, + client_info, &t->server_handle), + "failed to open printserver"); + torture_assert(tctx, + test_get_environment(tctx, + t->iremotewinspool_pipe->binding_handle, + &t->server_handle, &t->environment), + "failed to get environment"); + + return true; +} + +static bool torture_rpc_iremotewinspool_setup(struct torture_context *tctx, + void **data) +{ + struct test_iremotewinspool_context *t; + + *data = t = talloc_zero(tctx, struct test_iremotewinspool_context); + + return torture_rpc_iremotewinspool_setup_common(tctx, t); +} + +static bool torture_rpc_iremotewinspool_teardown_common(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + + test_AsyncClosePrinter_byhandle(tctx, t, t->iremotewinspool_pipe, &t->server_handle); + + return true; +} + +static bool torture_rpc_iremotewinspool_teardown(struct torture_context *tctx, + void *data) +{ + struct test_iremotewinspool_context *t = talloc_get_type(data, struct test_iremotewinspool_context); + bool ret; + + ret = torture_rpc_iremotewinspool_teardown_common(tctx, t); + talloc_free(t); + + return ret; +} + +static bool test_AsyncClosePrinter(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + const char *printer_name; + struct spoolss_UserLevel1 client_info; + struct policy_handle handle; + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user"); + + torture_assert(tctx, + test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle), + "failed to test AsyncOpenPrinter"); + + torture_assert(tctx, + test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle), + "failed to test AsyncClosePrinter"); + + return true; +} + +static bool test_AsyncOpenPrinter(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + const char *printer_name; + struct spoolss_UserLevel1 client_info; + struct policy_handle handle; + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user"); + + torture_assert(tctx, + test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle), + "failed to test AsyncOpenPrinter"); + + test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle); + + return true; +} + +/* + * Validate the result of AsyncOpenPrinter calls based on client info + * build number. Windows Server 2016 rejects an advertised build + * number less than 6000(Windows Vista and Windows Server 2008, or older) + */ +static bool test_AsyncOpenPrinterValidateBuildNumber(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + const char *printer_name; + struct spoolss_UserLevel1 client_info; + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_DevmodeContainer devmode_ctr; + struct spoolss_UserLevelCtr client_info_ctr = { + .level = 1, + }; + uint32_t access_mask = SERVER_ALL_ACCESS; + struct winspool_AsyncOpenPrinter r; + NTSTATUS status; + bool ok = false; + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert_not_null(tctx, printer_name, "Cannot allocate memory"); + + /* fail with Windows 2000 build number */ + client_info = test_get_client_info(tctx, WIN_2000, 3, SPOOLSS_MINOR_VERSION_0, + "testclient_machine", "testclient_user"); + + ZERO_STRUCT(devmode_ctr); + + client_info_ctr.user_info.level1 = &client_info; + + r.in.pPrinterName = printer_name; + r.in.pDatatype = NULL; + r.in.pDevModeContainer = &devmode_ctr; + r.in.AccessRequired = access_mask; + r.in.pClientInfo = &client_info_ctr; + r.out.pHandle = &handle; + + status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "AsyncOpenPrinter failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED, + "AsyncOpenPrinter should have failed"); + + /* succeed with Windows 7 build number */ + client_info = test_get_client_info(tctx, WIN_7, 6, 1, + "testclient_machine", "testclient_user"); + client_info_ctr.user_info.level1 = &client_info; + r.in.pClientInfo = &client_info_ctr; + + status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "AsyncOpenPrinter failed"); + torture_assert_werr_ok(tctx, r.out.result, + "AsyncOpenPrinter failed"); + + ok = test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle); + torture_assert(tctx, ok, "failed to AsyncClosePrinter handle"); + + return true; + +} + +static struct spoolss_NotifyOption *setup_printserver_NotifyOption(struct torture_context *tctx) +{ + struct spoolss_NotifyOption *o; + + o = talloc_zero(tctx, struct spoolss_NotifyOption); + if (o == NULL) { + return NULL; + } + + o->version = 2; + o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH; + + o->count = 2; + o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count); + if (o->types == NULL) { + talloc_free(o); + return NULL; + } + + o->types[0].type = PRINTER_NOTIFY_TYPE; + o->types[0].count = 1; + o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count); + if (o->types[0].fields == NULL) { + talloc_free(o); + return NULL; + } + o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_SERVER_NAME; + + o->types[1].type = JOB_NOTIFY_TYPE; + o->types[1].count = 1; + o->types[1].fields = talloc_array(o->types, union spoolss_Field, o->types[1].count); + if (o->types[1].fields == NULL) { + talloc_free(o); + return NULL; + } + o->types[1].fields[0].field = JOB_NOTIFY_FIELD_MACHINE_NAME; + + return o; +} + +static bool test_SyncUnRegisterForRemoteNotifications_args(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *notify_handle) +{ + struct winspool_SyncUnRegisterForRemoteNotifications r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.phRpcHandle = notify_handle; + r.out.phRpcHandle = notify_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_SyncUnRegisterForRemoteNotifications_r(b, tctx, &r), + "SyncUnRegisterForRemoteNotifications failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "SyncUnRegisterForRemoteNotifications failed"); + + return true; +} + +static bool test_SyncRegisterForRemoteNotifications_args(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *server_handle, + struct policy_handle *notify_handle); + +static bool test_SyncUnRegisterForRemoteNotifications(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + struct policy_handle notify_handle; + + torture_assert(tctx, + test_SyncRegisterForRemoteNotifications_args(tctx, + ctx->iremotewinspool_pipe, + &ctx->server_handle, + ¬ify_handle), + "failed to test SyncRegisterForRemoteNotifications"); + + torture_assert(tctx, + test_SyncUnRegisterForRemoteNotifications_args(tctx, + ctx->iremotewinspool_pipe, + ¬ify_handle), + "failed to test UnSyncRegisterForRemoteNotifications"); + + return true; +} + +static bool test_SyncRegisterForRemoteNotifications_args(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *server_handle, + struct policy_handle *notify_handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + + struct winspool_SyncRegisterForRemoteNotifications r; + struct winspool_PrintPropertiesCollection NotifyFilter; + struct winspool_PrintNamedProperty *c; + struct spoolss_NotifyOption *options; + + ZERO_STRUCT(NotifyFilter); + + options = setup_printserver_NotifyOption(tctx); + torture_assert(tctx, options, "out of memory"); + + c = talloc_zero_array(tctx, struct winspool_PrintNamedProperty, 4); + torture_assert(tctx, c, "out of memory"); + + c[0].propertyName = "RemoteNotifyFilter Flags"; + c[0].propertyValue.PropertyType = winspool_PropertyTypeInt32; + c[0].propertyValue.value.propertyInt32 = 0xff; + + c[1].propertyName = "RemoteNotifyFilter Options"; + c[1].propertyValue.PropertyType = winspool_PropertyTypeInt32; + c[1].propertyValue.value.propertyInt32 = 0; + + c[2].propertyName = "RemoteNotifyFilter Color"; + c[2].propertyValue.PropertyType = winspool_PropertyTypeInt32; + c[2].propertyValue.value.propertyInt32 = 0; + + c[3].propertyName = "RemoteNotifyFilter NotifyOptions"; + c[3].propertyValue.PropertyType = winspool_PropertyTypeNotificationOptions; + c[3].propertyValue.value.propertyOptionsContainer.pOptions = options; + + NotifyFilter.numberOfProperties = 4; + NotifyFilter.propertiesCollection = c; + + r.in.hPrinter = *server_handle; + r.in.pNotifyFilter = &NotifyFilter; + r.out.phRpcHandle = notify_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_SyncRegisterForRemoteNotifications_r(b, tctx, &r), + "SyncRegisterForRemoteNotifications failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "SyncRegisterForRemoteNotifications failed"); + + return true; +} + +static bool test_SyncRegisterForRemoteNotifications(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + struct policy_handle notify_handle; + + torture_assert(tctx, + test_SyncRegisterForRemoteNotifications_args(tctx, + ctx->iremotewinspool_pipe, + &ctx->server_handle, + ¬ify_handle), + "failed to test SyncRegisterForRemoteNotifications"); + + test_SyncUnRegisterForRemoteNotifications_args(tctx, ctx->iremotewinspool_pipe, ¬ify_handle); + + return true; +} + +static bool test_AsyncUploadPrinterDriverPackage(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct winspool_AsyncUploadPrinterDriverPackage r; + uint32_t pcchDestInfPath = 0; + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.pszInfPath = ""; + r.in.pszEnvironment = ""; + r.in.dwFlags = 0; + r.in.pszDestInfPath = NULL; + r.in.pcchDestInfPath = &pcchDestInfPath; + r.out.pszDestInfPath = NULL; + r.out.pcchDestInfPath = &pcchDestInfPath; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r), + "AsyncUploadPrinterDriverPackage failed"); + torture_assert_hresult_equal(tctx, r.out.result, HRES_E_INVALIDARG, + "AsyncUploadPrinterDriverPackage failed"); + + pcchDestInfPath = 260; + r.in.pszDestInfPath = talloc_zero(tctx, const char); + r.out.pszDestInfPath = talloc_zero(tctx, const char); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r), + "AsyncUploadPrinterDriverPackage failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT, + "AsyncUploadPrinterDriverPackage failed"); + + r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r), + "AsyncUploadPrinterDriverPackage failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND, + "AsyncUploadPrinterDriverPackage failed"); + + r.in.pszInfPath = "\\\\mthelena\\print$\\x64\\{BD443844-ED00-4D96-8CAE-95E49492312A}\\prnbrcl1.inf"; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r), + "AsyncUploadPrinterDriverPackage failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND, + "AsyncUploadPrinterDriverPackage failed"); + + return true; +} + +static bool test_AsyncEnumPrinters(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct winspool_AsyncEnumPrinters r; + uint32_t levels[] = { 1, 2, /*3,*/ 4, 5 }; + int i; + + uint32_t needed; + uint32_t returned; + + for (i = 0; i < ARRAY_SIZE(levels); i++) { + + r.in.Flags = PRINTER_ENUM_LOCAL; + r.in.pName = NULL; + r.in.Level = levels[i]; + r.in.cbBuf = 0; + r.in.pPrinterEnum = NULL; + r.out.pcbNeeded = &needed; + r.out.pcReturned = &returned; + r.out.pPrinterEnum = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncEnumPrinters_r(b, tctx, &r), + "AsyncEnumPrinters failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "AsyncEnumPrinters failed"); + + r.in.cbBuf = needed; + r.in.pPrinterEnum = talloc_zero_array(tctx, uint8_t, r.in.cbBuf); + r.out.pPrinterEnum = r.in.pPrinterEnum; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncEnumPrinters_r(b, tctx, &r), + "AsyncEnumPrinters failed"); + torture_assert_werr_ok(tctx, r.out.result, + "AsyncEnumPrinters failed"); + } + + return true; +} + +static bool test_AsyncGetPrinterData(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB blob; + const char *s; + bool ok; + + uint32_t pType; + uint32_t pcbNeeded; + uint8_t *pData; + + torture_assert(tctx, + test_AsyncGetPrinterData_args(tctx, b, &ctx->server_handle, + "MajorVersion", + &pType, &pData, &pcbNeeded), + "failed to check for MajorVersion"); + + torture_assert_int_equal(tctx, pcbNeeded, 4, "pcbNeeded"); + torture_assert_int_equal(tctx, pType, REG_DWORD, "pType"); + torture_assert_int_equal(tctx, IVAL(pData, 0), 3, "pData"); + + torture_assert(tctx, + test_AsyncGetPrinterData_args(tctx, b, &ctx->server_handle, + "Architecture", + &pType, &pData, &pcbNeeded), + "failed to check for Architecture"); + + blob = data_blob_const(pData, pcbNeeded); + + torture_assert_int_equal(tctx, pType, REG_SZ, "pType"); + torture_assert(tctx, pull_reg_sz(tctx, &blob, &s), ""); + ok = strequal(s, SPOOLSS_ARCHITECTURE_x64) || strequal(s, SPOOLSS_ARCHITECTURE_NT_X86); + torture_assert(tctx, ok, "unexpected architecture returned"); + + return true; +} + +static bool test_AsyncCorePrinterDriverInstalled(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct winspool_AsyncCorePrinterDriverInstalled r; + int32_t pbDriverInstalled; + struct GUID guid; + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.pszEnvironment = ""; + r.in.CoreDriverGUID = GUID_zero(); + r.in.ftDriverDate = 0; + r.in.dwlDriverVersion = 0; + r.out.pbDriverInstalled = &pbDriverInstalled; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT, + "AsyncCorePrinterDriverInstalled failed"); + + r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false, + "unexpected driver installed"); + + r.in.CoreDriverGUID = GUID_random(); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false, + "unexpected driver installed"); + + torture_assert_ntstatus_ok(tctx, + GUID_from_string(SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV, &guid), ""); + + r.in.CoreDriverGUID = guid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true, + "xps core driver not installed?"); + + r.in.dwlDriverVersion = 0xffffffff; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true, + "xps core driver not installed?"); + + r.in.dwlDriverVersion = 1234; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true, + "xps core driver not installed?"); + + r.in.ftDriverDate = unix_timespec_to_nt_time(timespec_current()); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false, + "driver too old ?"); + + r.in.dwlDriverVersion = 0; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r), + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "AsyncCorePrinterDriverInstalled failed"); + torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false, + "unexpected driver installed"); + + return true; +} + +static bool test_get_core_printer_drivers_arch_guid(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *architecture, + const char *guid_str, + const char **package_id) +{ + struct winspool_AsyncGetCorePrinterDrivers r; + DATA_BLOB blob; + const char **s; + struct dcerpc_binding_handle *b = p->binding_handle; + + s = talloc_zero_array(tctx, const char *, 2); + s[0] = guid_str; + + torture_assert(tctx, + push_reg_multi_sz(tctx, &blob, s), + "push_reg_multi_sz failed"); + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.pszEnvironment = architecture; + r.in.cchCoreDrivers = blob.length/2; + r.in.pszzCoreDriverDependencies = (uint16_t *)blob.data; + r.in.cCorePrinterDrivers = 1; + r.out.pCorePrinterDrivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.cCorePrinterDrivers); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncGetCorePrinterDrivers_r(b, tctx, &r), + "winspool_AsyncCorePrinterDrivers failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "winspool_AsyncCorePrinterDrivers failed"); + + if (package_id) { + *package_id = r.out.pCorePrinterDrivers[0].szPackageID; + } + + return true; +} + +static bool test_AsyncDeletePrintDriverPackage(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct winspool_AsyncDeletePrinterDriverPackage r; + + const char *architectures[] = { +/* SPOOLSS_ARCHITECTURE_NT_X86, */ + SPOOLSS_ARCHITECTURE_x64 + }; + int i; + + for (i=0; i < ARRAY_SIZE(architectures); i++) { + + const char *package_id; + + torture_assert(tctx, + test_get_core_printer_drivers_arch_guid(tctx, p, + architectures[i], + SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV, + &package_id), + "failed to get core printer driver"); + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.pszEnvironment = ""; + r.in.pszInfPath = ""; + + torture_comment(tctx, "Testing AsyncDeletePrinterDriverPackage(%s, %s, %s)\n", + r.in.pszServer, architectures[i], package_id); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r), + "AsyncDeletePrinterDriverPackage failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_NOT_FOUND, + "AsyncDeletePrinterDriverPackage failed"); + + r.in.pszInfPath = package_id; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r), + "AsyncDeletePrinterDriverPackage failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT, + "AsyncDeletePrinterDriverPackage failed"); + + r.in.pszEnvironment = architectures[i]; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r), + "AsyncDeletePrinterDriverPackage failed"); + torture_assert_hresult_equal(tctx, r.out.result, HRES_E_ACCESSDENIED, + "AsyncDeletePrinterDriverPackage failed"); + } + + return true; +} + +static bool test_AsyncGetPrinterDriverDirectory(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct winspool_AsyncGetPrinterDriverDirectory r; + uint32_t pcbNeeded; + DATA_BLOB blob; + const char *s; + + r.in.pName = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.pEnvironment = ctx->environment; + r.in.Level = 1; + r.in.cbBuf = 0x200; + r.in.pDriverDirectory = talloc_zero_array(tctx, uint8_t, r.in.cbBuf); + r.out.pcbNeeded = &pcbNeeded; + r.out.pDriverDirectory = r.in.pDriverDirectory; + + torture_comment(tctx, "Testing AsyncGetPrinterDriverDirectory(%s, %s)\n", + r.in.pName, r.in.pEnvironment); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winspool_AsyncGetPrinterDriverDirectory_r(b, tctx, &r), + "AsyncGetPrinterDriverDirectory failed"); + torture_assert_werr_ok(tctx, r.out.result, + "AsyncGetPrinterDriverDirectory failed"); + + blob = data_blob_const(r.out.pDriverDirectory, pcbNeeded); + + torture_assert(tctx, + pull_reg_sz(tctx, &blob, &s), + "failed to pull reg_sz"); + + torture_comment(tctx, "got: %s\n", s); + + return true; +} + +/* + * Test if one can close a printserver handle that has been acquired via + * winspool_AsyncOpenPrinter with a spoolss_ClosePrinter operation. + */ + +static bool test_OpenPrinter(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + const char *printer_name; + struct policy_handle handle; + struct dcerpc_pipe *s; + struct dcerpc_binding *binding; + struct spoolss_UserLevel1 client_info; + struct spoolss_ClosePrinter r; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to get binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_transport(binding, NCACN_NP), + "failed to set ncacn_np transport"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_object(binding, GUID_zero()), + "failed to set object uuid to zero"); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection_with_binding(tctx, binding, &s, &ndr_table_spoolss), + "failed to connect to spoolss"); + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user"); + + torture_assert(tctx, + test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle), + "failed to open printserver via winspool"); + + + r.in.handle = &handle; + r.out.handle = &handle; + + torture_assert_ntstatus_equal(tctx, + dcerpc_spoolss_ClosePrinter_r(s->binding_handle, tctx, &r), + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "ClosePrinter failed"); + + talloc_free(s); + + return true; +} + +static bool test_object_one_uuid(struct torture_context *tctx, + const struct GUID *object_uuid, + NTSTATUS expected_status, + uint32_t expected_fault_code) +{ + const char *printer_name; + struct spoolss_UserLevel1 client_info; + struct dcerpc_binding *binding; + struct dcerpc_pipe *p; + struct policy_handle server_handle; + + torture_comment(tctx, "Testing with object_uuid: %s\n", + GUID_string(tctx, object_uuid)); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to retrieve torture binding"); + + if (object_uuid) { + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_object(binding, *object_uuid), + "failed to set object_uuid"); + } + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection_with_binding(tctx, binding, &p, + &ndr_table_iremotewinspool), + "Error connecting to server"); + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert(tctx, printer_name, "out of memory"); + + client_info = test_get_client_info(tctx, + WIN_7, 6, 1, "testclient_machine", "testclient_user"); + + torture_assert(tctx, + test_AsyncOpenPrinter_byprinter_expect(tctx, NULL, + p, + printer_name, + SEC_FLAG_MAXIMUM_ALLOWED, + client_info, + expected_status, + WERR_OK, + expected_fault_code, + &server_handle), + "failed to open printserver"); + if (NT_STATUS_IS_OK(expected_status)) { + test_AsyncClosePrinter_byhandle(tctx, NULL, p, &server_handle); + } + + talloc_free(p); + + return true; +} + +static bool test_object_uuid(struct torture_context *tctx, + void *private_data) +{ + struct GUID object_uuid; + + torture_assert(tctx, + test_object_one_uuid(tctx, NULL, + NT_STATUS_RPC_NOT_RPC_ERROR, + DCERPC_NCA_S_UNSUPPORTED_TYPE), + "failed to test NULL object uuid"); + + object_uuid = GUID_zero(); + torture_assert(tctx, + test_object_one_uuid(tctx, &object_uuid, + NT_STATUS_RPC_NOT_RPC_ERROR, + DCERPC_NCA_S_UNSUPPORTED_TYPE), + "failed to test zeroed object uuid"); + + object_uuid = GUID_random(); + torture_assert(tctx, + test_object_one_uuid(tctx, &object_uuid, + NT_STATUS_RPC_NOT_RPC_ERROR, + DCERPC_NCA_S_UNSUPPORTED_TYPE), + "failed to test random object uuid"); + + GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &object_uuid); + torture_assert(tctx, + test_object_one_uuid(tctx, &object_uuid, + NT_STATUS_OK, + 0), + "failed to test IREMOTEWINSPOOL_OBJECT_GUID"); + + torture_assert(tctx, + test_object_one_uuid(tctx, &ndr_table_spoolss.syntax_id.uuid, + NT_STATUS_RPC_NOT_RPC_ERROR, + DCERPC_NCA_S_UNSUPPORTED_TYPE), + "failed to test spoolss interface uuid"); + + torture_assert(tctx, + test_object_one_uuid(tctx, &ndr_table_iremotewinspool.syntax_id.uuid, + NT_STATUS_RPC_NOT_RPC_ERROR, + DCERPC_NCA_S_UNSUPPORTED_TYPE), + "failed to test iremotewinspool interface uuid"); + + return true; +} + +static bool test_setup_binding_handle(struct torture_context *tctx, + struct GUID object_uuid, + enum dcerpc_transport_t transport, + const struct ndr_interface_table *table, + struct dcerpc_pipe **p) +{ + struct dcerpc_binding *binding; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to retrieve torture binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_object(binding, object_uuid), + "failed to set object_uuid"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_transport(binding, transport), + "failed to set transport"); + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection_with_binding(tctx, binding, p, table), + "Error connecting to server"); + + return true; +} + +static enum ndr_err_code ndr_push_inout_blob(DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + ndr_flags_type flags, + const void *p, + ndr_push_flags_fn_t fn) +{ + struct ndr_push *ndr; + ndr = ndr_push_init_ctx(mem_ctx); + NDR_ERR_HAVE_NO_MEMORY(ndr); + + NDR_CHECK_FREE(fn(ndr, flags, p)); + + *blob = ndr_push_blob(ndr); + talloc_steal(mem_ctx, blob->data); + talloc_free(ndr); + + return NDR_ERR_SUCCESS; +} + +static bool test_compare_spoolss(struct torture_context *tctx, + void *private_data) +{ + DATA_BLOB reply_iremotewinspool, reply_spoolss, request_spoolss; + struct dcerpc_pipe *iremotewinspool_pipe, *spoolss_pipe; + struct GUID object_uuid; + uint32_t out_flags; + NTSTATUS status; + struct spoolss_EnumPrinters r; + DATA_BLOB blob; + + /* setup two dcerpc pipes */ + + torture_assert_ntstatus_ok(tctx, + GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &object_uuid), + "failed to parse GUID"); + + torture_assert(tctx, + test_setup_binding_handle(tctx, object_uuid, NCACN_IP_TCP, + &ndr_table_iremotewinspool, &iremotewinspool_pipe), + "failed to setup binding handle"); + + torture_assert(tctx, + test_setup_binding_handle(tctx, GUID_zero(), NCACN_NP, + &ndr_table_spoolss, &spoolss_pipe), + "failed to setup binding handle"); + + + /* create a spoolss enumprinters request */ + + ZERO_STRUCT(r); + + blob = data_blob_talloc_zero(tctx, 0x1000); + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(spoolss_pipe)); + r.in.level = 1; + r.in.buffer = &blob; + r.in.offered = blob.length; + + torture_assert_ndr_success(tctx, + ndr_push_inout_blob(&request_spoolss, tctx, NDR_IN | NDR_SET_VALUES, &r, + (ndr_push_flags_fn_t)ndr_push_spoolss_EnumPrinters), + "failed to push EnumPrinters request"); + + /* send same request to both endpoints */ + + status = dcerpc_binding_handle_raw_call(iremotewinspool_pipe->binding_handle, + NULL, + NDR_WINSPOOL_ASYNCENUMPRINTERS, + 0, /* in_flags */ + request_spoolss.data, + request_spoolss.length, + tctx, + &reply_iremotewinspool.data, + &reply_iremotewinspool.length, + &out_flags); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_binding_handle_raw_call failed"); + + status = dcerpc_binding_handle_raw_call(spoolss_pipe->binding_handle, + NULL, + NDR_SPOOLSS_ENUMPRINTERS, + 0, /* in_flags */ + request_spoolss.data, + request_spoolss.length, + tctx, + &reply_spoolss.data, + &reply_spoolss.length, + &out_flags); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_binding_handle_raw_call failed"); + + torture_assert_data_blob_equal(tctx, + reply_iremotewinspool, + reply_spoolss, + "unexpected difference in replies from spoolss and iremotewinspool servers"); + + talloc_free(iremotewinspool_pipe); + talloc_free(spoolss_pipe); + + return true; +} + +struct torture_suite *torture_rpc_iremotewinspool(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "iremotewinspool"); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver"); + + torture_tcase_set_fixture(tcase, + torture_rpc_iremotewinspool_setup, + torture_rpc_iremotewinspool_teardown); + + torture_tcase_add_simple_test(tcase, "AsyncOpenPrinter", test_AsyncOpenPrinter); + torture_tcase_add_simple_test(tcase, "SyncRegisterForRemoteNotifications", test_SyncRegisterForRemoteNotifications); + torture_tcase_add_simple_test(tcase, "SyncUnRegisterForRemoteNotifications", test_SyncUnRegisterForRemoteNotifications); + torture_tcase_add_simple_test(tcase, "AsyncClosePrinter", test_AsyncClosePrinter); + torture_tcase_add_simple_test(tcase, "AsyncUploadPrinterDriverPackage", test_AsyncUploadPrinterDriverPackage); + torture_tcase_add_simple_test(tcase, "AsyncEnumPrinters", test_AsyncEnumPrinters); + torture_tcase_add_simple_test(tcase, "AsyncGetPrinterData", test_AsyncGetPrinterData); + torture_tcase_add_simple_test(tcase, "AsyncCorePrinterDriverInstalled", test_AsyncCorePrinterDriverInstalled); + torture_tcase_add_simple_test(tcase, "AsyncDeletePrintDriverPackage", test_AsyncDeletePrintDriverPackage); + torture_tcase_add_simple_test(tcase, "AsyncGetPrinterDriverDirectory", test_AsyncGetPrinterDriverDirectory); + torture_tcase_add_simple_test(tcase, "AsyncOpenPrinterValidateBuildNumber", test_AsyncOpenPrinterValidateBuildNumber); + + tcase = torture_suite_add_tcase(suite, "handles"); + + torture_tcase_set_fixture(tcase, + torture_rpc_iremotewinspool_setup, + torture_rpc_iremotewinspool_teardown); + + torture_tcase_add_simple_test(tcase, "OpenPrinter", test_OpenPrinter); + + tcase = torture_suite_add_tcase(suite, "protocol"); + torture_tcase_add_simple_test(tcase, "object_uuid", test_object_uuid); + torture_tcase_add_simple_test(tcase, "compare_spoolss", test_compare_spoolss); + + return suite; +} diff --git a/source4/torture/rpc/iremotewinspool_common.c b/source4/torture/rpc/iremotewinspool_common.c new file mode 100644 index 0000000..d4dd19a --- /dev/null +++ b/source4/torture/rpc/iremotewinspool_common.c @@ -0,0 +1,269 @@ +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_winspool.h" +#include "librpc/gen_ndr/ndr_winspool_c.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/registry/util_reg.h" +#include "torture/rpc/iremotewinspool_common.h" +#include "lib/printer_driver/printer_driver.h" + +void init_winreg_String(struct winreg_String *name, const char *s) +{ + name->name = s; + if (s != NULL) { + name->name_len = 2 * (strlen_m(s) + 1); + name->name_size = name->name_len; + } else { + name->name_len = 0; + name->name_size = 0; + } +} + +struct spoolss_UserLevel1 test_get_client_info(struct torture_context *tctx, + enum client_os_version os, + enum spoolss_MajorVersion major_number, + enum spoolss_MinorVersion minor_number, + const char *machine, + const char *user) +{ + struct spoolss_UserLevel1 level1; + + level1.size = 28; + level1.client = talloc_asprintf(tctx, "\\\\%s", machine); + level1.user = user; + level1.processor = PROCESSOR_ARCHITECTURE_AMD64; + level1.major = major_number; + level1.minor = minor_number; + + if (os == WIN_SERVER_2016 || os == WIN_10) { + level1.build = 10586; + } else if (os == WIN_SERVER_2012 || os == WIN_8) { + level1.build = 9200; + } else if (os == WIN_SERVER_2008R2 || os == WIN_7) { + level1.build = 7007; + } else if (os == WIN_SERVER_2008 || os == WIN_VISTA) { + level1.build = 6000; + } else if (os == WIN_2000) { + level1.build = 1382; + } + + return level1; +} + +bool test_AsyncOpenPrinter_byprinter_expect(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + const char *printer_name, + uint32_t access_mask, + struct spoolss_UserLevel1 cinfo, + NTSTATUS expected_status, + WERROR expected_result, + uint32_t expected_fault_code, + struct policy_handle *handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_DevmodeContainer devmode_ctr; + struct spoolss_UserLevelCtr client_info_ctr; + struct winspool_AsyncOpenPrinter r; + NTSTATUS status; + bool ok = true; + + ZERO_STRUCT(r); + ZERO_STRUCT(devmode_ctr); + + client_info_ctr.level = 1; + client_info_ctr.user_info.level1 = &cinfo; + + r.in.pPrinterName = printer_name; + r.in.pDatatype = NULL; + r.in.pDevModeContainer = &devmode_ctr; + r.in.AccessRequired = access_mask; + r.in.pClientInfo = &client_info_ctr; + r.out.pHandle = handle; + + status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, expected_status, "AsyncOpenPrinter failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "AsyncOpenPrinter failed"); + torture_assert_u32_equal(tctx, p->last_fault_code, expected_fault_code, + "unexpected DCERPC fault code"); + + return ok; +} + +bool test_AsyncOpenPrinter_byprinter(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + const char *printer_name, + struct spoolss_UserLevel1 cinfo, + struct policy_handle *handle) +{ + return test_AsyncOpenPrinter_byprinter_expect(tctx, + ctx, + p, + printer_name, + SERVER_ALL_ACCESS, + cinfo, + NT_STATUS_OK, + WERR_OK, + 0, + handle); +} + +bool test_get_environment(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char **architecture) +{ + DATA_BLOB blob; + enum winreg_Type type; + uint8_t *data; + uint32_t needed; + bool ok; + + ok = test_AsyncGetPrinterData_args(tctx, b, handle, "Architecture", &type, &data, &needed); + torture_assert(tctx, ok, "failed to get Architecture"); + + torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type"); + + blob = data_blob_const(data, needed); + + torture_assert(tctx, + pull_reg_sz(tctx, &blob, architecture), + "failed to pull environment"); + + return true; +} + +bool test_AsyncClosePrinter_byhandle(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + + struct winspool_AsyncClosePrinter r; + NTSTATUS status; + bool ok = true; + + r.in.phPrinter = handle; + r.out.phPrinter = handle; + + status = dcerpc_winspool_AsyncClosePrinter_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncClosePrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "AsyncClosePrinter failed"); + +done: + + return ok; +} + +static bool test_AsyncGetPrinterData_checktype(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type *expected_type, + enum winreg_Type *type_p, + uint8_t **data_p, + uint32_t *needed_p) +{ + struct winspool_AsyncGetPrinterData r; + enum winreg_Type type; + uint32_t needed; + NTSTATUS status; + bool ok = true; + + r.in.hPrinter = *handle; + r.in.pValueName = value_name; + r.in.nSize = 0; + r.out.pType = &type; + r.out.pData = talloc_zero_array(tctx, uint8_t, r.in.nSize); + r.out.pcbNeeded = &needed; + + torture_comment(tctx, "Testing AsyncGetPrinterData(%s)\n", + r.in.pValueName); + + status = dcerpc_winspool_AsyncGetPrinterData_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncGetPrinterData failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + if (expected_type) { + torture_assert_int_equal(tctx, type, *expected_type, "unexpected type"); + } + r.in.nSize = needed; + r.out.pData = talloc_zero_array(tctx, uint8_t, r.in.nSize); + + status = dcerpc_winspool_AsyncGetPrinterData_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncGetPrinterData failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "AsyncGetPrinterData failed"); + + if (type_p) { + *type_p = type; + } + + if (data_p) { + *data_p = r.out.pData; + } + + if (needed_p) { + *needed_p = needed; + } + +done: + + return ok; +} + +bool test_AsyncGetPrinterData_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type *type_p, + uint8_t **data_p, + uint32_t *needed_p) +{ + return test_AsyncGetPrinterData_checktype(tctx, b, handle, + value_name, + NULL, + type_p, data_p, needed_p); +} + +/* Parse a driver inf file */ +bool parse_inf_driver(struct torture_context *tctx, + const char *driver_name, + const char *abs_inf_path, + const char *driver_arch, + const char *core_driver_inf, + struct spoolss_AddDriverInfo8 **_parsed_dinfo) +{ + struct spoolss_AddDriverInfo8 *drv_info; + const char *source_disk_name = NULL; + NTSTATUS status; + bool ok = true; + + drv_info = talloc_zero(tctx, struct spoolss_AddDriverInfo8); + torture_assert_not_null_goto(tctx, drv_info, ok, done, "Cannot allocate memory"); + + status = driver_inf_parse(tctx, + core_driver_inf, + abs_inf_path, + driver_arch, + driver_name, + drv_info, + &source_disk_name); + + if (NT_STATUS_EQUAL(status, NT_STATUS_DRIVER_INTERNAL_ERROR)) { + torture_comment(tctx, "--- Verify the correct torture option:driver_name is provided\n"); + } + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to parse driver inf\n"); + + *_parsed_dinfo = drv_info; +done: + return ok; +} diff --git a/source4/torture/rpc/iremotewinspool_common.h b/source4/torture/rpc/iremotewinspool_common.h new file mode 100644 index 0000000..5887416 --- /dev/null +++ b/source4/torture/rpc/iremotewinspool_common.h @@ -0,0 +1,110 @@ +/* + Unix SMB/CIFS implementation. + + iremotewinspool rpc test operations + + Copyright (C) 2018 Justin Stephenson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "torture/rpc/torture_rpc.h" + +#define REG_DRIVER_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print" + +struct test_driver_info { + struct smbcli_state *cli; + struct spoolss_AddDriverInfo8 *info; + const char *local_driver_path; + size_t driver_path_len; + char *server_name; + char *share_name; + char *print_upload_guid_dir; + const char *inf_file; + const char *uploaded_inf_path; + const char *driver_name; + const char *driver_arch; + const char *core_driver_inf; +}; + +struct test_iremotewinspool_context { + struct GUID object_uuid; + struct dcerpc_pipe *iremotewinspool_pipe; + struct policy_handle server_handle; + struct test_driver_info *dinfo; + const char *environment; +}; + +enum client_os_version +{ + WIN_2000, + WIN_VISTA, + WIN_SERVER_2008, + WIN_7, + WIN_SERVER_2008R2, + WIN_8, + WIN_SERVER_2012, + WIN_10, + WIN_SERVER_2016 +}; + +void init_winreg_String(struct winreg_String *name, const char *s); + +struct spoolss_UserLevel1 test_get_client_info(struct torture_context *tctx, + enum client_os_version os, + enum spoolss_MajorVersion major_number, + enum spoolss_MinorVersion minor_number, + const char *machine, + const char *user); + +bool test_AsyncOpenPrinter_byprinter(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + const char *printer_name, + struct spoolss_UserLevel1 cinfo, + struct policy_handle *handle); +bool test_AsyncOpenPrinter_byprinter_expect(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + const char *printer_name, + uint32_t access_mask, + struct spoolss_UserLevel1 cinfo, + NTSTATUS exected_status, + WERROR exected_result, + uint32_t expected_fault_code, + struct policy_handle *handle); +bool test_get_environment(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char **architecture); + +bool test_AsyncClosePrinter_byhandle(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx, + struct dcerpc_pipe *p, + struct policy_handle *handle); + +bool test_AsyncGetPrinterData_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type *type_p, + uint8_t **data_p, + uint32_t *needed_p); + +bool parse_inf_driver(struct torture_context *tctx, + const char *driver_name, + const char *abs_inf_path, + const char *driver_arch, + const char *core_driver_inf, + struct spoolss_AddDriverInfo8 **_parsed_dinfo); diff --git a/source4/torture/rpc/iremotewinspool_driver.c b/source4/torture/rpc/iremotewinspool_driver.c new file mode 100644 index 0000000..4e558ef --- /dev/null +++ b/source4/torture/rpc/iremotewinspool_driver.c @@ -0,0 +1,840 @@ +/* + Unix SMB/CIFS implementation. + test suite for iremotewinspool driver rpc operations + + Copyright (C) Justin Stephenson 2018 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_winspool.h" +#include "librpc/gen_ndr/ndr_winspool_c.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/registry/util_reg.h" +#include "torture/rpc/iremotewinspool_common.h" +#include "libcli/libcli.h" +#include "param/param.h" +#include "lib/registry/registry.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/resolve/resolve.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/cmdline/cmdline.h" +#include "system/filesys.h" +#include "lib/util/tftw.h" + +/* Connect to print driver share //server_name/share */ +static bool smb_connect_print_share(struct torture_context *tctx, + const char *server_name, + const char *share_name, + struct smbcli_state **cli) +{ + NTSTATUS status; + bool ok = true; + + struct smbcli_options smb_options; + struct smbcli_session_options smb_session_options; + + torture_comment(tctx, "Connecting to printer driver share '//%s/%s'\n", + server_name, share_name); + + lpcfg_smbcli_options(tctx->lp_ctx, &smb_options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options); + + /* On Windows, SMB1 must be enabled! */ + status = smbcli_full_connection(tctx, cli, server_name, + lpcfg_smb_ports(tctx->lp_ctx), + share_name, NULL, + lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, + &smb_options, + &smb_session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to connect to print$ share"); + +done: + + return ok; +} + +/* Copy file to destination where dst_fpath is a smb share path, + * files are either created or overwritten */ +static bool smb_copy_files(TALLOC_CTX *tctx, + const char *fpath, + const char *dst_fpath, + struct test_driver_info *dinfo) +{ + FILE *fp; + int smbfp = 0; + char *buffer = NULL; + int maxwrite = 64512; + size_t nread; + ssize_t nwrote; + bool ok = true; + size_t total_read; + + fp = fopen(fpath, "r"); + torture_assert_goto(tctx, fp, ok, done, "Failed to open local file\n"); + + smbfp = smbcli_open(dinfo->cli->tree, dst_fpath, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE); + torture_assert_int_not_equal_goto(tctx, smbfp, -1, ok, done, "Failed to open dst file\n"); + + buffer = talloc_array(tctx, char, maxwrite); + torture_assert_not_null_goto(tctx, buffer, ok, done, "Failed to allocate buffer\n"); + + total_read = 0; + + while (!feof(fp)) { + nread = fread(buffer, 1, maxwrite, fp); + if (ferror(fp)) { + torture_warning(tctx, "Error reading file [%s]\n", fpath); + continue; + } + + nwrote = smbcli_write(dinfo->cli->tree, smbfp, 0, buffer, total_read, nread); + if (nwrote != nread) { + torture_warning(tctx, "Not all data in stream written!\n"); + } + + total_read += nread; + } + + fclose(fp); + smbcli_close(dinfo->cli->tree, smbfp); +done: + + TALLOC_FREE(buffer); + return ok; +} + +/* Callback function provided to tftw() to + * copy driver files to smb share */ +static int copy_driver_files(TALLOC_CTX *tctx, + const char *fpath, + const struct stat *sb, + enum tftw_flags_e flag, + void *userdata) +{ + char *dst_fpath = NULL; + struct test_driver_info *dinfo = userdata; + char *path = NULL; + NTSTATUS status; + bool ok = true; + + path = talloc_strdup(tctx, fpath + dinfo->driver_path_len); + torture_assert_not_null_goto(tctx, path, ok, done, "Cannot allocate memory"); + + string_replace(path, '/', '\\'); + + dst_fpath = talloc_asprintf(tctx, "%s%s", dinfo->print_upload_guid_dir, path); + torture_assert_not_null_goto(tctx, dst_fpath, ok, done, "Cannot allocate memory"); + + switch (flag) { + case TFTW_FLAG_FILE: + ok = smb_copy_files(tctx, fpath, dst_fpath, dinfo); + torture_assert_goto(tctx, ok, ok, done, "Failed to copy files over smb"); + break; + case TFTW_FLAG_DIR: + status = smbcli_mkdir(dinfo->cli->tree, dst_fpath); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to create directories"); + break; + case TFTW_FLAG_SLINK: + case TFTW_FLAG_DNR: + case TFTW_FLAG_NSTAT: + case TFTW_FLAG_SPEC: + case TFTW_FLAG_DP: + case TFTW_FLAG_SLN: + torture_warning(tctx, "WARN: Unhandled typeflag [%s]\n", fpath); + break; + } + +done: + TALLOC_FREE(path); + TALLOC_FREE(dst_fpath); + + if (ok == true) { + return 0; + } else { + return 1; + } +} + +static bool test_get_driver_torture_options(struct torture_context *tctx, + const char **_local_driver_path, + const char **_inf_file, + const char **_driver_name, + const char **_driver_arch, + const char **_core_driver_inf) +{ + const char *local_driver_path = NULL; + const char *inf_file = NULL; + const char *driver_name = NULL; + const char *driver_arch = NULL; + const char *core_driver_inf = NULL; + const char *arches_list[] = { + SPOOLSS_ARCHITECTURE_x64, + SPOOLSS_ARCHITECTURE_NT_X86, + SPOOLSS_ARCHITECTURE_IA_64, + SPOOLSS_ARCHITECTURE_ARM, + SPOOLSS_ARCHITECTURE_4_0, + NULL, + }; + const char **p; + bool valid = false; + bool ok = true; + + local_driver_path = torture_setting_string(tctx, "driver_path", NULL); + if (local_driver_path == NULL) { + torture_fail(tctx, + "option --option=torture:driver_path=" + "/full/path/to/local/driver/dir\n"); + } + + inf_file = torture_setting_string(tctx, "inf_file", NULL); + if (inf_file == NULL) { + torture_fail(tctx, + "option --option=torture:inf_file=" + "filename.inf\n"); + } + + driver_name = torture_setting_string(tctx, "driver_name", NULL); + if (driver_name == NULL) { + torture_fail(tctx, + "option --option=torture:driver_name=" + "driver name\n"); + } + + driver_arch = torture_setting_string(tctx, "driver_arch", NULL); + if (driver_arch == NULL) { + torture_fail(tctx, + "option --option=torture:driver_arch=" + "driver arch\n"); + } + + core_driver_inf = torture_setting_string(tctx, "core_driver_inf", NULL); + + for (p = arches_list; *p != NULL; p++) { + if (strequal(*p, driver_arch) == 0) { + valid = true; + break; + } + } + torture_assert_goto(tctx, valid, ok, done, "Invalid driver arch provided"); + + *_local_driver_path = local_driver_path; + *_inf_file = inf_file; + *_driver_name = driver_name; + *_driver_arch = driver_arch; + *_core_driver_inf = core_driver_inf; +done: + return ok; +} + + +static bool test_get_misc_driver_info(struct torture_context *tctx, + struct test_driver_info *dinfo, + const char **_abs_inf_path, + size_t *_driver_path_len) +{ + const char *abs_inf_path; + size_t driver_path_len; + bool ok = true; + + driver_path_len = strlen(dinfo->local_driver_path); + torture_assert_int_not_equal_goto(tctx, driver_path_len, 0, ok, done, "driver path length is 0"); + + abs_inf_path = talloc_asprintf(tctx, "%s/%s", dinfo->local_driver_path, dinfo->inf_file); + torture_assert_not_null_goto(tctx, abs_inf_path, ok, done, "Cannot allocate memory"); + + *_abs_inf_path = abs_inf_path; + *_driver_path_len = driver_path_len; +done: + + return ok; +} + +/* Uninstall the previously installed print driver */ +static bool test_uninstall_printer_driver(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx) +{ + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct winspool_AsyncDeletePrinterDriverEx r; + bool ok = true; + NTSTATUS status; + + r.in.pName = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + r.in.pDriverName = talloc_strdup(tctx, ctx->dinfo->driver_name); + torture_assert_not_null_goto(tctx, r.in.pDriverName, ok, done, "Cannot allocate memory"); + + r.in.pEnvironment = SPOOLSS_ARCHITECTURE_x64; + + r.in.dwDeleteFlag = 0; + r.in.dwVersionNum = 0; + + status = dcerpc_winspool_AsyncDeletePrinterDriverEx_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncDeletePrinterDriverEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, "AsyncDeletePrinterDriverEx failed"); +done: + + return ok; +} + +/* Remove the leftover print driver package files from the driver store */ +static bool test_remove_driver_package(struct torture_context *tctx, + struct test_iremotewinspool_context *ctx) +{ + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct winspool_AsyncDeletePrinterDriverPackage r; + bool ok = true; + NTSTATUS status; + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory"); + + r.in.pszInfPath = ctx->dinfo->uploaded_inf_path; + + r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64; + + status = dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncDeletePrinterPackage failed"); + + torture_assert_hresult_ok(tctx, r.out.result, "AsyncDeletePrinterDriverPackage failed"); +done: + + return ok; +} + +static bool test_winreg_iremotewinspool_openhklm(struct torture_context *tctx, + struct dcerpc_binding_handle *winreg_bh, + struct policy_handle *_hklm_handle) +{ + struct winreg_OpenHKLM r; + NTSTATUS status; + bool ok = true; + + r.in.system_name = NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = _hklm_handle; + + status = dcerpc_winreg_OpenHKLM_r(winreg_bh, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to Open HKLM"); + + torture_assert_werr_ok(tctx, r.out.result, "Failed to Open HKLM"); +done: + + return ok; +} + +static bool test_winreg_iremotewinspool_openkey(struct torture_context *tctx, + struct dcerpc_binding_handle *winreg_bh, + struct policy_handle *hklm_handle, + const char *keyname, + struct policy_handle *_key_handle) +{ + struct winreg_OpenKey r; + NTSTATUS status; + bool ok = true; + + r.in.parent_handle = hklm_handle; + init_winreg_String(&r.in.keyname, keyname); + r.in.options = REG_OPTION_NON_VOLATILE; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = _key_handle; + + status = dcerpc_winreg_OpenKey_r(winreg_bh, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "OpenKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "OpenKey failed"); +done: + + return ok; +} + +static bool test_winreg_iremotewinspool_queryvalue(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *key_handle, + const char *value_name, + const char **_valuestr) +{ + struct winreg_QueryValue r; + enum winreg_Type type = REG_NONE; + struct winreg_String valuename; + DATA_BLOB blob; + const char *str; + uint32_t data_size = 0; + uint32_t data_length = 0; + uint8_t *data = NULL; + NTSTATUS status; + bool ok = true; + + init_winreg_String(&valuename, value_name); + + data = talloc_zero_array(tctx, uint8_t, 0); + + r.in.handle = key_handle; + r.in.value_name = &valuename; + r.in.type = &type; + r.in.data_size = &data_size; + r.in.data_length = &data_length; + r.in.data = data; + + r.out.type = &type; + r.out.data = data; + r.out.data_size = &data_size; + r.out.data_length = &data_length; + + status = dcerpc_winreg_QueryValue_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "winreg_QueryValue failure"); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + *r.in.data_size = *r.out.data_size; + data = talloc_zero_array(tctx, uint8_t, *r.in.data_size); + r.in.data = data; + r.out.data = data; + status = dcerpc_winreg_QueryValue_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "QueryValue failed"); + } + torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed"); + + torture_assert_int_equal_goto(tctx, *r.out.type, REG_SZ, ok, done, "unexpected type"); + blob = data_blob(r.out.data, *r.out.data_size); + str = reg_val_data_string(tctx, REG_SZ, blob); + + *_valuestr = str; +done: + + return ok; +} + +/* Validate the installed driver subkey exists, and the InfPath + * value matches the pszDestInfPath from test_UploadPrinterDriverPackage */ +static bool test_winreg_validate_driver(struct torture_context *tctx, + struct dcerpc_pipe *winreg_pipe, + struct test_driver_info *dinfo) +{ + struct policy_handle hklm_handle; + struct policy_handle key_handle; + char *driver_key = NULL; + const char *val_name = NULL; + const char *val_str = NULL; + bool ok = true; + + struct dcerpc_binding_handle *winreg_bh; + struct spoolss_AddDriverInfo8 *parsed_dinfo; + + winreg_bh = winreg_pipe->binding_handle; + parsed_dinfo = dinfo->info; + + /* OpenHKLM */ + ok = test_winreg_iremotewinspool_openhklm(tctx, winreg_bh, &hklm_handle); + torture_assert_goto(tctx, ok, ok, done, "Failed to perform winreg OpenHKLM"); + + /* Open registry subkey for the installed print driver */ + driver_key = talloc_asprintf(tctx, "%s\\Environments\\%s\\Drivers\\Version-%d\\%s", + REG_DRIVER_CONTROL_KEY, + parsed_dinfo->architecture, + parsed_dinfo->version, + parsed_dinfo->driver_name); + torture_assert_not_null_goto(tctx, driver_key, ok, done, "Cannot allocate driver_key string"); + ok = test_winreg_iremotewinspool_openkey(tctx, winreg_bh, &hklm_handle, + driver_key, + &key_handle); + torture_assert_goto(tctx, ok, ok, done, "Failed to perform winreg OpenKey"); + + /* Read infpath value and validate this matches what was uploaded */ + val_name = "InfPath"; + ok = test_winreg_iremotewinspool_queryvalue(tctx, winreg_bh, &key_handle, val_name, + &val_str); + torture_assert_goto(tctx, ok, ok, done, "QueryValue failed"); + + torture_assert_casestr_equal(tctx, val_str, + dinfo->uploaded_inf_path, + "InfPath does not match uploaded inf"); +done: + + return ok; +} + +static bool test_init_iremotewinspool_conn(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + struct dcerpc_binding *binding = {0}; + bool ok = true; + NTSTATUS status; + + status = GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &t->object_uuid); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to parse GUID"); + + status = torture_rpc_binding(tctx, &binding); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to retrieve torture binding"); + + status = dcerpc_binding_set_object(binding, t->object_uuid); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to set object_uuid"); + + status = torture_rpc_connection_with_binding(tctx, binding, &t->iremotewinspool_pipe, + &ndr_table_iremotewinspool); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Error connecting to server"); + +done: + + return ok; + +} + +static bool test_init_iremotewinspool_openprinter(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + struct spoolss_UserLevel1 client_info = {0}; + char *printer_name = NULL; + bool ok = true; + + printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(t->iremotewinspool_pipe)); + torture_assert_not_null_goto(tctx, printer_name, ok, done, "Cannot allocate memory"); + + client_info = test_get_client_info(tctx, WIN_7, 3, SPOOLSS_MINOR_VERSION_0, + "testclient_machine", "testclient_user"); + + ok = test_AsyncOpenPrinter_byprinter(tctx, t, t->iremotewinspool_pipe, printer_name, + client_info, &t->server_handle); + torture_assert_goto(tctx, ok, ok, done, "failed to open printserver"); + + ok = test_get_environment(tctx, t->iremotewinspool_pipe->binding_handle, + &t->server_handle, &t->environment); + torture_assert_goto(tctx, ok, ok, done, "failed to get environment"); + +done: + TALLOC_FREE(printer_name); + + return ok; +} + +static bool test_init_driver_info(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + bool ok = true; + const char *abs_inf_path; + struct test_driver_info *drv_info = {0}; + + drv_info = talloc_zero(tctx, struct test_driver_info); + torture_assert_not_null_goto(tctx, drv_info, ok, done, "Cannot allocate memory"); + + t->dinfo = drv_info; + + ok = test_get_driver_torture_options(tctx, + &drv_info->local_driver_path, + &drv_info->inf_file, + &drv_info->driver_name, + &drv_info->driver_arch, + &drv_info->core_driver_inf); + torture_assert_goto(tctx, ok, ok, done, "Failed to get driver torture options"); + + ok = test_get_misc_driver_info(tctx, drv_info, + &abs_inf_path, + &drv_info->driver_path_len); + torture_assert_goto(tctx, ok, ok, done, "Failed to get misc driver info"); + + ok = parse_inf_driver(tctx, drv_info->driver_name, abs_inf_path, drv_info->driver_arch, + drv_info->core_driver_inf, &drv_info->info); + torture_assert_goto(tctx, ok, ok, done, "Failed to parse inf driver"); + + /* Ensure that we are trying to install the correct device class: + * https://docs.microsoft.com/en-us/windows-hardware/drivers/install/system-defined-device-setup-classes-available-to-vendors + */ + if (!(drv_info->info->printer_driver_attributes & PRINTER_DRIVER_CLASS)) { + ok = false; + torture_fail_goto(tctx, done, "Inf file Class value must be Printer"); + } +done: + return ok; + +} + +static bool test_init_server_and_share_info(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + struct GUID guid; + bool ok = true; + + t->dinfo->server_name = talloc_asprintf(tctx, "%s", dcerpc_server_name(t->iremotewinspool_pipe)); + torture_assert_not_null_goto(tctx, t->dinfo->server_name, ok, done, "Cannot allocate memory"); + + t->dinfo->share_name = talloc_strdup(tctx, "print$"); + torture_assert_not_null_goto(tctx, t->dinfo->share_name, ok, done, "Cannot allocate memory"); + + guid = GUID_random(); + t->dinfo->print_upload_guid_dir = GUID_string2(tctx, &guid); +done: + return ok; +} + + +static bool torture_rpc_iremotewinspool_drv_setup_common(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + bool ok = true; + int ret = 0; + + ok = test_init_driver_info(tctx, t); + torture_assert_goto(tctx, ok, ok, done, "failed to init driver info"); + + ok = test_init_iremotewinspool_conn(tctx, t); + torture_assert_goto(tctx, ok, ok, done, "failed to init iremotewinspool conn"); + + ok = test_init_iremotewinspool_openprinter(tctx, t); + torture_assert_goto(tctx, ok, ok, done, "failed to init iremotewinspool openprinter"); + + ok = test_init_server_and_share_info(tctx, t); + torture_assert_goto(tctx, ok, ok, done, "failed to init server and share info"); + + ret = smb_connect_print_share(tctx, t->dinfo->server_name, t->dinfo->share_name, &t->dinfo->cli); + torture_assert_goto(tctx, ret, ok, done, "Failed to connect to print share"); + +done: + + return ok; +} + +static bool torture_rpc_iremotewinspool_drv_setup(struct torture_context *tctx, + void **data) +{ + struct test_iremotewinspool_context *t; + + *data = t = talloc_zero(tctx, struct test_iremotewinspool_context); + + return torture_rpc_iremotewinspool_drv_setup_common(tctx, t); +} + +static bool torture_rpc_iremotewinspool_drv_teardown_common(struct torture_context *tctx, + struct test_iremotewinspool_context *t) +{ + smbcli_deltree(t->dinfo->cli->tree, t->dinfo->print_upload_guid_dir); + smb_raw_exit(t->dinfo->cli->session); + + test_uninstall_printer_driver(tctx, t); + test_remove_driver_package(tctx, t); + + test_AsyncClosePrinter_byhandle(tctx, t, t->iremotewinspool_pipe, &t->server_handle); + + return true; +} + +static bool torture_rpc_iremotewinspool_drv_teardown(struct torture_context *tctx, + void *data) +{ + struct test_iremotewinspool_context *t = talloc_get_type(data, struct test_iremotewinspool_context); + bool ret; + + ret = torture_rpc_iremotewinspool_drv_teardown_common(tctx, t); + talloc_free(t); + + return ret; +} + +/* Creates {GUID} directory inside //server/print$ then copies driver files + * and directories from torture option driver_path to this directory over smb */ +static bool test_CopyDriverFiles(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + bool ret = false; + bool ok = true; + NTSTATUS status; + + status = smbcli_mkdir(ctx->dinfo->cli->tree, ctx->dinfo->print_upload_guid_dir); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to create upload directory"); + + /* Walk the provided torture option driver_path file tree, creating the directory hierarchy and + * copying all files to print$/{GUID}/ share */ + ret = tftw(tctx, ctx->dinfo->local_driver_path, copy_driver_files, TFTW_MAX_DEPTH, ctx->dinfo); + torture_assert_int_equal_goto(tctx, ret, 0, ok, done, "Failed to copy driver files to print$/{GUID}/ dir"); + +done: + + return ok; +} + +/* + * Upload print driver package files and inf file, preparing the print server + * for driver installation + */ +static bool test_UploadPrinterDriverPackage(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct spoolss_AddDriverInfo8 *parsed_dinfo; + struct winspool_AsyncUploadPrinterDriverPackage r; + uint32_t pcchDestInfPath = 0; + NTSTATUS status; + bool ok = true; + + parsed_dinfo = ctx->dinfo->info; + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory"); + + r.in.pszInfPath = talloc_asprintf(tctx, "\\\\%s\\%s\\%s\\%s", ctx->dinfo->server_name, + ctx->dinfo->share_name, + ctx->dinfo->print_upload_guid_dir, + ctx->dinfo->inf_file); + torture_assert_not_null_goto(tctx, r.in.pszInfPath, ok, done, "Cannot allocate memory"); + + r.in.pszEnvironment = parsed_dinfo->architecture; + /* Upload driver package files even if the driver package is already present + * on the print server */ + r.in.dwFlags = UPDP_UPLOAD_ALWAYS; + pcchDestInfPath = 260; + r.in.pszDestInfPath = NULL; + r.in.pcchDestInfPath = &pcchDestInfPath; + r.out.pszDestInfPath = NULL; + r.out.pcchDestInfPath = &pcchDestInfPath; + + r.in.pszDestInfPath = talloc_zero(tctx, const char); + torture_assert_not_null_goto(tctx, r.in.pszDestInfPath, ok, done, "Cannot allocate memory"); + r.out.pszDestInfPath = talloc_zero(tctx, const char); + torture_assert_not_null_goto(tctx, r.out.pszDestInfPath, ok, done, "Cannot allocate memory"); + + status = dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncUploadPrinterDriverPackage failed"); + + torture_assert_hresult_ok(tctx, r.out.result, "AsyncUploadPrinterDriverPackage failed"); + + ctx->dinfo->uploaded_inf_path = talloc_strdup(tctx, r.out.pszDestInfPath); + torture_assert_not_null_goto(tctx, ctx->dinfo->uploaded_inf_path, ok, done, "Cannot allocate memory"); + +done: + + return ok; +} + +/* Install the driver that was successfully uploaded to the printer driver + * store, note that Windows validates the pszDriverName as mentioned below */ +static bool test_InstallPrinterDriverFromPackage(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *p = ctx->iremotewinspool_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + char *abs_inf_path = NULL; + struct spoolss_AddDriverInfo8 *parsed_dinfo; + struct winspool_AsyncInstallPrinterDriverFromPackage r; + bool ok = true; + NTSTATUS status; + + parsed_dinfo = ctx->dinfo->info; + + r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory"); + + /* output string(pszDestInfPath) from test_UploadPrinterDriverPackage() */ + r.in.pszInfPath = talloc_strdup(tctx, ctx->dinfo->uploaded_inf_path); + torture_assert_not_null_goto(tctx, r.in.pszInfPath, ok, done, "Cannot allocate memory"); + + abs_inf_path = talloc_asprintf(tctx, "%s/%s", ctx->dinfo->local_driver_path, ctx->dinfo->inf_file); + torture_assert_not_null_goto(tctx, abs_inf_path, ok, done, "Cannot allocate memory"); + + r.in.pszEnvironment = parsed_dinfo->architecture; + torture_assert_not_null_goto(tctx, r.in.pszEnvironment, ok, done, "Cannot allocate memory"); + + /* Windows validates the print driver name by checking the pszDriverName input against the inf file: + * 1) "DriverName" value + * 2) "CompatName" value + * 3) left-hand-side value under the [Model] section + * otherwise ERROR_UNKNOWN_PRINTER_DRIVER is returned */ + r.in.pszDriverName = parsed_dinfo->driver_name; + torture_assert_not_null_goto(tctx, r.in.pszDriverName, ok, done, "Cannot allocate memory"); + + /* All files should be installed, even if doing so would overwrite some newer + * versions */ + r.in.dwFlags = IPDFP_COPY_ALL_FILES; + + status = dcerpc_winspool_AsyncInstallPrinterDriverFromPackage_r(b, tctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncInstallPrinterDriverFromPackage failed"); + + torture_assert_hresult_ok(tctx, r.out.result, "AsyncInstallPrinterDriverFromPackage failed"); +done: + TALLOC_FREE(abs_inf_path); + + return ok; +} + +/* Check the registry to validate the print driver installed successfully */ +static bool test_ValidatePrinterDriverInstalled(struct torture_context *tctx, + void *private_data) +{ + struct test_iremotewinspool_context *ctx = + talloc_get_type_abort(private_data, struct test_iremotewinspool_context); + + struct dcerpc_pipe *winreg_pipe = NULL; + NTSTATUS status; + bool ok = true; + + /* winreg is not available over ncacn_ip_tcp */ + status = torture_rpc_connection_transport(tctx, &winreg_pipe, &ndr_table_winreg, NCACN_NP, 0, 0); + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_NOT_AVAILABLE)) { + /* retry */ + status = torture_rpc_connection_transport(tctx, &winreg_pipe, &ndr_table_winreg, NCACN_NP, 0, 0); + } + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to connect to winreg"); + + ok = test_winreg_validate_driver(tctx, winreg_pipe, ctx->dinfo); + torture_assert_goto(tctx, ok, ok, done, "Failed to validate driver with winreg"); + +done: + TALLOC_FREE(winreg_pipe); + + return ok; +} + +struct torture_suite *torture_rpc_iremotewinspool_drv(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "iremotewinspool_driver"); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drivers"); + + torture_tcase_set_fixture(tcase, + torture_rpc_iremotewinspool_drv_setup, + torture_rpc_iremotewinspool_drv_teardown); + + torture_tcase_add_simple_test(tcase, "CopyDriverFiles", test_CopyDriverFiles); + torture_tcase_add_simple_test(tcase, "UploadPrinterDriverPackage", test_UploadPrinterDriverPackage); + torture_tcase_add_simple_test(tcase, "InstallPrinterDriverFromPackage", test_InstallPrinterDriverFromPackage); + torture_tcase_add_simple_test(tcase, "ValidatePrinterDriverInstalled", test_ValidatePrinterDriverInstalled); + + return suite; +} diff --git a/source4/torture/rpc/join.c b/source4/torture/rpc/join.c new file mode 100644 index 0000000..6e0afca --- /dev/null +++ b/source4/torture/rpc/join.c @@ -0,0 +1,86 @@ +#include "includes.h" +#include "libcli/libcli.h" + +#include "torture/rpc/torture_rpc.h" + +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +#define TORTURE_NETBIOS_NAME "smbtorturejoin" + + +bool torture_rpc_join(struct torture_context *torture) +{ + NTSTATUS status; + struct test_join *tj; + struct cli_credentials *machine_account; + struct smbcli_state *cli; + const char *host = torture_setting_string(torture, "host", NULL); + struct smbcli_options options; + struct smbcli_session_options session_options; + + /* Join domain as a member server. */ + tj = torture_join_domain(torture, + TORTURE_NETBIOS_NAME, + ACB_WSTRUST, + &machine_account); + + if (!tj) { + DEBUG(0, ("%s failed to join domain as workstation\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(tj, &cli, host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + machine_account, + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n", + TORTURE_NETBIOS_NAME)); + return false; + } + smbcli_tdis(cli); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + /* Join domain as a domain controller. */ + tj = torture_join_domain(torture, TORTURE_NETBIOS_NAME, + ACB_SVRTRUST, + &machine_account); + if (!tj) { + DEBUG(0, ("%s failed to join domain as domain controller\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + status = smbcli_full_connection(tj, &cli, host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + machine_account, + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n", + TORTURE_NETBIOS_NAME)); + return false; + } + + smbcli_tdis(cli); + + /* Leave domain. */ + torture_leave_domain(torture, tj); + + return true; +} + diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c new file mode 100644 index 0000000..c2190e5 --- /dev/null +++ b/source4/torture/rpc/lsa.c @@ -0,0 +1,5645 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "libcli/cldap/cldap.h" +#include "../lib/tsocket/tsocket.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/netlogon.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "lib/events/events.h" +#include "libcli/security/security.h" +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" +#include "libcli/resolve/resolve.h" + +#include +#include + +#define TEST_MACHINENAME "lsatestmach" +#define TRUSTPW "12345678" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool test_OpenPolicy(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct lsa_ObjectAttribute attr; + struct policy_handle handle; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + uint16_t system_name = '\\'; + + torture_comment(tctx, "\nTesting OpenPolicy\n"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy_r(b, tctx, &r), + "OpenPolicy failed"); + + torture_assert_ntstatus_ok(tctx, + r.out.result, + "OpenPolicy failed"); + + return true; +} + +static bool test_OpenPolicy_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct lsa_ObjectAttribute attr; + struct policy_handle handle; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy r; + uint16_t system_name = '\\'; + NTSTATUS status; + + torture_comment(tctx, "\nTesting OpenPolicy_fail\n"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, + "OpenPolicy correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "OpenPolicy return value should " + "be ACCESS_DENIED"); + return true; + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "OpenPolicy correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + } + + torture_assert_ntstatus_equal(tctx, + r.out.result, + NT_STATUS_OK, + "OpenPolicy return value should be " + "ACCESS_DENIED"); + + return false; +} + + +bool test_lsa_OpenPolicy2_ex(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle **handle, + NTSTATUS expected_status, + NTSTATUS expected_status2) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + NTSTATUS status; + + torture_comment(tctx, "\nTesting OpenPolicy2\n"); + + *handle = talloc(tctx, struct policy_handle); + torture_assert(tctx, *handle != NULL, "talloc(tctx, struct policy_handle)"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = *handle; + + status = dcerpc_lsa_OpenPolicy2_r(b, tctx, &r); + + /* Allow two possible failure status codes */ + if (!NT_STATUS_EQUAL(status, expected_status2)) { + torture_assert_ntstatus_equal(tctx, status, + expected_status, + "OpenPolicy2 failed"); + } + if (!NT_STATUS_IS_OK(expected_status) || + !NT_STATUS_IS_OK(expected_status2)) { + return true; + } + + torture_assert_ntstatus_ok(tctx, + r.out.result, + "OpenPolicy2 failed"); + + return true; +} + + +bool test_lsa_OpenPolicy2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle **handle) +{ + return test_lsa_OpenPolicy2_ex(b, tctx, handle, + NT_STATUS_OK, NT_STATUS_OK); +} + +static bool test_OpenPolicy2_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct lsa_ObjectAttribute attr; + struct policy_handle handle; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + NTSTATUS status; + + torture_comment(tctx, "\nTesting OpenPolicy2_fail\n"); + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED) || + NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, + "OpenPolicy2 correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "OpenPolicy2 return value should " + "be ACCESS_DENIED"); + return true; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "OpenPolicy2 correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + + torture_fail(tctx, + "OpenPolicy2 return value should be " + "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED"); + + return false; +} + +bool test_lsa_OpenPolicy3_ex(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle **handle, + NTSTATUS expected_status, + NTSTATUS expected_status2) +{ + struct lsa_QosInfo qos = { + .impersonation_level = 2, + .context_mode = 1, + }; + struct lsa_ObjectAttribute attr = { + .len = 0, + .sec_qos = &qos, + }; + struct lsa_revision_info1 in_rinfo1 = { + .revision = 1, + .supported_features = 0, + }; + union lsa_revision_info in_rinfo = { + .info1 = in_rinfo1, + }; + struct lsa_revision_info1 out_rinfo1 = { + .revision = 0, + }; + union lsa_revision_info out_rinfo = { + .info1 = out_rinfo1, + }; + uint32_t out_version = 0; + struct lsa_OpenPolicy3 r = { + .in.system_name = "\\", + .in.attr = &attr, + .in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED, + .in.in_version = 1, + .in.in_revision_info = &in_rinfo, + .out.out_version = &out_version, + .out.out_revision_info = &out_rinfo, + }; + NTSTATUS status; + + torture_comment(tctx, "\nTesting OpenPolicy3\n"); + + *handle = talloc(tctx, struct policy_handle); + torture_assert(tctx, + *handle != NULL, + "talloc(tctx, struct policy_handle)"); + r.out.handle = *handle; + + status = dcerpc_lsa_OpenPolicy3_r(b, tctx, &r); + + /* Allow two possible failure status codes */ + if (!NT_STATUS_EQUAL(status, expected_status2)) { + torture_assert_ntstatus_equal(tctx, + status, + expected_status, + "OpenPolicy3 failed"); + } + if (!NT_STATUS_IS_OK(expected_status) || + !NT_STATUS_IS_OK(expected_status2)) { + return true; + } + + torture_assert_ntstatus_ok(tctx, r.out.result, "OpenPolicy3 failed"); + torture_assert_int_equal(tctx, out_version, 1, "Invalid out_version"); + torture_assert_int_equal(tctx, + out_rinfo1.revision, + 1, + "Invalid revision"); +#if 0 /* TODO: Enable as soon as it is supported */ + torture_assert_int_equal(tctx, + out_rinfo1.supported_features, + LSA_FEATURE_TDO_AUTH_INFO_AES_CIPHER, + "Invalid supported feature set"); +#endif + + return true; +} + +bool test_lsa_OpenPolicy3(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle **handle) +{ + return test_lsa_OpenPolicy3_ex(b, + tctx, + handle, + NT_STATUS_OK, + NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE); +} + +static bool test_OpenPolicy3_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct policy_handle handle = { + .handle_type = 0, + }; + struct lsa_QosInfo qos = { + .impersonation_level = 2, + .context_mode = 1, + }; + struct lsa_ObjectAttribute attr = { + .len = 0, + .sec_qos = &qos, + }; + struct lsa_revision_info1 in_rinfo1 = { + .revision = 0, + .supported_features = 0, + }; + union lsa_revision_info in_rinfo = { + .info1 = in_rinfo1, + }; + struct lsa_revision_info1 out_rinfo1 = { + .revision = 0, + }; + union lsa_revision_info out_rinfo = { + .info1 = out_rinfo1, + }; + uint32_t out_version = 0; + struct lsa_OpenPolicy3 r = { + .in.system_name = "\\", + .in.attr = &attr, + .in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED, + .in.in_version = 1, + .in.in_revision_info = &in_rinfo, + .out.out_version = &out_version, + .out.out_revision_info = &out_rinfo, + .out.handle = &handle, + }; + NTSTATUS status; + + torture_comment(tctx, "\nTesting OpenPolicy3_fail\n"); + + status = dcerpc_lsa_OpenPolicy3_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED) || + NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, + NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + torture_comment(tctx, + "OpenPolicy3 correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "OpenPolicy3 return value should " + "be ACCESS_DENIED or CONNECTION_DISCONNECTED"); + return true; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, + NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "OpenPolicy3 correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + + torture_fail(tctx, + "OpenPolicy3 return value should be " + "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED"); + + return false; +} + +static bool test_LookupNames(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level, + struct lsa_TransNameArray *tnames) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String *names; + uint32_t count = 0; + int i; + uint32_t *input_idx; + + torture_comment(tctx, "\nTesting LookupNames with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + + r.in.num_names = 0; + + input_idx = talloc_array(tctx, uint32_t, tnames->count); + names = talloc_array(tctx, struct lsa_String, tnames->count); + + for (i=0;icount;i++) { + if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) { + init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string); + input_idx[r.in.num_names] = i; + r.in.num_names++; + } + } + + r.in.handle = handle; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r), + "LookupNames failed"); + if (NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) { + for (i=0;i< r.in.num_names;i++) { + if (i < count && sids.sids[i].sid_type == SID_NAME_UNKNOWN) { + torture_comment(tctx, "LookupName of %s was unmapped\n", + tnames->names[i].name.string); + } else if (i >=count) { + torture_comment(tctx, "LookupName of %s failed to return a result\n", + tnames->names[i].name.string); + } + } + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupNames failed"); + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupNames failed"); + } + + for (i=0;i< r.in.num_names;i++) { + torture_assert(tctx, (i < count), + talloc_asprintf(tctx, + "LookupName of %s failed to return a result\n", + tnames->names[input_idx[i]].name.string)); + + torture_assert_int_equal(tctx, + sids.sids[i].sid_type, + tnames->names[input_idx[i]].sid_type, + talloc_asprintf(tctx, + "LookupName of %s got unexpected name type: %s\n", + tnames->names[input_idx[i]].name.string, + sid_type_lookup(sids.sids[i].sid_type))); + if (sids.sids[i].sid_type != SID_NAME_DOMAIN) { + continue; + } + torture_assert_int_equal(tctx, + sids.sids[i].rid, + UINT32_MAX, + talloc_asprintf(tctx, + "LookupName of %s got unexpected rid: %d\n", + tnames->names[input_idx[i]].name.string, + sids.sids[i].rid)); + } + + return true; +} + +static bool test_LookupNames_bogus(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String names[1]; + uint32_t count = 0; + + torture_comment(tctx, "\nTesting LookupNames with bogus name\n"); + + sids.count = 0; + sids.sids = NULL; + + init_lsa_String(&names[0], "NT AUTHORITY\\BOGUS"); + + r.in.handle = handle; + r.in.num_names = 1; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r), + "LookupNames bogus failed"); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) { + torture_comment(tctx, "LookupNames failed - %s\n", + nt_errstr(r.out.result)); + return false; + } + + torture_comment(tctx, "\n"); + + return true; +} + +static bool test_LookupNames_NULL(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String names[1]; + uint32_t count = 0; + + torture_comment(tctx, "\nTesting LookupNames with NULL name\n"); + + sids.count = 0; + sids.sids = NULL; + + names[0].string = NULL; + + r.in.handle = handle; + r.in.num_names = 1; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + /* nt4 returns NT_STATUS_NONE_MAPPED with sid_type + * SID_NAME_UNKNOWN, rid 0, and sid_index -1; + * + * w2k3/w2k8 return NT_STATUS_OK with sid_type + * SID_NAME_DOMAIN, rid -1 and sid_index 0 and BUILTIN domain + */ + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r), + "LookupNames with NULL name failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupNames with NULL name failed"); + + torture_comment(tctx, "\n"); + + return true; +} + +static bool test_LookupNames_wellknown(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level) +{ + struct lsa_TranslatedName name; + struct lsa_TransNameArray tnames; + bool ret = true; + + torture_comment(tctx, "Testing LookupNames with well known names\n"); + + tnames.names = &name; + tnames.count = 1; + name.name.string = "NT AUTHORITY\\SYSTEM"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "NT AUTHORITY\\ANONYMOUS LOGON"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "NT AUTHORITY\\Authenticated Users"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + +#if 0 + name.name.string = "NT AUTHORITY"; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "NT AUTHORITY\\"; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); +#endif + + name.name.string = "BUILTIN\\"; + name.sid_type = SID_NAME_DOMAIN; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "BUILTIN\\Administrators"; + name.sid_type = SID_NAME_ALIAS; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "SYSTEM"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + + name.name.string = "Everyone"; + name.sid_type = SID_NAME_WKN_GRP; + ret &= test_LookupNames(b, tctx, handle, level, &tnames); + return ret; +} + +static bool test_LookupNames2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level, + struct lsa_TransNameArray2 *tnames, + bool check_result) +{ + struct lsa_LookupNames2 r; + struct lsa_TransSidArray2 sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String *names; + uint32_t *input_idx; + uint32_t count = 0; + int i; + + torture_comment(tctx, "\nTesting LookupNames2 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + r.in.num_names = 0; + + input_idx = talloc_array(tctx, uint32_t, tnames->count); + names = talloc_array(tctx, struct lsa_String, tnames->count); + + for (i=0;icount;i++) { + if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) { + init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string); + input_idx[r.in.num_names] = i; + r.in.num_names++; + } + } + + r.in.handle = handle; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames2_r(b, tctx, &r), + "LookupNames2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LookupNames2 failed"); + + if (check_result) { + torture_assert_int_equal(tctx, count, sids.count, + "unexpected number of results returned"); + if (sids.count > 0) { + torture_assert(tctx, sids.sids, "invalid sid buffer"); + } + } + + torture_comment(tctx, "\n"); + + return true; +} + + +static bool test_LookupNames3(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level, + struct lsa_TransNameArray2 *tnames, + bool check_result) +{ + struct lsa_LookupNames3 r; + struct lsa_TransSidArray3 sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String *names; + uint32_t count = 0; + int i; + uint32_t *input_idx; + + torture_comment(tctx, "\nTesting LookupNames3 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + r.in.num_names = 0; + + input_idx = talloc_array(tctx, uint32_t, tnames->count); + names = talloc_array(tctx, struct lsa_String, tnames->count); + for (i=0;icount;i++) { + if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) { + init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string); + input_idx[r.in.num_names] = i; + r.in.num_names++; + } + } + + r.in.handle = handle; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames3_r(b, tctx, &r), + "LookupNames3 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupNames3 failed"); + + if (check_result) { + torture_assert_int_equal(tctx, count, sids.count, + "unexpected number of results returned"); + if (sids.count > 0) { + torture_assert(tctx, sids.sids, "invalid sid buffer"); + } + } + + torture_comment(tctx, "\n"); + + return true; +} + +static bool test_LookupNames4(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + enum lsa_LookupNamesLevel level, + struct lsa_TransNameArray2 *tnames, + bool check_result) +{ + struct lsa_LookupNames4 r; + struct lsa_TransSidArray3 sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String *names; + uint32_t count = 0; + int i; + uint32_t *input_idx; + + torture_comment(tctx, "\nTesting LookupNames4 with %d names\n", tnames->count); + + sids.count = 0; + sids.sids = NULL; + + r.in.num_names = 0; + + input_idx = talloc_array(tctx, uint32_t, tnames->count); + names = talloc_array(tctx, struct lsa_String, tnames->count); + for (i=0;icount;i++) { + if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) { + init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string); + input_idx[r.in.num_names] = i; + r.in.num_names++; + } + } + + r.in.num_names = tnames->count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames4_r(b, tctx, &r), + "LookupNames4 failed"); + + if (!NT_STATUS_IS_OK(r.out.result)) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) { + torture_comment(tctx, + "LookupNames4 failed: %s - not considered as an error", + nt_errstr(r.out.result)); + + return true; + } + } + torture_assert_ntstatus_ok(tctx, + r.out.result, + "LookupNames4 failed"); + + if (check_result) { + torture_assert_int_equal(tctx, count, sids.count, + "unexpected number of results returned"); + if (sids.count > 0) { + torture_assert(tctx, sids.sids, "invalid sid buffer"); + } + } + + torture_comment(tctx, "\n"); + + return true; +} + +static bool test_LookupNames4_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + enum lsa_LookupNamesLevel level) +{ + struct lsa_LookupNames4 r; + struct lsa_TransSidArray3 sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String *names = NULL; + uint32_t count = 0; + NTSTATUS status; + + torture_comment(tctx, "\nTesting LookupNames4_fail"); + + sids.count = 0; + sids.sids = NULL; + + r.in.num_names = 0; + + r.in.num_names = count; + r.in.names = names; + r.in.sids = &sids; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + status = dcerpc_lsa_LookupNames4_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + torture_comment(tctx, + "LookupNames4 correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "LookupNames4 return value should " + "be ACCESS_DENIED"); + return true; + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "LookupSids3 correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + } + + torture_fail(tctx, + "LookupNames4 return value should be " + "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED"); + + return false; +} + + +static bool test_LookupSids(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids r; + struct lsa_TransNameArray names; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = sids->num_sids; + + torture_comment(tctx, "\nTesting LookupSids\n"); + + names.count = 0; + names.names = NULL; + + r.in.handle = handle; + r.in.sids = sids; + r.in.names = &names; + r.in.level = level; + r.in.count = &count; + r.out.count = &count; + r.out.names = &names; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids_r(b, tctx, &r), + "LookupSids failed"); + if (!NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupSids failed"); + } + + torture_comment(tctx, "\n"); + + if (!test_LookupNames(b, tctx, handle, level, &names)) { + return false; + } + + return true; +} + + +static bool test_LookupSids2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids2 r; + struct lsa_TransNameArray2 names; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = sids->num_sids; + + torture_comment(tctx, "\nTesting LookupSids2\n"); + + names.count = 0; + names.names = NULL; + + r.in.handle = handle; + r.in.sids = sids; + r.in.names = &names; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.count = &count; + r.out.names = &names; + r.out.domains = &domains; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids2_r(b, tctx, &r), + "LookupSids2 failed"); + if (!NT_STATUS_IS_OK(r.out.result) && + !NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED)) { + torture_comment(tctx, "LookupSids2 failed - %s\n", + nt_errstr(r.out.result)); + return false; + } + + torture_comment(tctx, "\n"); + + if (!test_LookupNames2(b, tctx, handle, level, &names, false)) { + return false; + } + + if (!test_LookupNames3(b, tctx, handle, level, &names, false)) { + return false; + } + + return true; +} + +static bool test_LookupSids3(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + enum lsa_LookupNamesLevel level, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids3 r; + struct lsa_TransNameArray2 names; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = sids->num_sids; + + torture_comment(tctx, "\nTesting LookupSids3\n"); + + names.count = 0; + names.names = NULL; + + r.in.sids = sids; + r.in.names = &names; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.domains = &domains; + r.out.count = &count; + r.out.names = &names; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids3_r(b, tctx, &r), + "LookupSids3 failed"); + + if (!NT_STATUS_IS_OK(r.out.result)) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) { + torture_comment(tctx, + "LookupSids3 failed: %s - not considered as an error", + nt_errstr(r.out.result)); + + return true; + } + + torture_assert_ntstatus_ok(tctx, + r.out.result, + "LookupSids3 failed"); + + return false; + } + + torture_comment(tctx, "\n"); + + if (!test_LookupNames4(b, tctx, level, &names, true)) { + return false; + } + + return true; +} + +static bool test_LookupSids3_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + enum lsa_LookupNamesLevel level, + struct lsa_SidArray *sids) +{ + struct lsa_LookupSids3 r; + struct lsa_TransNameArray2 names; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = sids->num_sids; + NTSTATUS status; + + torture_comment(tctx, "\nTesting LookupSids3\n"); + + names.count = 0; + names.names = NULL; + + r.in.sids = sids; + r.in.names = &names; + r.in.level = level; + r.in.count = &count; + r.in.lookup_options = 0; + r.in.client_revision = 0; + r.out.domains = &domains; + r.out.count = &count; + r.out.names = &names; + + status = dcerpc_lsa_LookupSids3_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + torture_comment(tctx, + "LookupSids3 correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "LookupSids3 return value should " + "be ACCESS_DENIED"); + return true; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "LookupNames4 correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + + torture_fail(tctx, + "LookupSids3 return value should be " + "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED"); + + return false; +} + +bool test_many_LookupSids(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level) +{ + uint32_t count; + struct lsa_SidArray sids; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding); + + torture_comment(tctx, "\nTesting LookupSids with lots of SIDs\n"); + + sids.num_sids = 100; + + sids.sids = talloc_array(tctx, struct lsa_SidPtr, sids.num_sids); + + for (i=0; ibinding_handle, + &auth_type, &auth_level); + + if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL && + auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) { + if (!test_LookupSids3(b, tctx, level, &sids)) { + return false; + } + if (!test_LookupNames4(b, tctx, level, &names, true)) { + return false; + } + } else { + /* + * If we don't have a secure channel these tests must + * fail with ACCESS_DENIED. + */ + if (!test_LookupSids3_fail(b, tctx, level, &sids)) { + return false; + } + if (!test_LookupNames4_fail(b, tctx, level)) { + return false; + } + } + } + + torture_comment(tctx, "\n"); + + + + return true; +} + +static void lookupsids_cb(struct tevent_req *subreq) +{ + int *replies = (int *)tevent_req_callback_data_void(subreq); + NTSTATUS status; + + status = dcerpc_lsa_LookupSids_r_recv(subreq, subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + printf("lookupsids returned %s\n", nt_errstr(status)); + *replies = -1; + } + + if (*replies >= 0) { + *replies += 1; + } +} + +static bool test_LookupSids_async(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + enum lsa_LookupNamesLevel level) +{ + struct lsa_SidArray sids; + struct lsa_SidPtr sidptr; + uint32_t *count; + struct lsa_TransNameArray *names; + struct lsa_LookupSids *r; + struct lsa_RefDomainList *domains = NULL; + struct tevent_req **req; + int i, replies; + bool ret = true; + const int num_async_requests = 50; + + count = talloc_array(tctx, uint32_t, num_async_requests); + names = talloc_array(tctx, struct lsa_TransNameArray, num_async_requests); + r = talloc_array(tctx, struct lsa_LookupSids, num_async_requests); + + torture_comment(tctx, "\nTesting %d async lookupsids request\n", num_async_requests); + + req = talloc_array(tctx, struct tevent_req *, num_async_requests); + + sids.num_sids = 1; + sids.sids = &sidptr; + sidptr.sid = dom_sid_parse_talloc(tctx, "S-1-5-32-545"); + + replies = 0; + + for (i=0; iev, b, &r[i]); + if (req[i] == NULL) { + ret = false; + break; + } + + tevent_req_set_callback(req[i], lookupsids_cb, &replies); + } + + while (replies >= 0 && replies < num_async_requests) { + tevent_loop_once(tctx->ev); + } + + talloc_free(req); + + if (replies < 0) { + ret = false; + } + + return ret; +} + +static bool test_LookupPrivValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_String *name) +{ + struct lsa_LookupPrivValue r; + struct lsa_LUID luid; + + r.in.handle = handle; + r.in.name = name; + r.out.luid = &luid; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivValue_r(b, tctx, &r), + "LookupPrivValue failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "LookupPrivValue failed"); + + return true; +} + +static bool test_LookupPrivName(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_LUID *luid) +{ + struct lsa_LookupPrivName r; + struct lsa_StringLarge *name = NULL; + + r.in.handle = handle; + r.in.luid = luid; + r.out.name = &name; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivName_r(b, tctx, &r), + "LookupPrivName failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LookupPrivName failed"); + + return true; +} + +static bool test_RemovePrivilegesFromAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *acct_handle, + struct lsa_LUID *luid) +{ + struct lsa_RemovePrivilegesFromAccount r; + struct lsa_PrivilegeSet privs; + bool ret = true; + + torture_comment(tctx, "\nTesting RemovePrivilegesFromAccount\n"); + + r.in.handle = acct_handle; + r.in.remove_all = 0; + r.in.privs = &privs; + + privs.count = 1; + privs.unknown = 0; + privs.set = talloc_array(tctx, struct lsa_LUIDAttribute, 1); + privs.set[0].luid = *luid; + privs.set[0].attribute = 0; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_RemovePrivilegesFromAccount_r(b, tctx, &r), + "RemovePrivilegesFromAccount failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + + struct lsa_LookupPrivName r_name; + struct lsa_StringLarge *name = NULL; + + r_name.in.handle = handle; + r_name.in.luid = luid; + r_name.out.name = &name; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivName_r(b, tctx, &r_name), + "LookupPrivName failed"); + if (!NT_STATUS_IS_OK(r_name.out.result)) { + torture_comment(tctx, "\nLookupPrivName failed - %s\n", + nt_errstr(r_name.out.result)); + return false; + } + /* Windows 2008 does not allow this to be removed */ + if (strcmp("SeAuditPrivilege", name->string) == 0) { + return ret; + } + + torture_comment(tctx, "RemovePrivilegesFromAccount failed to remove %s - %s\n", + name->string, + nt_errstr(r.out.result)); + return false; + } + + return ret; +} + +static bool test_AddPrivilegesToAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *acct_handle, + struct lsa_LUID *luid) +{ + struct lsa_AddPrivilegesToAccount r; + struct lsa_PrivilegeSet privs; + bool ret = true; + + torture_comment(tctx, "\nTesting AddPrivilegesToAccount\n"); + + r.in.handle = acct_handle; + r.in.privs = &privs; + + privs.count = 1; + privs.unknown = 0; + privs.set = talloc_array(tctx, struct lsa_LUIDAttribute, 1); + privs.set[0].luid = *luid; + privs.set[0].attribute = 0; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddPrivilegesToAccount_r(b, tctx, &r), + "AddPrivilegesToAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "AddPrivilegesToAccount failed"); + return ret; +} + +static bool test_EnumPrivsAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *acct_handle) +{ + struct lsa_EnumPrivsAccount r; + struct lsa_PrivilegeSet *privs = NULL; + bool ret = true; + + torture_comment(tctx, "\nTesting EnumPrivsAccount\n"); + + r.in.handle = acct_handle; + r.out.privs = &privs; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumPrivsAccount_r(b, tctx, &r), + "EnumPrivsAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "EnumPrivsAccount failed"); + + if (privs && privs->count > 0) { + int i; + for (i=0;icount;i++) { + test_LookupPrivName(b, tctx, handle, + &privs->set[i].luid); + } + + ret &= test_RemovePrivilegesFromAccount(b, tctx, handle, acct_handle, + &privs->set[0].luid); + ret &= test_AddPrivilegesToAccount(b, tctx, acct_handle, + &privs->set[0].luid); + } + + return ret; +} + +static bool test_GetSystemAccessAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *acct_handle) +{ + uint32_t access_mask; + struct lsa_GetSystemAccessAccount r; + + torture_comment(tctx, "\nTesting GetSystemAccessAccount\n"); + + r.in.handle = acct_handle; + r.out.access_mask = &access_mask; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(b, tctx, &r), + "GetSystemAccessAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "GetSystemAccessAccount failed"); + + if (r.out.access_mask != NULL) { + torture_comment(tctx, "Rights:"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_INTERACTIVE) + torture_comment(tctx, " LSA_POLICY_MODE_INTERACTIVE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_NETWORK) + torture_comment(tctx, " LSA_POLICY_MODE_NETWORK"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_BATCH) + torture_comment(tctx, " LSA_POLICY_MODE_BATCH"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_SERVICE) + torture_comment(tctx, " LSA_POLICY_MODE_SERVICE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_PROXY) + torture_comment(tctx, " LSA_POLICY_MODE_PROXY"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_INTERACTIVE) + torture_comment(tctx, " LSA_POLICY_MODE_DENY_INTERACTIVE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_NETWORK) + torture_comment(tctx, " LSA_POLICY_MODE_DENY_NETWORK"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_BATCH) + torture_comment(tctx, " LSA_POLICY_MODE_DENY_BATCH"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_SERVICE) + torture_comment(tctx, " LSA_POLICY_MODE_DENY_SERVICE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_REMOTE_INTERACTIVE) + torture_comment(tctx, " LSA_POLICY_MODE_REMOTE_INTERACTIVE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE) + torture_comment(tctx, " LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL) + torture_comment(tctx, " LSA_POLICY_MODE_ALL"); + if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL_NT4) + torture_comment(tctx, " LSA_POLICY_MODE_ALL_NT4"); + torture_comment(tctx, "\n"); + } + + return true; +} + +static bool test_Delete(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_Delete r; + + torture_comment(tctx, "\nTesting Delete\n"); + + r.in.handle = handle; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Delete_r(b, tctx, &r), + "Delete failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_SUPPORTED, + "Delete should have failed NT_STATUS_NOT_SUPPORTED"); + + return true; +} + +static bool test_DeleteObject(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_DeleteObject r; + + torture_comment(tctx, "\nTesting DeleteObject\n"); + + r.in.handle = handle; + r.out.handle = handle; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(b, tctx, &r), + "DeleteObject failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "DeleteObject failed"); + + return true; +} + + +static bool test_CreateAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_CreateAccount r; + struct dom_sid2 *newsid; + struct policy_handle acct_handle; + + newsid = dom_sid_parse_talloc(tctx, "S-1-5-12349876-4321-2854"); + + torture_comment(tctx, "\nTesting CreateAccount\n"); + + r.in.handle = handle; + r.in.sid = newsid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateAccount_r(b, tctx, &r), + "CreateAccount failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_OBJECT_NAME_COLLISION)) { + struct lsa_OpenAccount r_o; + r_o.in.handle = handle; + r_o.in.sid = newsid; + r_o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_o.out.acct_handle = &acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(b, tctx, &r_o), + "OpenAccount failed"); + torture_assert_ntstatus_ok(tctx, r_o.out.result, + "OpenAccount failed"); + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateAccount failed"); + } + + if (!test_Delete(b, tctx, &acct_handle)) { + return false; + } + + if (!test_DeleteObject(b, tctx, &acct_handle)) { + return false; + } + + return true; +} + +static bool test_DeleteTrustedDomain(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_StringLarge name) +{ + struct lsa_OpenTrustedDomainByName r; + struct policy_handle trustdom_handle; + + r.in.handle = handle; + r.in.name.string = name.string; + r.in.access_mask = SEC_STD_DELETE; + r.out.trustdom_handle = &trustdom_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomainByName_r(b, tctx, &r), + "OpenTrustedDomainByName failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "OpenTrustedDomainByName failed"); + + if (!test_Delete(b, tctx, &trustdom_handle)) { + return false; + } + + if (!test_DeleteObject(b, tctx, &trustdom_handle)) { + return false; + } + + return true; +} + +static bool test_DeleteTrustedDomainBySid(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct dom_sid *sid) +{ + struct lsa_DeleteTrustedDomain r; + + r.in.handle = handle; + r.in.dom_sid = sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteTrustedDomain_r(b, tctx, &r), + "DeleteTrustedDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "DeleteTrustedDomain failed"); + + return true; +} + + +static bool test_CreateSecret(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_CreateSecret r; + struct lsa_OpenSecret r2; + struct lsa_SetSecret r3; + struct lsa_QuerySecret r4; + struct lsa_SetSecret r5; + struct lsa_QuerySecret r6; + struct lsa_SetSecret r7; + struct lsa_QuerySecret r8; + struct policy_handle sec_handle, sec_handle2, sec_handle3; + struct lsa_DeleteObject d_o; + struct lsa_DATA_BUF buf1; + struct lsa_DATA_BUF_PTR bufp1; + struct lsa_DATA_BUF_PTR bufp2; + DATA_BLOB enc_key; + bool ret = true; + DATA_BLOB session_key; + NTTIME old_mtime, new_mtime; + DATA_BLOB blob1; + const char *secret1 = "abcdef12345699qwerty"; + char *secret2; + const char *secret3 = "ABCDEF12345699QWERTY"; + char *secret4; + const char *secret5 = "NEW-SAMBA4-SECRET"; + char *secret6; + char *secname[2]; + int i; + const int LOCAL = 0; + const int GLOBAL = 1; + struct dcerpc_binding_handle *b = p->binding_handle; + + secname[LOCAL] = talloc_asprintf(tctx, "torturesecret-%u", (unsigned int)random()); + secname[GLOBAL] = talloc_asprintf(tctx, "G$torturesecret-%u", (unsigned int)random()); + + for (i=0; i< 2; i++) { + torture_comment(tctx, "\nTesting CreateSecret of %s\n", secname[i]); + + init_lsa_String(&r.in.name, secname[i]); + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = &sec_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r), + "CreateSecret failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "CreateSecret failed"); + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = &sec_handle3; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r), + "CreateSecret failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_COLLISION, + "CreateSecret should have failed OBJECT_NAME_COLLISION"); + + r2.in.handle = handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.name = r.in.name; + r2.out.sec_handle = &sec_handle2; + + torture_comment(tctx, "Testing OpenSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(b, tctx, &r2), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, + "OpenSecret failed"); + + torture_assert_ntstatus_ok(tctx, dcerpc_fetch_session_key(p, &session_key), + "dcerpc_fetch_session_key failed"); + + enc_key = sess_encrypt_string(secret1, &session_key); + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + torture_comment(tctx, "Testing SetSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3), + "SetSecret failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, + "SetSecret failed"); + + r3.in.sec_handle = &sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + /* break the encrypted data */ + enc_key.data[0]++; + + torture_comment(tctx, "Testing SetSecret with broken key\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3), + "SetSecret failed"); + torture_assert_ntstatus_equal(tctx, r3.out.result, NT_STATUS_UNKNOWN_REVISION, + "SetSecret should have failed UNKNOWN_REVISION"); + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r4.in.sec_handle = &sec_handle; + r4.in.new_val = &bufp1; + r4.in.new_mtime = &new_mtime; + r4.in.old_val = NULL; + r4.in.old_mtime = NULL; + + bufp1.buf = NULL; + + torture_comment(tctx, "Testing QuerySecret\n"); + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r4), + "QuerySecret failed"); + if (!NT_STATUS_IS_OK(r4.out.result)) { + torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r4.out.result)); + ret = false; + } else { + if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) { + torture_comment(tctx, "No secret buffer returned\n"); + ret = false; + } else { + blob1.data = r4.out.new_val->buf->data; + blob1.length = r4.out.new_val->buf->size; + + secret2 = sess_decrypt_string(tctx, + &blob1, &session_key); + + if (strcmp(secret1, secret2) != 0) { + torture_comment(tctx, "Returned secret (r4) '%s' doesn't match '%s'\n", + secret2, secret1); + ret = false; + } + } + } + + enc_key = sess_encrypt_string(secret3, &session_key); + + r5.in.sec_handle = &sec_handle; + r5.in.new_val = &buf1; + r5.in.old_val = NULL; + r5.in.new_val->data = enc_key.data; + r5.in.new_val->length = enc_key.length; + r5.in.new_val->size = enc_key.length; + + + smb_msleep(200); + torture_comment(tctx, "Testing SetSecret (existing value should move to old)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r5), + "SetSecret failed"); + if (!NT_STATUS_IS_OK(r5.out.result)) { + torture_comment(tctx, "SetSecret failed - %s\n", nt_errstr(r5.out.result)); + ret = false; + } + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r6.in.sec_handle = &sec_handle; + r6.in.new_val = &bufp1; + r6.in.new_mtime = &new_mtime; + r6.in.old_val = &bufp2; + r6.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r6), + "QuerySecret failed"); + if (!NT_STATUS_IS_OK(r6.out.result)) { + torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r6.out.result)); + ret = false; + secret4 = NULL; + } else { + + if (r6.out.new_val->buf == NULL || r6.out.old_val->buf == NULL + || r6.out.new_mtime == NULL || r6.out.old_mtime == NULL) { + torture_comment(tctx, "Both secret buffers and both times not returned\n"); + ret = false; + secret4 = NULL; + } else { + blob1.data = r6.out.new_val->buf->data; + blob1.length = r6.out.new_val->buf->size; + + secret4 = sess_decrypt_string(tctx, + &blob1, &session_key); + + if (strcmp(secret3, secret4) != 0) { + torture_comment(tctx, "Returned NEW secret %s doesn't match %s\n", secret4, secret3); + ret = false; + } + + blob1.data = r6.out.old_val->buf->data; + blob1.length = r6.out.old_val->buf->length; + + secret2 = sess_decrypt_string(tctx, + &blob1, &session_key); + + if (strcmp(secret1, secret2) != 0) { + torture_comment(tctx, "Returned OLD secret %s doesn't match %s\n", secret2, secret1); + ret = false; + } + + if (*r6.out.new_mtime == *r6.out.old_mtime) { + torture_comment(tctx, "Returned secret (r6-%d) %s must not have same mtime for both secrets: %s != %s\n", + i, + secname[i], + nt_time_string(tctx, *r6.out.old_mtime), + nt_time_string(tctx, *r6.out.new_mtime)); + ret = false; + } + } + } + + enc_key = sess_encrypt_string(secret5, &session_key); + + r7.in.sec_handle = &sec_handle; + r7.in.old_val = &buf1; + r7.in.old_val->data = enc_key.data; + r7.in.old_val->length = enc_key.length; + r7.in.old_val->size = enc_key.length; + r7.in.new_val = NULL; + + torture_comment(tctx, "Testing SetSecret of old Secret only\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r7), + "SetSecret failed"); + if (!NT_STATUS_IS_OK(r7.out.result)) { + torture_comment(tctx, "SetSecret failed - %s\n", nt_errstr(r7.out.result)); + ret = false; + } + + data_blob_free(&enc_key); + + /* fetch the secret back again */ + r8.in.sec_handle = &sec_handle; + r8.in.new_val = &bufp1; + r8.in.new_mtime = &new_mtime; + r8.in.old_val = &bufp2; + r8.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r8), + "QuerySecret failed"); + if (!NT_STATUS_IS_OK(r8.out.result)) { + torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r8.out.result)); + ret = false; + } else { + if (!r8.out.new_val || !r8.out.old_val) { + torture_comment(tctx, "in/out pointers not returned, despite being set on in for QuerySecret\n"); + ret = false; + } else if (r8.out.new_val->buf != NULL) { + torture_comment(tctx, "NEW secret buffer must not be returned after OLD set\n"); + ret = false; + } else if (r8.out.old_val->buf == NULL) { + torture_comment(tctx, "OLD secret buffer was not returned after OLD set\n"); + ret = false; + } else if (r8.out.new_mtime == NULL || r8.out.old_mtime == NULL) { + torture_comment(tctx, "Both times not returned after OLD set\n"); + ret = false; + } else { + blob1.data = r8.out.old_val->buf->data; + blob1.length = r8.out.old_val->buf->size; + + secret6 = sess_decrypt_string(tctx, + &blob1, &session_key); + + if (strcmp(secret5, secret6) != 0) { + torture_comment(tctx, "Returned OLD secret %s doesn't match %s\n", secret5, secret6); + ret = false; + } + + if (*r8.out.new_mtime != *r8.out.old_mtime) { + torture_comment(tctx, "Returned secret (r8) %s did not had same mtime for both secrets: %s != %s\n", + secname[i], + nt_time_string(tctx, *r8.out.old_mtime), + nt_time_string(tctx, *r8.out.new_mtime)); + ret = false; + } + } + } + + if (!test_Delete(b, tctx, &sec_handle)) { + ret = false; + } + + if (!test_DeleteObject(b, tctx, &sec_handle)) { + return false; + } + + d_o.in.handle = &sec_handle2; + d_o.out.handle = &sec_handle2; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(b, tctx, &d_o), + "DeleteObject failed"); + torture_assert_ntstatus_equal(tctx, d_o.out.result, NT_STATUS_INVALID_HANDLE, + "OpenSecret expected INVALID_HANDLE"); + + torture_comment(tctx, "Testing OpenSecret of just-deleted secret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(b, tctx, &r2), + "OpenSecret failed"); + torture_assert_ntstatus_equal(tctx, r2.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, + "OpenSecret expected OBJECT_NAME_NOT_FOUND"); + } + return ret; +} + + +static bool test_EnumAccountRights(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *acct_handle, + struct dom_sid *sid) +{ + struct lsa_EnumAccountRights r; + struct lsa_RightSet rights; + + torture_comment(tctx, "\nTesting EnumAccountRights\n"); + + r.in.handle = acct_handle; + r.in.sid = sid; + r.out.rights = &rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(b, tctx, &r), + "EnumAccountRights failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "EnumAccountRights of %s failed - %s\n", + dom_sid_string(tctx, sid), nt_errstr(r.out.result)); + } + torture_assert_ntstatus_ok(tctx, r.out.result, + "EnumAccountRights failed"); + + return true; +} + + +static bool test_QuerySecurity(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *acct_handle) +{ + struct lsa_QuerySecurity r; + struct sec_desc_buf *sdbuf = NULL; + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "\nskipping QuerySecurity test against Samba4\n"); + return true; + } + + torture_comment(tctx, "\nTesting QuerySecurity\n"); + + r.in.handle = acct_handle; + r.in.sec_info = SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + r.out.sdbuf = &sdbuf; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecurity_r(b, tctx, &r), + "QuerySecurity failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "QuerySecurity failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + return true; +} + +static bool test_OpenAccount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct dom_sid *sid) +{ + struct lsa_OpenAccount r; + struct policy_handle acct_handle; + + torture_comment(tctx, "\nTesting OpenAccount\n"); + + r.in.handle = handle; + r.in.sid = sid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(b, tctx, &r), + "OpenAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "OpenAccount failed"); + + if (!test_EnumPrivsAccount(b, tctx, handle, &acct_handle)) { + return false; + } + + if (!test_GetSystemAccessAccount(b, tctx, handle, &acct_handle)) { + return false; + } + + if (!test_QuerySecurity(b, tctx, handle, &acct_handle)) { + return false; + } + + return true; +} + +static bool test_EnumAccounts(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_EnumAccounts r; + struct lsa_SidArray sids1, sids2; + uint32_t resume_handle = 0; + int i; + bool ret = true; + + torture_comment(tctx, "\nTesting EnumAccounts\n"); + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.num_entries = 100; + r.out.resume_handle = &resume_handle; + r.out.sids = &sids1; + + resume_handle = 0; + while (true) { + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(b, tctx, &r), + "EnumAccounts failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) { + break; + } + torture_assert_ntstatus_ok(tctx, r.out.result, + "EnumAccounts failed"); + + if (!test_LookupSids(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &sids1)) { + return false; + } + + if (!test_LookupSids2(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &sids1)) { + return false; + } + + /* Can't test lookupSids3 here, as clearly we must not + * be on schannel, or we would not be able to do the + * rest */ + + torture_comment(tctx, "Testing all accounts\n"); + for (i=0;istring); + + r.in.handle = handle; + r.in.name = priv_name; + r.in.language_id = language_id; + r.in.language_id_sys = 0; + r.out.returned_language_id = &returned_language_id; + r.out.disp_name = &disp_name; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivDisplayName_r(b, tctx, &r), + "LookupPrivDisplayName failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "LookupPrivDisplayName failed - %s\n", nt_errstr(r.out.result)); + return false; + } + torture_comment(tctx, "%s -> \"%s\" (language 0x%x/0x%x)\n", + priv_name->string, disp_name->string, + r.in.language_id, *r.out.returned_language_id); + + return true; +} + +static bool test_EnumAccountsWithUserRight(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_String *priv_name) +{ + struct lsa_EnumAccountsWithUserRight r; + struct lsa_SidArray sids; + + ZERO_STRUCT(sids); + + torture_comment(tctx, "\nTesting EnumAccountsWithUserRight(%s)\n", priv_name->string); + + r.in.handle = handle; + r.in.name = priv_name; + r.out.sids = &sids; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountsWithUserRight_r(b, tctx, &r), + "EnumAccountsWithUserRight failed"); + + /* NT_STATUS_NO_MORE_ENTRIES means no one has this privilege */ + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) { + return true; + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "EnumAccountsWithUserRight failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + return true; +} + + +static bool test_EnumPrivs(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_EnumPrivs r; + struct lsa_PrivArray privs1; + uint32_t resume_handle = 0; + int i; + bool ret = true; + + torture_comment(tctx, "\nTesting EnumPrivs\n"); + + r.in.handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_count = 100; + r.out.resume_handle = &resume_handle; + r.out.privs = &privs1; + + resume_handle = 0; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumPrivs_r(b, tctx, &r), + "EnumPrivs failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "EnumPrivs failed"); + + for (i = 0; i< privs1.count; i++) { + test_LookupPrivDisplayName(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name); + test_LookupPrivValue(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name); + if (!test_EnumAccountsWithUserRight(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name)) { + ret = false; + } + } + + return ret; +} + +static bool test_QueryForestTrustInformation(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *trusted_domain_name) +{ + bool ret = true; + struct lsa_lsaRQueryForestTrustInformation r; + struct lsa_String string; + struct lsa_ForestTrustInformation info, *info_ptr; + + torture_comment(tctx, "\nTesting lsaRQueryForestTrustInformation\n"); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping QueryForestTrustInformation against Samba4\n"); + return true; + } + + ZERO_STRUCT(string); + + if (trusted_domain_name) { + init_lsa_String(&string, trusted_domain_name); + } + + info_ptr = &info; + + r.in.handle = handle; + r.in.trusted_domain_name = &string; + r.in.highest_record_type = LSA_FOREST_TRUST_TOP_LEVEL_NAME; + r.out.forest_trust_info = &info_ptr; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_lsaRQueryForestTrustInformation_r(b, tctx, &r), + "lsaRQueryForestTrustInformation failed"); + + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "lsaRQueryForestTrustInformation of %s failed - %s\n", trusted_domain_name, nt_errstr(r.out.result)); + ret = false; + } + + return ret; +} + +static bool test_query_each_TrustDomEx(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_DomainListEx *domains) +{ + int i; + bool ret = true; + + for (i=0; i< domains->count; i++) { + + if (domains->domains[i].trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) { + ret &= test_QueryForestTrustInformation(b, tctx, handle, + domains->domains[i].domain_name.string); + } + } + + return ret; +} + +static bool test_query_each_TrustDom(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + struct lsa_DomainList *domains) +{ + int i,j; + bool ret = true; + + torture_comment(tctx, "\nTesting OpenTrustedDomain, OpenTrustedDomainByName and QueryInfoTrustedDomain\n"); + for (i=0; i< domains->count; i++) { + struct lsa_OpenTrustedDomain trust; + struct lsa_OpenTrustedDomainByName trust_by_name; + struct policy_handle trustdom_handle; + struct policy_handle handle2; + struct lsa_Close c; + struct lsa_CloseTrustedDomainEx c_trust; + int levels [] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + int ok[] = {1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1}; + + if (domains->domains[i].sid) { + trust.in.handle = handle; + trust.in.sid = domains->domains[i].sid; + trust.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + trust.out.trustdom_handle = &trustdom_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomain_r(b, tctx, &trust), + "OpenTrustedDomain failed"); + + if (NT_STATUS_EQUAL(trust.out.result, NT_STATUS_NO_SUCH_DOMAIN)) { + torture_comment(tctx, "DOMAIN(%s, %s) not a direct trust?\n", + domains->domains[i].name.string, + dom_sid_string(tctx, domains->domains[i].sid)); + continue; + } + if (!NT_STATUS_IS_OK(trust.out.result)) { + torture_comment(tctx, "OpenTrustedDomain failed - %s\n", nt_errstr(trust.out.result)); + return false; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + c_trust.in.handle = &trustdom_handle; + c_trust.out.handle = &handle2; + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info = NULL; + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[j]; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q), + "QueryTrustedDomainInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } + } + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CloseTrustedDomainEx_r(b, tctx, &c_trust), + "CloseTrustedDomainEx failed"); + if (!NT_STATUS_EQUAL(c_trust.out.result, NT_STATUS_NOT_IMPLEMENTED)) { + torture_comment(tctx, "Expected CloseTrustedDomainEx to return NT_STATUS_NOT_IMPLEMENTED, instead - %s\n", nt_errstr(c_trust.out.result)); + return false; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &c), + "Close failed"); + if (!NT_STATUS_IS_OK(c.out.result)) { + torture_comment(tctx, "Close of trusted domain failed - %s\n", nt_errstr(c.out.result)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfoBySid q; + union lsa_TrustedDomainInfo *info = NULL; + + if (!domains->domains[i].sid) { + continue; + } + + q.in.handle = handle; + q.in.dom_sid = domains->domains[i].sid; + q.in.level = levels[j]; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfoBySid_r(b, tctx, &q), + "lsa_QueryTrustedDomainInfoBySid failed"); + if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfoBySid level %d failed - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfoBySid level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } + } + } + + trust_by_name.in.handle = handle; + trust_by_name.in.name.string = domains->domains[i].name.string; + trust_by_name.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + trust_by_name.out.trustdom_handle = &trustdom_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomainByName_r(b, tctx, &trust_by_name), + "OpenTrustedDomainByName failed"); + + if (NT_STATUS_EQUAL(trust_by_name.out.result, NT_STATUS_NO_SUCH_DOMAIN)) { + torture_comment(tctx, "DOMAIN(%s, %s) not a direct trust?\n", + domains->domains[i].name.string, + dom_sid_string(tctx, domains->domains[i].sid)); + continue; + } + if (!NT_STATUS_IS_OK(trust_by_name.out.result)) { + torture_comment(tctx, "OpenTrustedDomainByName failed - %s\n", nt_errstr(trust_by_name.out.result)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info = NULL; + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[j]; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q), + "QueryTrustedDomainInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } + } + + c.in.handle = &trustdom_handle; + c.out.handle = &handle2; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &c), + "Close failed"); + if (!NT_STATUS_IS_OK(c.out.result)) { + torture_comment(tctx, "Close of trusted domain failed - %s\n", nt_errstr(c.out.result)); + return false; + } + + for (j=0; j < ARRAY_SIZE(levels); j++) { + struct lsa_QueryTrustedDomainInfoByName q; + union lsa_TrustedDomainInfo *info = NULL; + struct lsa_String name; + + name.string = domains->domains[i].name.string; + + q.in.handle = handle; + q.in.trusted_domain = &name; + q.in.level = levels[j]; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfoByName_r(b, tctx, &q), + "QueryTrustedDomainInfoByName failed"); + if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfoByName level %d failed - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) { + torture_comment(tctx, "QueryTrustedDomainInfoByName level %d unexpectedly succeeded - %s\n", + levels[j], nt_errstr(q.out.result)); + ret = false; + } + } + } + return ret; +} + +static bool test_EnumTrustDom(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_EnumTrustDom r; + uint32_t in_resume_handle = 0; + uint32_t out_resume_handle; + struct lsa_DomainList domains; + bool ret = true; + + torture_comment(tctx, "\nTesting EnumTrustDom\n"); + + r.in.handle = handle; + r.in.resume_handle = &in_resume_handle; + r.in.max_size = 0; + r.out.domains = &domains; + r.out.resume_handle = &out_resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b, tctx, &r), + "lsa_EnumTrustDom failed"); + + /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST + * always be larger than the previous input resume handle, in + * particular when hitting the last query it is vital to set the + * resume handle correctly to avoid infinite client loops, as + * seen e.g. with Windows XP SP3 when resume handle is 0 and + * status is NT_STATUS_OK - gd */ + + if (NT_STATUS_IS_OK(r.out.result) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES) || + NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) + { + if (out_resume_handle <= in_resume_handle) { + torture_comment(tctx, "EnumTrustDom failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n", + out_resume_handle, in_resume_handle); + return false; + } + } + + if (NT_STATUS_IS_OK(r.out.result)) { + if (domains.count == 0) { + torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + } else if (!(NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(tctx, "EnumTrustDom of zero size failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + /* Start from the bottom again */ + in_resume_handle = 0; + + do { + r.in.handle = handle; + r.in.resume_handle = &in_resume_handle; + r.in.max_size = LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3; + r.out.domains = &domains; + r.out.resume_handle = &out_resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b, tctx, &r), + "EnumTrustDom failed"); + + /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST + * always be larger than the previous input resume handle, in + * particular when hitting the last query it is vital to set the + * resume handle correctly to avoid infinite client loops, as + * seen e.g. with Windows XP SP3 when resume handle is 0 and + * status is NT_STATUS_OK - gd */ + + if (NT_STATUS_IS_OK(r.out.result) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES) || + NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) + { + if (out_resume_handle <= in_resume_handle) { + torture_comment(tctx, "EnumTrustDom failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n", + out_resume_handle, in_resume_handle); + return false; + } + } + + /* NO_MORE_ENTRIES is allowed */ + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) { + if (domains.count == 0) { + return true; + } + torture_comment(tctx, "EnumTrustDom failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n"); + return false; + } else if (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) { + /* Windows 2003 gets this off by one on the first run */ + if (r.out.domains->count < 3 || r.out.domains->count > 4) { + torture_comment(tctx, "EnumTrustDom didn't fill the buffer we " + "asked it to (got %d, expected %d / %d == %d entries)\n", + r.out.domains->count, LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3, + LSA_ENUM_TRUST_DOMAIN_MULTIPLIER, r.in.max_size); + ret = false; + } + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "EnumTrustDom failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + if (domains.count == 0) { + torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + + ret &= test_query_each_TrustDom(b, tctx, handle, &domains); + + in_resume_handle = out_resume_handle; + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + + return ret; +} + +static bool test_EnumTrustDomEx(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_EnumTrustedDomainsEx r_ex; + uint32_t in_resume_handle = 0; + uint32_t out_resume_handle; + struct lsa_DomainListEx domains_ex; + bool ret = true; + + torture_comment(tctx, "\nTesting EnumTrustedDomainsEx\n"); + + r_ex.in.handle = handle; + r_ex.in.resume_handle = &in_resume_handle; + r_ex.in.max_size = 0; + r_ex.out.domains = &domains_ex; + r_ex.out.resume_handle = &out_resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustedDomainsEx_r(b, tctx, &r_ex), + "EnumTrustedDomainsEx failed"); + + /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST + * always be larger than the previous input resume handle, in + * particular when hitting the last query it is vital to set the + * resume handle correctly to avoid infinite client loops, as + * seen e.g. with Windows XP SP3 when resume handle is 0 and + * status is NT_STATUS_OK - gd */ + + if (NT_STATUS_IS_OK(r_ex.out.result) || + NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES) || + NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES)) + { + if (out_resume_handle <= in_resume_handle) { + torture_comment(tctx, "EnumTrustDomEx failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n", + out_resume_handle, in_resume_handle); + return false; + } + } + + if (NT_STATUS_IS_OK(r_ex.out.result)) { + if (domains_ex.count == 0) { + torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + } else if (!(NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES) || + NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES))) { + torture_comment(tctx, "EnumTrustDom of zero size failed - %s\n", + nt_errstr(r_ex.out.result)); + return false; + } + + in_resume_handle = 0; + do { + r_ex.in.handle = handle; + r_ex.in.resume_handle = &in_resume_handle; + r_ex.in.max_size = LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER * 3; + r_ex.out.domains = &domains_ex; + r_ex.out.resume_handle = &out_resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustedDomainsEx_r(b, tctx, &r_ex), + "EnumTrustedDomainsEx failed"); + + in_resume_handle = out_resume_handle; + + /* NO_MORE_ENTRIES is allowed */ + if (NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES)) { + if (domains_ex.count == 0) { + return true; + } + torture_comment(tctx, "EnumTrustDomainsEx failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n"); + return false; + } else if (NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES)) { + /* Windows 2003 gets this off by one on the first run */ + if (r_ex.out.domains->count < 3 || r_ex.out.domains->count > 4) { + torture_comment(tctx, "EnumTrustDom didn't fill the buffer we " + "asked it to (got %d, expected %d / %d == %d entries)\n", + r_ex.out.domains->count, + r_ex.in.max_size, + LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER, + r_ex.in.max_size / LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER); + } + } else if (!NT_STATUS_IS_OK(r_ex.out.result)) { + torture_comment(tctx, "EnumTrustedDomainEx failed - %s\n", nt_errstr(r_ex.out.result)); + return false; + } + + if (domains_ex.count == 0) { + torture_comment(tctx, "EnumTrustDomainEx failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n"); + return false; + } + + ret &= test_query_each_TrustDomEx(b, tctx, handle, &domains_ex); + + } while (NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES)); + + return ret; +} + + +static bool test_CreateTrustedDomain(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t num_trusts) +{ + bool ret = true; + struct lsa_CreateTrustedDomain r; + struct lsa_DomainInfo trustinfo; + struct dom_sid **domsid; + struct policy_handle *trustdom_handle; + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info = NULL; + int i; + + torture_comment(tctx, "\nTesting CreateTrustedDomain for %d domains\n", num_trusts); + + if (!test_EnumTrustDom(b, tctx, handle)) { + ret = false; + } + + if (!test_EnumTrustDomEx(b, tctx, handle)) { + ret = false; + } + + domsid = talloc_array(tctx, struct dom_sid *, num_trusts); + trustdom_handle = talloc_array(tctx, struct policy_handle, num_trusts); + + for (i=0; i< num_trusts; i++) { + char *trust_name = talloc_asprintf(tctx, "TORTURE1%02d", i); + char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-1%02d", i); + + domsid[i] = dom_sid_parse_talloc(tctx, trust_sid); + + trustinfo.sid = domsid[i]; + init_lsa_String((struct lsa_String *)&trustinfo.name, trust_name); + + r.in.policy_handle = handle; + r.in.info = &trustinfo; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.trustdom_handle = &trustdom_handle[i]; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateTrustedDomain_r(b, tctx, &r), + "CreateTrustedDomain failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_OBJECT_NAME_COLLISION)) { + test_DeleteTrustedDomain(b, tctx, handle, trustinfo.name); + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateTrustedDomain_r(b, tctx, &r), + "CreateTrustedDomain failed"); + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateTrustedDomain failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } else { + + q.in.trustdom_handle = &trustdom_handle[i]; + q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q), + "QueryTrustedDomainInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n", q.in.level, nt_errstr(q.out.result)); + ret = false; + } else if (!q.out.info) { + ret = false; + } else { + if (strcmp(info->info_ex.domain_name.string, trustinfo.name.string) != 0) { + torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent long name: %s != %s\n", + info->info_ex.domain_name.string, trustinfo.name.string); + ret = false; + } + if (strcmp(info->info_ex.netbios_name.string, trustinfo.name.string) != 0) { + torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent short name: %s != %s\n", + info->info_ex.netbios_name.string, trustinfo.name.string); + ret = false; + } + if (info->info_ex.trust_type != LSA_TRUST_TYPE_DOWNLEVEL) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n", + trust_name, info->info_ex.trust_type, LSA_TRUST_TYPE_DOWNLEVEL); + ret = false; + } + if (info->info_ex.trust_attributes != 0) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n", + trust_name, info->info_ex.trust_attributes, 0); + ret = false; + } + if (info->info_ex.trust_direction != LSA_TRUST_DIRECTION_OUTBOUND) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n", + trust_name, info->info_ex.trust_direction, LSA_TRUST_DIRECTION_OUTBOUND); + ret = false; + } + } + } + } + + /* now that we have some domains to look over, we can test the enum calls */ + if (!test_EnumTrustDom(b, tctx, handle)) { + ret = false; + } + + if (!test_EnumTrustDomEx(b, tctx, handle)) { + ret = false; + } + + for (i=0; iauth_blob.size = auth_blob.length; + authinfo_internal->auth_blob.data = auth_blob.data; + + *_authinfo_internal = authinfo_internal; + + return true; +} + +static bool gen_authinfo(TALLOC_CTX *mem_ctx, + const char *incoming_old, const char *incoming_new, + const char *outgoing_old, const char *outgoing_new, + struct lsa_TrustDomainInfoAuthInfo **_authinfo) +{ + struct lsa_TrustDomainInfoAuthInfo *authinfo; + struct lsa_TrustDomainInfoBuffer *in_buffer; + struct lsa_TrustDomainInfoBuffer *io_buffer; + struct lsa_TrustDomainInfoBuffer *on_buffer; + struct lsa_TrustDomainInfoBuffer *oo_buffer; + size_t converted_size; + bool ok; + + authinfo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoAuthInfo); + if (authinfo == NULL) { + return false; + } + + in_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer); + if (in_buffer == NULL) { + return false; + } + in_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR; + ok = convert_string_talloc(in_buffer, CH_UNIX, CH_UTF16, + incoming_new, + strlen(incoming_new), + &in_buffer->data.data, + &converted_size); + if (!ok) { + return false; + } + in_buffer->data.size = converted_size; + + io_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer); + if (io_buffer == NULL) { + return false; + } + io_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR; + ok = convert_string_talloc(io_buffer, CH_UNIX, CH_UTF16, + incoming_old, + strlen(incoming_old), + &io_buffer->data.data, + &converted_size); + if (!ok) { + return false; + } + io_buffer->data.size = converted_size; + + on_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer); + if (on_buffer == NULL) { + return false; + } + on_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR; + ok = convert_string_talloc(on_buffer, CH_UNIX, CH_UTF16, + outgoing_new, + strlen(outgoing_new), + &on_buffer->data.data, + &converted_size); + if (!ok) { + return false; + } + on_buffer->data.size = converted_size; + + oo_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer); + if (oo_buffer == NULL) { + return false; + } + oo_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR; + ok = convert_string_talloc(oo_buffer, CH_UNIX, CH_UTF16, + outgoing_old, + strlen(outgoing_old), + &oo_buffer->data.data, + &converted_size); + if (!ok) { + return false; + } + oo_buffer->data.size = converted_size; + + authinfo->incoming_count = 1; + authinfo->incoming_current_auth_info = in_buffer; + authinfo->incoming_previous_auth_info = io_buffer; + authinfo->outgoing_count = 1; + authinfo->outgoing_current_auth_info = on_buffer; + authinfo->outgoing_previous_auth_info = oo_buffer; + + *_authinfo = authinfo; + + return true; +} + +static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t negotiate_flags, + const char *server_name, + struct cli_credentials *machine_credentials, + struct netlogon_creds_CredentialState **creds_out) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + const struct samr_Password *new_password = NULL; + const struct samr_Password *old_password = NULL; + uint32_t rid; + struct dcerpc_binding_handle *b = p->binding_handle; + + new_password = cli_credentials_get_nt_hash(machine_credentials, tctx); + old_password = cli_credentials_get_old_nt_hash(machine_credentials, tctx); + + r.in.server_name = server_name; + r.in.computer_name = cli_credentials_get_workstation(machine_credentials); + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + a.in.server_name = server_name; + a.in.account_name = cli_credentials_get_username(machine_credentials); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = cli_credentials_get_workstation(machine_credentials); + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + new_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + if (!NT_STATUS_IS_OK(a.out.result)) { + if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_assert_ntstatus_ok(tctx, a.out.result, + "ServerAuthenticate3 failed"); + } + return false; + } + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + if (old_password != NULL) { + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + old_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + if (!NT_STATUS_IS_OK(a.out.result)) { + if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_assert_ntstatus_ok(tctx, a.out.result, + "ServerAuthenticate3 (old) failed"); + } + return false; + } + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential (old) chaining failed"); + } + + /* Prove that requesting a challenge again won't break it */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + *creds_out = creds; + return true; +} + +#ifdef SAMBA4_USES_HEIMDAL + +/* + * This function is set in torture_krb5_init_context as krb5 + * send_and_recv function. This allows us to override what server the + * test is aimed at, and to inspect the packets just before they are + * sent to the network, and before they are processed on the recv + * side. + * + * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test() + * functions are implement the actual tests. + * + * When this asserts, the caller will get a spurious 'cannot contact + * any KDC' message. + * + */ +struct check_pw_with_krb5_ctx { + struct addrinfo *server; + const char *server_nb_domain; + const char *server_dns_domain; + struct { + unsigned io; + unsigned fail; + unsigned errors; + unsigned error_io; + unsigned ok; + } counts; + krb5_error error; + struct smb_krb5_context *smb_krb5_context; + krb5_get_init_creds_opt *krb_options; + krb5_creds my_creds; + krb5_get_creds_opt opt_canon; + krb5_get_creds_opt opt_nocanon; + krb5_principal upn_realm; + krb5_principal upn_dns; + krb5_principal upn_netbios; + krb5_ccache krbtgt_ccache; + krb5_principal krbtgt_trust_realm; + krb5_creds *krbtgt_trust_realm_creds; + krb5_principal krbtgt_trust_dns; + krb5_creds *krbtgt_trust_dns_creds; + krb5_principal krbtgt_trust_netbios; + krb5_creds *krbtgt_trust_netbios_creds; + krb5_principal cifs_trust_dns; + krb5_creds *cifs_trust_dns_creds; + krb5_principal cifs_trust_netbios; + krb5_creds *cifs_trust_netbios_creds; + krb5_principal drs_trust_dns; + krb5_creds *drs_trust_dns_creds; + krb5_principal drs_trust_netbios; + krb5_creds *drs_trust_netbios_creds; + krb5_principal four_trust_dns; + krb5_creds *four_trust_dns_creds; + krb5_creds krbtgt_referral_creds; + Ticket krbtgt_referral_ticket; + krb5_keyblock krbtgt_referral_keyblock; + EncTicketPart krbtgt_referral_enc_part; +}; + +static krb5_error_code check_pw_with_krb5_send_to_realm( + struct smb_krb5_context *smb_krb5_context, + void *data, /* struct check_pw_with_krb5_ctx */ + krb5_const_realm realm, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + struct check_pw_with_krb5_ctx *ctx = + talloc_get_type_abort(data, struct check_pw_with_krb5_ctx); + krb5_error_code k5ret; + size_t used; + int ret; + + SMB_ASSERT(smb_krb5_context == ctx->smb_krb5_context); + + if (!strequal_m(realm, ctx->server_nb_domain) && + !strequal_m(realm, ctx->server_dns_domain)) + { + return KRB5_KDC_UNREACH; + } + + krb5_free_error_contents(ctx->smb_krb5_context->krb5_context, + &ctx->error); + ctx->counts.io++; + + k5ret = smb_krb5_send_and_recv_func_forced_tcp(ctx->smb_krb5_context, + ctx->server, + timeout, send_buf, recv_buf); + if (k5ret != 0) { + ctx->counts.fail++; + return k5ret; + } + + ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length, + &ctx->error, &used); + if (ret == 0) { + ctx->counts.errors++; + ctx->counts.error_io = ctx->counts.io; + } else { + ctx->counts.ok++; + } + + return k5ret; +} + +static int check_pw_with_krb5_ctx_destructor(struct check_pw_with_krb5_ctx *ctx) +{ + if (ctx->server != NULL) { + freeaddrinfo(ctx->server); + ctx->server = NULL; + } + + if (ctx->krb_options != NULL) { + krb5_get_init_creds_opt_free(ctx->smb_krb5_context->krb5_context, + ctx->krb_options); + ctx->krb_options = NULL; + } + + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds); + + if (ctx->opt_canon != NULL) { + krb5_get_creds_opt_free(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon); + ctx->opt_canon = NULL; + } + + if (ctx->opt_nocanon != NULL) { + krb5_get_creds_opt_free(ctx->smb_krb5_context->krb5_context, + ctx->opt_nocanon); + ctx->opt_nocanon = NULL; + } + + if (ctx->krbtgt_ccache != NULL) { + krb5_cc_close(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache); + ctx->krbtgt_ccache = NULL; + } + + if (ctx->upn_realm != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->upn_realm); + ctx->upn_realm = NULL; + } + + if (ctx->upn_dns != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->upn_dns); + ctx->upn_dns = NULL; + } + + if (ctx->upn_netbios != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->upn_netbios); + ctx->upn_netbios = NULL; + } + + if (ctx->krbtgt_trust_realm != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_realm); + ctx->krbtgt_trust_realm = NULL; + } + + if (ctx->krbtgt_trust_realm_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_realm_creds); + ctx->krbtgt_trust_realm_creds = NULL; + } + + if (ctx->krbtgt_trust_dns != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_dns); + ctx->krbtgt_trust_dns = NULL; + } + + if (ctx->krbtgt_trust_dns_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_dns_creds); + ctx->krbtgt_trust_dns_creds = NULL; + } + + if (ctx->krbtgt_trust_netbios != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_netbios); + ctx->krbtgt_trust_netbios = NULL; + } + + if (ctx->krbtgt_trust_netbios_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_netbios_creds); + ctx->krbtgt_trust_netbios_creds = NULL; + } + + if (ctx->cifs_trust_dns != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->cifs_trust_dns); + ctx->cifs_trust_dns = NULL; + } + + if (ctx->cifs_trust_dns_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->cifs_trust_dns_creds); + ctx->cifs_trust_dns_creds = NULL; + } + + if (ctx->cifs_trust_netbios != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->cifs_trust_netbios); + ctx->cifs_trust_netbios = NULL; + } + + if (ctx->cifs_trust_netbios_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->cifs_trust_netbios_creds); + ctx->cifs_trust_netbios_creds = NULL; + } + + if (ctx->drs_trust_dns != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->drs_trust_dns); + ctx->drs_trust_dns = NULL; + } + + if (ctx->drs_trust_dns_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->drs_trust_dns_creds); + ctx->drs_trust_dns_creds = NULL; + } + + if (ctx->drs_trust_netbios != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->drs_trust_netbios); + ctx->drs_trust_netbios = NULL; + } + + if (ctx->drs_trust_netbios_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->drs_trust_netbios_creds); + ctx->drs_trust_netbios_creds = NULL; + } + + if (ctx->four_trust_dns != NULL) { + krb5_free_principal(ctx->smb_krb5_context->krb5_context, + ctx->four_trust_dns); + ctx->four_trust_dns = NULL; + } + + if (ctx->four_trust_dns_creds != NULL) { + krb5_free_creds(ctx->smb_krb5_context->krb5_context, + ctx->four_trust_dns_creds); + ctx->four_trust_dns_creds = NULL; + } + + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + + free_Ticket(&ctx->krbtgt_referral_ticket); + + krb5_free_keyblock_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_keyblock); + + free_EncTicketPart(&ctx->krbtgt_referral_enc_part); + + krb5_free_error_contents(ctx->smb_krb5_context->krb5_context, + &ctx->error); + + talloc_unlink(ctx, ctx->smb_krb5_context); + ctx->smb_krb5_context = NULL; + return 0; +} + +static bool check_pw_with_krb5(struct torture_context *tctx, + struct cli_credentials *credentials, + const struct lsa_TrustDomainInfoInfoEx *trusted) +{ + const char *trusted_dns_name = trusted->domain_name.string; + const char *trusted_netbios_name = trusted->netbios_name.string; + char *trusted_realm_name = NULL; + krb5_principal principal = NULL; + enum credentials_obtained obtained; + const char *error_string = NULL; + const char *workstation = cli_credentials_get_workstation(credentials); + const char *password = cli_credentials_get_password(credentials); +#ifndef USING_EMBEDDED_HEIMDAL + const struct samr_Password *nthash = NULL; + const struct samr_Password *old_nthash = NULL; +#endif + const char *old_password = cli_credentials_get_old_password(credentials); +#ifndef USING_EMBEDDED_HEIMDAL + int kvno = cli_credentials_get_kvno(credentials); + int expected_kvno = 0; + krb5uint32 t_kvno = 0; +#endif + const char *host = torture_setting_string(tctx, "host", NULL); + krb5_error_code k5ret; + krb5_boolean k5ok; + int type; + bool ok; + struct check_pw_with_krb5_ctx *ctx = NULL; + char *assertion_message = NULL; + const char *realm = NULL; + char *upn_realm_string = NULL; + char *upn_dns_string = NULL; + char *upn_netbios_string = NULL; + char *krbtgt_cc_name = NULL; + char *krbtgt_trust_realm_string = NULL; + char *krbtgt_trust_dns_string = NULL; + char *krbtgt_trust_netbios_string = NULL; + char *cifs_trust_dns_string = NULL; + char *cifs_trust_netbios_string = NULL; + char *drs_trust_dns_string = NULL; + char *drs_trust_netbios_string = NULL; + char *four_trust_dns_string = NULL; + + ctx = talloc_zero(tctx, struct check_pw_with_krb5_ctx); + torture_assert(tctx, ctx != NULL, "Failed to allocate"); + + realm = cli_credentials_get_realm(credentials); + trusted_realm_name = strupper_talloc(tctx, trusted_dns_name); + +#ifndef USING_EMBEDDED_HEIMDAL + nthash = cli_credentials_get_nt_hash(credentials, ctx); + old_nthash = cli_credentials_get_old_nt_hash(credentials, ctx); +#endif + + k5ret = smb_krb5_init_context(ctx, tctx->lp_ctx, &ctx->smb_krb5_context); + torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); + + ctx->server_nb_domain = cli_credentials_get_domain(credentials); + ctx->server_dns_domain = cli_credentials_get_realm(credentials); + + ok = interpret_string_addr_internal(&ctx->server, host, 0); + torture_assert(tctx, ok, "Failed to parse target server"); + talloc_set_destructor(ctx, check_pw_with_krb5_ctx_destructor); + + set_sockaddr_port(ctx->server->ai_addr, 88); + + k5ret = smb_krb5_set_send_to_kdc_func(ctx->smb_krb5_context, + check_pw_with_krb5_send_to_realm, + NULL, /* send_to_kdc */ + ctx); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(ctx->smb_krb5_context->krb5_context, + &ctx->krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_pac_request( + ctx->smb_krb5_context->krb5_context, + ctx->krb_options, true), + 0, "krb5_get_init_creds_opt_set_pac_request failed"); + + upn_realm_string = talloc_asprintf(ctx, "user@%s", + trusted_realm_name); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->upn_realm, + realm, upn_realm_string, NULL), + 0, "smb_krb5_make_principal failed"); + smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context, + ctx->upn_realm, KRB5_NT_ENTERPRISE_PRINCIPAL); + + upn_dns_string = talloc_asprintf(ctx, "user@%s", + trusted_dns_name); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->upn_dns, + realm, upn_dns_string, NULL), + 0, "smb_krb5_make_principal failed"); + smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context, + ctx->upn_dns, KRB5_NT_ENTERPRISE_PRINCIPAL); + + upn_netbios_string = talloc_asprintf(ctx, "user@%s", + trusted_netbios_name); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->upn_netbios, + realm, upn_netbios_string, NULL), + 0, "smb_krb5_make_principal failed"); + smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context, + ctx->upn_netbios, KRB5_NT_ENTERPRISE_PRINCIPAL); + + k5ret = principal_from_credentials(ctx, credentials, ctx->smb_krb5_context, + &principal, &obtained, &error_string); + torture_assert_int_equal(tctx, k5ret, 0, error_string); + + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds, ctx->upn_realm, + "_none_", NULL, NULL, 0, + NULL, ctx->krb_options); + assertion_message = talloc_asprintf(ctx, + "krb5_get_init_creds_password(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]", + upn_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message); + torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message); + torture_assert(tctx, ctx->error.crealm != NULL, assertion_message); + torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert(tctx, ctx->error.cname != NULL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message); + torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_realm_string, assertion_message); +#else + torture_assert(tctx, ctx->error.cname == NULL, assertion_message); +#endif + torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message); + + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds, ctx->upn_dns, + "_none_", NULL, NULL, 0, + NULL, ctx->krb_options); + assertion_message = talloc_asprintf(ctx, + "krb5_get_init_creds_password(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]", + upn_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message); + torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message); + torture_assert(tctx, ctx->error.crealm != NULL, assertion_message); + torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert(tctx, ctx->error.cname != NULL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message); + torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_dns_string, assertion_message); +#else + torture_assert(tctx, ctx->error.cname == NULL, assertion_message); +#endif + torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message); + + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds, ctx->upn_netbios, + "_none_", NULL, NULL, 0, + NULL, ctx->krb_options); + assertion_message = talloc_asprintf(ctx, + "krb5_get_init_creds_password(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]", + upn_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message); + torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message); + torture_assert(tctx, ctx->error.crealm != NULL, assertion_message); + torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert(tctx, ctx->error.cname != NULL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message); + torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message); + torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_netbios_string, assertion_message); +#else + torture_assert(tctx, ctx->error.cname == NULL, assertion_message); +#endif + torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message); + + torture_comment(tctx, "(%s:%s) password[%s] old_password[%s]\n", + __location__, __FUNCTION__, + password, old_password); + if (old_password != NULL) { + k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds, principal, + old_password, NULL, NULL, 0, + NULL, ctx->krb_options); + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, + "preauth should fail with old password"); + } + + k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context, + &ctx->my_creds, principal, + password, NULL, NULL, 0, + NULL, ctx->krb_options); + if (k5ret == KRB5KDC_ERR_PREAUTH_FAILED) { + TALLOC_FREE(ctx); + return false; + } + + assertion_message = talloc_asprintf(ctx, + "krb5_get_init_creds_password for failed: %s", + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(ctx->smb_krb5_context->krb5_context, + &ctx->opt_canon), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + KRB5_GC_CANONICALIZE); + + krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + KRB5_GC_NO_STORE); + + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(ctx->smb_krb5_context->krb5_context, + &ctx->opt_nocanon), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context, + ctx->opt_nocanon, + KRB5_GC_NO_STORE); + + krbtgt_cc_name = talloc_asprintf(ctx, "MEMORY:%p.krbtgt", ctx->smb_krb5_context); + torture_assert_int_equal(tctx, + krb5_cc_resolve(ctx->smb_krb5_context->krb5_context, + krbtgt_cc_name, + &ctx->krbtgt_ccache), + 0, "krb5_cc_resolve failed"); + + torture_assert_int_equal(tctx, + krb5_cc_initialize(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + ctx->my_creds.client), + 0, "krb5_cc_initialize failed"); + + torture_assert_int_equal(tctx, + krb5_cc_store_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + &ctx->my_creds), + 0, "krb5_cc_store_cred failed"); + + krbtgt_trust_realm_string = talloc_asprintf(ctx, "krbtgt/%s@%s", + trusted_realm_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_trust_realm, + realm, "krbtgt", + trusted_realm_name, NULL), + 0, "smb_krb5_make_principal failed"); + + krbtgt_trust_dns_string = talloc_asprintf(ctx, "krbtgt/%s@%s", + trusted_dns_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_trust_dns, + realm, "krbtgt", + trusted_dns_name, NULL), + 0, "smb_krb5_make_principal failed"); + + krbtgt_trust_netbios_string = talloc_asprintf(ctx, "krbtgt/%s@%s", + trusted_netbios_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_trust_netbios, + realm, "krbtgt", + trusted_netbios_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we can do a TGS for krbtgt/trusted_realm */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_nocanon, + ctx->krbtgt_ccache, + ctx->krbtgt_trust_realm, + &ctx->krbtgt_trust_realm_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_realm_creds->server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_realm_creds->server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Confirm if we have no referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); + + /* Confirm if we can do a TGS for krbtgt/trusted_dns with CANON */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->krbtgt_trust_dns, + &ctx->krbtgt_trust_dns_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + krbtgt_trust_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#endif + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + k5ret = decode_Ticket(ctx->krbtgt_referral_creds.ticket.data, + ctx->krbtgt_referral_creds.ticket.length, + &ctx->krbtgt_referral_ticket, NULL); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + if (kvno > 0) { + expected_kvno = kvno - 1; + } + if (ctx->krbtgt_referral_ticket.enc_part.kvno != NULL) { + t_kvno = *ctx->krbtgt_referral_ticket.enc_part.kvno; + assertion_message = talloc_asprintf(ctx, + "krbtgt_referral_ticket(%s) kvno(%u) expected(%u) current(%u)", + krbtgt_trust_realm_string, + (unsigned)t_kvno, (unsigned)expected_kvno,(unsigned)kvno); + torture_comment(tctx, "%s\n", assertion_message); + torture_assert_int_not_equal(tctx, t_kvno, 0, assertion_message); + } else { + assertion_message = talloc_asprintf(ctx, + "krbtgt_referral_ticket(%s) kvno(NULL) expected(%u) current(%u)", + krbtgt_trust_realm_string, + (unsigned)expected_kvno,(unsigned)kvno); + torture_comment(tctx, "%s\n", assertion_message); + } + torture_assert_int_equal(tctx, t_kvno, expected_kvno, assertion_message); + + if (old_nthash != NULL && expected_kvno != kvno) { + torture_comment(tctx, "old_nthash: %s\n", assertion_message); + k5ret = smb_krb5_keyblock_init_contents(ctx->smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + old_nthash->hash, + sizeof(old_nthash->hash), + &ctx->krbtgt_referral_keyblock); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } else { + torture_comment(tctx, "nthash: %s\n", assertion_message); + k5ret = smb_krb5_keyblock_init_contents(ctx->smb_krb5_context->krb5_context, + ENCTYPE_ARCFOUR_HMAC, + nthash->hash, + sizeof(nthash->hash), + &ctx->krbtgt_referral_keyblock); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } + k5ret = krb5_decrypt_ticket(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_ticket, + &ctx->krbtgt_referral_keyblock, + &ctx->krbtgt_referral_enc_part, + 0); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + /* Confirm if we can do a TGS for krbtgt/trusted_dns no CANON */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_nocanon, + ctx->krbtgt_ccache, + ctx->krbtgt_trust_dns, + &ctx->krbtgt_trust_dns_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s, nocanon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + krbtgt_trust_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#endif + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_dns_creds->server, +#ifdef USING_EMBEDDED_HEIMDAL + ctx->krbtgt_trust_dns); +#else + ctx->krbtgt_trust_realm); +#endif + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_dns_creds->server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + /* Confirm if we can do a TGS for krbtgt/NETBIOS with CANON */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->krbtgt_trust_netbios, + &ctx->krbtgt_trust_netbios_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s, canon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + krbtgt_trust_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#endif + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + /* Confirm if we can do a TGS for krbtgt/NETBIOS no CANON */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_nocanon, + ctx->krbtgt_ccache, + ctx->krbtgt_trust_netbios, + &ctx->krbtgt_trust_netbios_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s, nocanon) for failed: " + "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + krbtgt_trust_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#endif + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_netbios_creds->server, +#ifdef USING_EMBEDDED_HEIMDAL + ctx->krbtgt_trust_netbios); +#else + ctx->krbtgt_trust_realm); +#endif + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_trust_netbios_creds->server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + cifs_trust_dns_string = talloc_asprintf(ctx, "cifs/%s@%s", + trusted_dns_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->cifs_trust_dns, + realm, "cifs", + trusted_dns_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we get krbtgt/trusted_realm back when asking for cifs/trusted_realm */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->cifs_trust_dns, + &ctx->cifs_trust_dns_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + cifs_trust_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#endif + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + cifs_trust_netbios_string = talloc_asprintf(ctx, "cifs/%s@%s", + trusted_netbios_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->cifs_trust_netbios, + realm, "cifs", + trusted_netbios_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we get krbtgt/trusted_realm back when asking for cifs/trusted_realm */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->cifs_trust_netbios, + &ctx->cifs_trust_netbios_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + cifs_trust_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#endif + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + drs_trust_dns_string = talloc_asprintf(ctx, + "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s", + workstation, trusted_dns_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->drs_trust_dns, + realm, "E3514235-4B06-11D1-AB04-00C04FC2DCD2", + workstation, trusted_dns_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we get krbtgt/trusted_realm back when asking for a 3 part principal */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->drs_trust_dns, + &ctx->drs_trust_dns_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + drs_trust_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#endif + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + drs_trust_netbios_string = talloc_asprintf(ctx, + "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s", + workstation, trusted_netbios_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->drs_trust_netbios, + realm, "E3514235-4B06-11D1-AB04-00C04FC2DCD2", + workstation, trusted_netbios_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we get krbtgt/trusted_realm back when asking for a 3 part principal */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->drs_trust_netbios, + &ctx->drs_trust_netbios_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + drs_trust_netbios_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message); +#endif + + /* Confirm if we have the referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); +#else + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server, + ctx->krbtgt_trust_realm); + torture_assert(tctx, k5ok, assertion_message); + type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_referral_creds.server); + torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message); + + /* Delete the referral ticket from the cache */ + k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_remove_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); +#endif + + four_trust_dns_string = talloc_asprintf(ctx, "four/tree/two/%s@%s", + trusted_dns_name, realm); + torture_assert_int_equal(tctx, + smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context, + &ctx->four_trust_dns, + realm, "four", "tree", "two", + trusted_dns_name, NULL), + 0, "smb_krb5_make_principal failed"); + + /* Confirm if we get an error back for a 4 part principal */ + ZERO_STRUCT(ctx->counts); + k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context, + ctx->opt_canon, + ctx->krbtgt_ccache, + ctx->four_trust_dns, + &ctx->four_trust_dns_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]", + four_trust_dns_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx), + trusted->trust_direction, + trusted->trust_type, + trusted->trust_attributes, + ctx->counts.io, ctx->counts.errors, ctx->counts.ok); + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); +#ifdef USING_EMBEDDED_HEIMDAL + torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.error_io, 2, assertion_message); +#else + torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message); + torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message); +#endif + torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 7, assertion_message); + + /* Confirm if we have no referral ticket in the cache */ + krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context, + &ctx->krbtgt_referral_creds); + k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context, + ctx->krbtgt_ccache, + 0, + ctx->krbtgt_trust_realm_creds, + &ctx->krbtgt_referral_creds); + assertion_message = talloc_asprintf(ctx, + "krb5_cc_retrieve_cred(%s) for failed: (%d) %s", + krbtgt_trust_realm_string, + k5ret, + smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context, + k5ret, ctx)); + torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message); + + TALLOC_FREE(ctx); + return true; +} +#endif + +static bool check_dom_trust_pw(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *our_netbios_name, + const char *our_dns_name, + enum netr_SchannelType secure_channel_type, + const struct lsa_TrustDomainInfoInfoEx *trusted, + const char *previous_password, + const char *current_password, + uint32_t current_version, + const char *next_password, + uint32_t next_version, + bool expected_result) +{ + struct cli_credentials *incoming_creds; + char *server_name = NULL; + char *account = NULL; + char *principal = NULL; + char *workstation = NULL; + const char *binding = torture_setting_string(tctx, "binding", NULL); + const char *host = torture_setting_string(tctx, "host", NULL); + const char *ip; + struct nbt_name nbt_name; + struct dcerpc_binding *b2; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword samr_crypt_password; + struct netr_CryptPassword netr_crypt_password; + struct netr_Authenticator req_auth; + struct netr_Authenticator rep_auth; + struct netr_ServerPasswordSet2 s; + struct dcerpc_pipe *p1 = NULL; + struct dcerpc_pipe *p2 = NULL; + NTSTATUS status; + bool ok; + int rc; + const char *trusted_netbios_name = trusted->netbios_name.string; + const char *trusted_dns_name = trusted->domain_name.string; + struct tsocket_address *dest_addr; + struct cldap_socket *cldap; + struct cldap_netlogon cldap1; + + incoming_creds = cli_credentials_init(tctx); + torture_assert(tctx, incoming_creds, "cli_credentials_init"); + + cli_credentials_set_domain(incoming_creds, our_netbios_name, CRED_SPECIFIED); + cli_credentials_set_realm(incoming_creds, our_dns_name, CRED_SPECIFIED); + + if (secure_channel_type == SEC_CHAN_DNS_DOMAIN) { + account = talloc_asprintf(tctx, "%s.", trusted_dns_name); + torture_assert(tctx, account, __location__); + + principal = talloc_asprintf(tctx, "%s$@%s", + trusted_netbios_name, + cli_credentials_get_realm(incoming_creds)); + torture_assert(tctx, principal, __location__); + + workstation = talloc_asprintf(tctx, "%sUP", + trusted_netbios_name); + torture_assert(tctx, workstation, __location__); + } else { + account = talloc_asprintf(tctx, "%s$", trusted_netbios_name); + torture_assert(tctx, account, __location__); + + workstation = talloc_asprintf(tctx, "%sDOWN", + trusted_netbios_name); + torture_assert(tctx, workstation, __location__); + } + + cli_credentials_set_username(incoming_creds, account, CRED_SPECIFIED); + if (principal != NULL) { + cli_credentials_set_principal(incoming_creds, principal, + CRED_SPECIFIED); + } + cli_credentials_set_kvno(incoming_creds, current_version); + cli_credentials_set_password(incoming_creds, current_password, CRED_SPECIFIED); + cli_credentials_set_old_password(incoming_creds, previous_password, CRED_SPECIFIED); + cli_credentials_set_workstation(incoming_creds, workstation, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(incoming_creds, secure_channel_type); + + make_nbt_name_server(&nbt_name, host); + + status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx), + 0, 0, &nbt_name, tctx, &ip, tctx->ev); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx,"Failed to resolve %s: %s", + nbt_name.name, nt_errstr(status))); + + rc = tsocket_address_inet_from_strings(tctx, "ip", + ip, + lpcfg_cldap_port(tctx->lp_ctx), + &dest_addr); + torture_assert_int_equal(tctx, rc, 0, + talloc_asprintf(tctx, + "tsocket_address_inet_from_strings failed parsing %s:%d", + host, lpcfg_cldap_port(tctx->lp_ctx))); + + /* cldap_socket_init should now know about the dest. address */ + status = cldap_socket_init(tctx, NULL, dest_addr, &cldap); + torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init"); + + ZERO_STRUCT(cldap1); + cldap1.in.dest_address = NULL; + cldap1.in.dest_port = 0; + cldap1.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; + cldap1.in.user = account; + if (secure_channel_type == SEC_CHAN_DNS_DOMAIN) { + cldap1.in.acct_control = ACB_AUTOLOCK; + } else { + cldap1.in.acct_control = ACB_DOMTRUST; + } + status = cldap_netlogon(cldap, tctx, &cldap1); + torture_assert_ntstatus_ok(tctx, status, "cldap_netlogon"); + torture_assert_int_equal(tctx, cldap1.out.netlogon.ntver, + NETLOGON_NT_VERSION_5EX, + "ntver"); + torture_assert_int_equal(tctx, cldap1.out.netlogon.data.nt5_ex.nt_version, + NETLOGON_NT_VERSION_1 | NETLOGON_NT_VERSION_5EX, + "nt_version"); + torture_assert_int_equal(tctx, cldap1.out.netlogon.data.nt5_ex.command, + LOGON_SAM_LOGON_RESPONSE_EX, + "command"); + torture_assert_str_equal(tctx, cldap1.out.netlogon.data.nt5_ex.user_name, + cldap1.in.user, + "user_name"); + server_name = talloc_asprintf(tctx, "\\\\%s", + cldap1.out.netlogon.data.nt5_ex.pdc_dns_name); + torture_assert(tctx, server_name, __location__); + + status = dcerpc_parse_binding(tctx, binding, &b2); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + status = dcerpc_pipe_connect_b(tctx, &p1, b2, + &ndr_table_netlogon, + cli_credentials_init_anon(tctx), + tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b"); + + ok = check_pw_with_ServerAuthenticate3(p1, tctx, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + server_name, + incoming_creds, &creds); + torture_assert_int_equal(tctx, ok, expected_result, + "check_pw_with_ServerAuthenticate3"); + if (expected_result == true) { + ok = test_SetupCredentialsPipe(p1, tctx, incoming_creds, creds, + DCERPC_SIGN | DCERPC_SEAL, &p2); + torture_assert_int_equal(tctx, ok, true, + "test_SetupCredentialsPipe"); + } + TALLOC_FREE(p1); + + if (trusted->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) { +#ifdef SAMBA4_USES_HEIMDAL + ok = check_pw_with_krb5(tctx, incoming_creds, trusted); + torture_assert_int_equal(tctx, ok, expected_result, + "check_pw_with_krb5"); +#else + torture_comment(tctx, "skipping check_pw_with_krb5 for MIT Kerberos build"); +#endif + } + + if (expected_result != true || next_password == NULL) { + TALLOC_FREE(p2); + return true; + } + + /* + * netr_ServerPasswordSet2 + */ + ok = encode_pw_buffer(samr_crypt_password.data, + next_password, STR_UNICODE); + torture_assert(tctx, ok, "encode_pw_buffer"); + + if (next_version != 0) { + struct NL_PASSWORD_VERSION version; + uint32_t len = IVAL(samr_crypt_password.data, 512); + uint32_t ofs = 512 - len; + uint8_t *ptr; + + ofs -= 12; + + version.ReservedField = 0; + version.PasswordVersionNumber = next_version; + version.PasswordVersionPresent = + NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT; + + ptr = samr_crypt_password.data + ofs; + SIVAL(ptr, 0, version.ReservedField); + SIVAL(ptr, 4, version.PasswordVersionNumber); + SIVAL(ptr, 8, version.PasswordVersionPresent); + } + + netlogon_creds_client_authenticator(creds, &req_auth); + ZERO_STRUCT(rep_auth); + + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, + samr_crypt_password.data, + 516); + } else { + netlogon_creds_arcfour_crypt(creds, + samr_crypt_password.data, + 516); + } + + memcpy(netr_crypt_password.data, + samr_crypt_password.data, 512); + netr_crypt_password.length = IVAL(samr_crypt_password.data, 512); + + + s.in.server_name = server_name; + s.in.account_name = cli_credentials_get_username(incoming_creds); + s.in.secure_channel_type = cli_credentials_get_secure_channel_type(incoming_creds); + s.in.computer_name = cli_credentials_get_workstation(incoming_creds); + s.in.credential = &req_auth; + s.in.new_password = &netr_crypt_password; + s.out.return_authenticator = &rep_auth; + status = dcerpc_netr_ServerPasswordSet2_r(p2->binding_handle, tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "failed to set password"); + + ok = netlogon_creds_client_check(creds, &rep_auth.cred); + torture_assert(tctx, ok, "netlogon_creds_client_check"); + + cli_credentials_set_kvno(incoming_creds, next_version); + cli_credentials_set_password(incoming_creds, next_password, CRED_SPECIFIED); + cli_credentials_set_old_password(incoming_creds, current_password, CRED_SPECIFIED); + + TALLOC_FREE(p2); + status = dcerpc_pipe_connect_b(tctx, &p2, b2, + &ndr_table_netlogon, + cli_credentials_init_anon(tctx), + tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b"); + + ok = check_pw_with_ServerAuthenticate3(p2, tctx, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + server_name, + incoming_creds, &creds); + torture_assert(tctx, ok, "check_pw_with_ServerAuthenticate3 with changed password"); + + if (trusted->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) { +#if SAMBA4_USES_HEIMDAL + ok = check_pw_with_krb5(tctx, incoming_creds, trusted); + torture_assert(tctx, ok, "check_pw_with_krb5 with changed password"); +#else + torture_comment(tctx, "skipping check_pw_with_krb5 for MIT Kerberos build"); +#endif + } + + TALLOC_FREE(p2); + return true; +} + +static bool test_CreateTrustedDomainEx_common(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t num_trusts, + bool ex2_call) +{ + NTSTATUS status; + bool ret = true; + struct lsa_QueryInfoPolicy2 p2; + union lsa_PolicyInformation *our_info = NULL; + struct lsa_CreateTrustedDomainEx r; + struct lsa_CreateTrustedDomainEx2 r2; + struct lsa_TrustDomainInfoInfoEx trustinfo; + struct lsa_TrustDomainInfoAuthInfoInternal *authinfo_internal = NULL; + struct lsa_TrustDomainInfoAuthInfo *authinfo = NULL; + struct dom_sid **domsid; + struct policy_handle *trustdom_handle; + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info = NULL; + DATA_BLOB session_key; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *id; + const char *incoming_v00 = TRUSTPW "InV00"; + const char *incoming_v0 = TRUSTPW "InV0"; + const char *incoming_v1 = TRUSTPW "InV1"; + const char *incoming_v2 = TRUSTPW "InV2"; + const char *incoming_v40 = TRUSTPW "InV40"; + const char *outgoing_v00 = TRUSTPW "OutV00"; + const char *outgoing_v0 = TRUSTPW "OutV0"; + + if (ex2_call) { + torture_comment(tctx, "\nTesting CreateTrustedDomainEx2 for %d domains\n", num_trusts); + id = "3"; + } else { + torture_comment(tctx, "\nTesting CreateTrustedDomainEx for %d domains\n", num_trusts); + id = "2"; + } + + domsid = talloc_array(tctx, struct dom_sid *, num_trusts); + trustdom_handle = talloc_array(tctx, struct policy_handle, num_trusts); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n", nt_errstr(status)); + return false; + } + + ZERO_STRUCT(p2); + p2.in.handle = handle; + p2.in.level = LSA_POLICY_INFO_DNS; + p2.out.info = &our_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_QueryInfoPolicy2_r(b, tctx, &p2), + "lsa_QueryInfoPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, p2.out.result, + "lsa_QueryInfoPolicy2 failed"); + torture_assert(tctx, our_info != NULL, "lsa_QueryInfoPolicy2 our_info"); + + for (i=0; i< num_trusts; i++) { + char *trust_name = talloc_asprintf(tctx, "TORTURE%s%02d", id, i); + char *trust_name_dns = talloc_asprintf(tctx, "torturedom%s%02d.samba._none_.example.com", id, i); + char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-%s%02d", id, i); + bool ok; + + domsid[i] = dom_sid_parse_talloc(tctx, trust_sid); + + trustinfo.sid = domsid[i]; + trustinfo.netbios_name.string = trust_name; + trustinfo.domain_name.string = trust_name_dns; + + /* Create inbound, some outbound, and some + * bi-directional trusts in a repeating pattern based + * on i */ + + /* 1 == inbound, 2 == outbound, 3 == both */ + trustinfo.trust_direction = (i % 3) + 1; + + /* Try different trust types too */ + + /* 1 == downlevel (NT4), 2 == uplevel (ADS), 3 == MIT (kerberos but not AD) */ + trustinfo.trust_type = (((i / 3) + 1) % 3) + 1; + + trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION; + + ok = gen_authinfo_internal(tctx, incoming_v00, incoming_v0, + outgoing_v00, outgoing_v0, + session_key, &authinfo_internal); + if (!ok) { + torture_comment(tctx, "gen_authinfo_internal failed"); + ret = false; + } + + ok = gen_authinfo(tctx, incoming_v00, incoming_v0, + outgoing_v00, outgoing_v0, + &authinfo); + if (!ok) { + torture_comment(tctx, "gen_authinfonfo failed"); + ret = false; + } + + if (ex2_call) { + + r2.in.policy_handle = handle; + r2.in.info = &trustinfo; + r2.in.auth_info_internal = authinfo_internal; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.out.trustdom_handle = &trustdom_handle[i]; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_CreateTrustedDomainEx2_r(b, tctx, &r2), + "CreateTrustedDomainEx2 failed"); + + status = r2.out.result; + } else { + + r.in.policy_handle = handle; + r.in.info = &trustinfo; + r.in.auth_info = authinfo; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.trustdom_handle = &trustdom_handle[i]; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_CreateTrustedDomainEx_r(b, tctx, &r), + "CreateTrustedDomainEx failed"); + + status = r.out.result; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + test_DeleteTrustedDomain(b, tctx, handle, trustinfo.netbios_name); + if (ex2_call) { + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_CreateTrustedDomainEx2_r(b, tctx, &r2), + "CreateTrustedDomainEx2 failed"); + status = r2.out.result; + } else { + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_CreateTrustedDomainEx_r(b, tctx, &r), + "CreateTrustedDomainEx2 failed"); + status = r.out.result; + } + } + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "CreateTrustedDomainEx failed2 - %s\n", nt_errstr(status)); + ret = false; + } else { + /* For outbound and MIT trusts there is no trust account */ + if (trustinfo.trust_direction != 2 && + trustinfo.trust_type != 3) { + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_comment(tctx, "skipping trusted domain auth tests against samba3\n"); + } else if (ex2_call == false && + torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping CreateTrustedDomainEx trusted domain auth tests against samba4\n"); + + } else { + ok = check_dom_trust_pw(p, tctx, + our_info->dns.name.string, + our_info->dns.dns_domain.string, + SEC_CHAN_DOMAIN, + &trustinfo, + NULL, + "x" TRUSTPW "x", 0, + NULL, 0, + false); + if (!ok) { + torture_comment(tctx, "Password check passed unexpectedly\n"); + ret = false; + } + ok = check_dom_trust_pw(p, tctx, + our_info->dns.name.string, + our_info->dns.dns_domain.string, + SEC_CHAN_DOMAIN, + &trustinfo, + incoming_v00, + incoming_v0, 0, + incoming_v1, 1, + true); + if (!ok) { + torture_comment(tctx, "Password check failed (SEC_CHAN_DOMAIN)\n"); + ret = false; + } + ok = check_dom_trust_pw(p, tctx, + our_info->dns.name.string, + our_info->dns.dns_domain.string, + SEC_CHAN_DNS_DOMAIN, + &trustinfo, + incoming_v0, + incoming_v1, 1, + incoming_v2, 2, + true); + if (!ok) { + torture_comment(tctx, "Password check failed v2 (SEC_CHAN_DNS_DOMAIN)\n"); + ret = false; + } + ok = check_dom_trust_pw(p, tctx, + our_info->dns.name.string, + our_info->dns.dns_domain.string, + SEC_CHAN_DNS_DOMAIN, + &trustinfo, + incoming_v1, + incoming_v2, 2, + incoming_v40, 40, + true); + if (!ok) { + torture_comment(tctx, "Password check failed v4 (SEC_CHAN_DNS_DOMAIN)\n"); + ret = false; + } + } + } + + q.in.trustdom_handle = &trustdom_handle[i]; + q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q), + "QueryTrustedDomainInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_comment(tctx, "QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(q.out.result)); + ret = false; + } else if (!q.out.info) { + torture_comment(tctx, "QueryTrustedDomainInfo level 1 failed to return an info pointer\n"); + ret = false; + } else { + if (strcmp(info->info_ex.domain_name.string, trustinfo.domain_name.string) != 0) { + torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent long name: %s != %s\n", + info->info_ex.domain_name.string, trustinfo.domain_name.string); + ret = false; + } + if (strcmp(info->info_ex.netbios_name.string, trustinfo.netbios_name.string) != 0) { + torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent short name: %s != %s\n", + info->info_ex.netbios_name.string, trustinfo.netbios_name.string); + ret = false; + } + if (info->info_ex.trust_type != trustinfo.trust_type) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n", + trust_name, info->info_ex.trust_type, trustinfo.trust_type); + ret = false; + } + if (info->info_ex.trust_attributes != LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n", + trust_name, info->info_ex.trust_attributes, LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION); + ret = false; + } + if (info->info_ex.trust_direction != trustinfo.trust_direction) { + torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n", + trust_name, info->info_ex.trust_direction, trustinfo.trust_direction); + ret = false; + } + } + } + } + + /* now that we have some domains to look over, we can test the enum calls */ + if (!test_EnumTrustDom(b, tctx, handle)) { + torture_comment(tctx, "test_EnumTrustDom failed\n"); + ret = false; + } + + if (!test_EnumTrustDomEx(b, tctx, handle)) { + torture_comment(tctx, "test_EnumTrustDomEx failed\n"); + ret = false; + } + + for (i=0; idns.name.string; + tnames.names[0].sid_type = SID_NAME_DOMAIN; + tnames.names[1].name.string = info->dns.dns_domain.string; + tnames.names[1].sid_type = SID_NAME_DOMAIN; + tnames.names[2].name.string = talloc_asprintf(tctx, "%s\\", info->dns.name.string); + tnames.names[2].sid_type = SID_NAME_DOMAIN; + tnames.names[3].name.string = talloc_asprintf(tctx, "%s\\", info->dns.dns_domain.string); + tnames.names[3].sid_type = SID_NAME_DOMAIN; + tnames.names[4].name.string = talloc_asprintf(tctx, "%s\\guest", info->dns.name.string); + tnames.names[4].sid_type = SID_NAME_USER; + tnames.names[5].name.string = talloc_asprintf(tctx, "%s\\krbtgt", info->dns.name.string); + tnames.names[5].sid_type = SID_NAME_USER; + tnames.names[6].name.string = talloc_asprintf(tctx, "%s\\guest", info->dns.dns_domain.string); + tnames.names[6].sid_type = SID_NAME_USER; + tnames.names[7].name.string = talloc_asprintf(tctx, "%s\\krbtgt", info->dns.dns_domain.string); + tnames.names[7].sid_type = SID_NAME_USER; + tnames.names[8].name.string = talloc_asprintf(tctx, "krbtgt@%s", info->dns.name.string); + tnames.names[8].sid_type = SID_NAME_USER; + tnames.names[9].name.string = talloc_asprintf(tctx, "krbtgt@%s", info->dns.dns_domain.string); + tnames.names[9].sid_type = SID_NAME_USER; + tnames.names[10].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.name.string); + tnames.names[10].sid_type = SID_NAME_USER; + tnames.names[11].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.dns_domain.string); + tnames.names[11].sid_type = SID_NAME_USER; + tnames.names[12].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", info->dns.name.string); + tnames.names[12].sid_type = SID_NAME_USER; + tnames.names[13].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", info->dns.dns_domain.string); + tnames.names[13].sid_type = SID_NAME_USER; + ret &= test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames); + + /* Try to use in-forest search for the test machine */ + dnames.count = 1; + dnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, dnames.count); + dnames.names[0].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.name.string); + dnames.names[0].sid_type = SID_NAME_USER; + ret &= test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2, &dnames); + } + } + + return ret; +} + +static bool test_QueryInfoPolicy(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + return test_QueryInfoPolicyCalls(false, b, tctx, handle); +} + +static bool test_QueryInfoPolicy2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + return test_QueryInfoPolicyCalls(true, b, tctx, handle); +} + +static bool test_GetUserName(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct lsa_GetUserName r; + struct lsa_String *authority_name_p = NULL; + struct lsa_String *account_name_p = NULL; + + torture_comment(tctx, "\nTesting GetUserName\n"); + + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = NULL; + r.out.account_name = &account_name_p; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r), + "GetUserName failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "GetUserName result failed"); + torture_assert_not_null(tctx, r.out.account_name, "r.out.account_name"); + torture_assert_not_null(tctx, *r.out.account_name, "*r.out.account_name"); + torture_assert(tctx, r.out.authority_name == NULL, "r.out.authority_name"); + + account_name_p = NULL; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r), + "GetUserName failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "GetUserName result failed"); + torture_assert_not_null(tctx, r.out.account_name, "r.out.account_name"); + torture_assert_not_null(tctx, *r.out.account_name, "*r.out.account_name"); + torture_assert_not_null(tctx, r.out.authority_name, "r.out.authority_name"); + torture_assert_not_null(tctx, *r.out.authority_name, "*r.out.authority_name"); + + torture_comment(tctx, + "Account Name: %s, Authority Name: %s\n", + (*r.out.account_name)->string, + (*r.out.authority_name)->string); + + return true; +} + +static bool test_GetUserName_fail(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct lsa_GetUserName r; + struct lsa_String *account_name_p = NULL; + NTSTATUS status; + + torture_comment(tctx, "\nTesting GetUserName_fail\n"); + + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = NULL; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, + "GetUserName correctly returned with " + "status: %s\n", + nt_errstr(status)); + return true; + } + + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_ACCESS_DENIED, + "GetUserName return value should " + "be ACCESS_DENIED"); + return true; + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) { + torture_comment(tctx, + "GetUserName correctly returned with " + "result: %s\n", + nt_errstr(r.out.result)); + return true; + } + } + + torture_assert_ntstatus_equal(tctx, + r.out.result, + NT_STATUS_OK, + "GetUserName return value should be " + "ACCESS_DENIED"); + + return false; +} + +bool test_lsa_Close(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct lsa_Close r; + struct policy_handle handle2; + + torture_comment(tctx, "\nTesting Close\n"); + + r.in.handle = handle; + r.out.handle = &handle2; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Close failed"); + + torture_assert_ntstatus_equal(tctx, dcerpc_lsa_Close_r(b, tctx, &r), + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, "Close should failed"); + + torture_comment(tctx, "\n"); + + return true; +} + +bool torture_rpc_lsa(struct torture_context *tctx) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle *handle = NULL; + struct test_join *join = NULL; + struct cli_credentials *machine_creds; + struct dcerpc_binding_handle *b; + enum dcerpc_transport_t transport; + + status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + b = p->binding_handle; + transport = dcerpc_binding_get_transport(p->binding); + + /* Test lsaLookupSids3 and lsaLookupNames4 over tcpip */ + if (transport == NCACN_IP_TCP) { + if (!test_OpenPolicy_fail(b, tctx)) { + ret = false; + } + + if (!test_OpenPolicy2_fail(b, tctx)) { + ret = false; + } + + if (!test_OpenPolicy3_fail(b, tctx)) { + ret = false; + } + + if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + return ret; + } + + if (!test_OpenPolicy(b, tctx)) { + ret = false; + } + + if (!test_lsa_OpenPolicy2(b, tctx, &handle)) { + ret = false; + } + + if (!test_lsa_OpenPolicy3(b, tctx, &handle)) { + ret = false; + } + + if (handle) { + join = torture_join_domain(tctx, TEST_MACHINENAME, ACB_WSTRUST, &machine_creds); + if (!join) { + ret = false; + } + + if (!test_LookupSids_async(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + if (!test_QueryDomainInfoPolicy(b, tctx, handle)) { + ret = false; + } + + if (!test_CreateSecret(p, tctx, handle)) { + ret = false; + } + + if (!test_QueryInfoPolicy(b, tctx, handle)) { + ret = false; + } + + if (!test_QueryInfoPolicy2(b, tctx, handle)) { + ret = false; + } + + if (!test_Delete(b, tctx, handle)) { + ret = false; + } + + if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + if (!test_lsa_Close(b, tctx, handle)) { + ret = false; + } + + torture_leave_domain(tctx, join); + + } else { + if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + } + + if (!test_GetUserName(b, tctx)) { + ret = false; + } + + return ret; +} + +bool torture_rpc_lsa_get_user(struct torture_context *tctx) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct dcerpc_binding_handle *b; + enum dcerpc_transport_t transport; + + status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + b = p->binding_handle; + transport = dcerpc_binding_get_transport(p->binding); + + if (transport == NCACN_IP_TCP) { + if (!test_GetUserName_fail(b, tctx)) { + ret = false; + } + return ret; + } + + if (!test_GetUserName(b, tctx)) { + ret = false; + } + + return ret; +} + +static bool testcase_LookupNames(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + bool ret = true; + struct policy_handle *handle; + struct lsa_TransNameArray tnames; + struct lsa_TransNameArray2 tnames2; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding); + + if (transport != NCACN_NP && transport != NCALRPC) { + torture_comment(tctx, "testcase_LookupNames is only available " + "over NCACN_NP or NCALRPC"); + return true; + } + + if (!test_OpenPolicy(b, tctx)) { + ret = false; + } + + if (!test_lsa_OpenPolicy2(b, tctx, &handle)) { + ret = false; + } + + if (!handle) { + ret = false; + } + + tnames.count = 1; + tnames.names = talloc_array(tctx, struct lsa_TranslatedName, tnames.count); + ZERO_STRUCT(tnames.names[0]); + tnames.names[0].name.string = "BUILTIN"; + tnames.names[0].sid_type = SID_NAME_DOMAIN; + + if (!test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames)) { + ret = false; + } + + tnames2.count = 1; + tnames2.names = talloc_array(tctx, struct lsa_TranslatedName2, tnames2.count); + ZERO_STRUCT(tnames2.names[0]); + tnames2.names[0].name.string = "BUILTIN"; + tnames2.names[0].sid_type = SID_NAME_DOMAIN; + + if (!test_LookupNames2(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames2, true)) { + ret = false; + } + + if (!test_LookupNames3(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames2, true)) { + ret = false; + } + + if (!test_LookupNames_wellknown(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + if (!test_LookupNames_NULL(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + if (!test_LookupNames_bogus(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) { + ret = false; + } + + if (!test_lsa_Close(b, tctx, handle)) { + ret = false; + } + + return ret; +} + +struct torture_suite *torture_rpc_lsa_lookup_names(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "lsa.lookupnames"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa", + &ndr_table_lsarpc); + torture_rpc_tcase_add_test(tcase, "LookupNames", + testcase_LookupNames); + + return suite; +} + +struct lsa_trustdom_state { + uint32_t num_trusts; +}; + +static bool testcase_TrustedDomains(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + bool ret = true; + struct policy_handle *handle; + struct lsa_trustdom_state *state = + talloc_get_type_abort(data, struct lsa_trustdom_state); + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding); + + if (transport != NCACN_NP && transport != NCALRPC) { + torture_comment(tctx, "testcase_TrustedDomains is only available " + "over NCACN_NP or NCALRPC"); + return true; + } + + torture_comment(tctx, "Testing %d domains\n", state->num_trusts); + + if (!test_OpenPolicy(b, tctx)) { + ret = false; + } + + if (!test_lsa_OpenPolicy2(b, tctx, &handle)) { + ret = false; + } + + if (!handle) { + ret = false; + } + + if (!test_CreateTrustedDomain(b, tctx, handle, state->num_trusts)) { + ret = false; + } + + if (!test_CreateTrustedDomainEx(p, tctx, handle, state->num_trusts)) { + ret = false; + } + + if (!test_CreateTrustedDomainEx2(p, tctx, handle, state->num_trusts)) { + ret = false; + } + + if (!test_lsa_Close(b, tctx, handle)) { + ret = false; + } + + return ret; +} + +struct torture_suite *torture_rpc_lsa_trusted_domains(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + struct lsa_trustdom_state *state; + + state = talloc(mem_ctx, struct lsa_trustdom_state); + + state->num_trusts = 12; + + suite = torture_suite_create(mem_ctx, "lsa.trusted.domains"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa", + &ndr_table_lsarpc); + torture_rpc_tcase_add_test_ex(tcase, "TrustedDomains", + testcase_TrustedDomains, + state); + + return suite; +} + +static bool testcase_Privileges(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle *handle; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding); + + if (transport != NCACN_NP && transport != NCALRPC) { + torture_skip(tctx, "testcase_Privileges is only available " + "over NCACN_NP or NCALRPC"); + } + + if (!test_OpenPolicy(b, tctx)) { + return false; + } + + if (!test_lsa_OpenPolicy2(b, tctx, &handle)) { + return false; + } + + if (!handle) { + return false; + } + + if (!test_CreateAccount(b, tctx, handle)) { + return false; + } + + if (!test_EnumAccounts(b, tctx, handle)) { + return false; + } + + if (!test_EnumPrivs(b, tctx, handle)) { + return false; + } + + if (!test_lsa_Close(b, tctx, handle)) { + return false; + } + + return true; +} + + +struct torture_suite *torture_rpc_lsa_privileges(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "lsa.privileges"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa", + &ndr_table_lsarpc); + torture_rpc_tcase_add_test(tcase, "Privileges", + testcase_Privileges); + + return suite; +} diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c new file mode 100644 index 0000000..f641827 --- /dev/null +++ b/source4/torture/rpc/lsa_lookup.c @@ -0,0 +1,428 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc lookup operations + + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "libcli/security/security.h" + +static bool open_policy(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle **handle) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + + *handle = talloc(tctx, struct policy_handle); + if (!*handle) { + return false; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = *handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b, tctx, &r), + "OpenPolicy2 failed"); + + return NT_STATUS_IS_OK(r.out.result); +} + +static bool get_domainsid(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct dom_sid **sid) +{ + struct lsa_QueryInfoPolicy r; + union lsa_PolicyInformation *info = NULL; + + r.in.level = LSA_POLICY_INFO_DOMAIN; + r.in.handle = handle; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryInfoPolicy_r(b, tctx, &r), + "QueryInfoPolicy failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "QueryInfoPolicy failed"); + + *sid = info->domain.sid; + return true; +} + +static NTSTATUS lookup_sids(struct torture_context *tctx, + uint16_t level, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct dom_sid **sids, uint32_t num_sids, + struct lsa_TransNameArray *names) +{ + struct lsa_LookupSids r; + struct lsa_SidArray sidarray; + struct lsa_RefDomainList *domains; + uint32_t count = 0; + uint32_t i; + NTSTATUS status; + + names->count = 0; + names->names = NULL; + + sidarray.num_sids = num_sids; + sidarray.sids = talloc_array(tctx, struct lsa_SidPtr, num_sids); + + for (i=0; iinfo_ex.trust_direction & 2) && + (info->info_ex.trust_type == 1)) { + *sid = domains.domains[i].sid; + return true; + } + } + + torture_fail(tctx, "I need a AD DC with an outgoing trust to NT4"); +} + +#define NUM_SIDS 8 + +bool torture_rpc_lsa_lookup(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle *handle; + struct dom_sid *dom_sid = NULL; + struct dom_sid *trusted_sid = NULL; + struct dom_sid *sids[NUM_SIDS]; + struct dcerpc_binding_handle *b; + enum dcerpc_transport_t transport; + + status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(torture, "unable to connect to table"); + } + b = p->binding_handle; + transport = dcerpc_binding_get_transport(p->binding); + + if (transport != NCACN_NP && transport != NCALRPC) { + torture_comment(torture, + "torture_rpc_lsa_lookup is only available " + "over NCACN_NP or NCALRPC"); + return true; + } + + ret &= open_policy(torture, b, &handle); + if (!ret) return false; + + ret &= get_domainsid(torture, b, handle, &dom_sid); + if (!ret) return false; + + ret &= get_downleveltrust(torture, b, handle, &trusted_sid); + if (!ret) return false; + + torture_comment(torture, "domain sid: %s\n", + dom_sid_string(torture, dom_sid)); + + sids[0] = dom_sid_parse_talloc(torture, "S-1-1-0"); + sids[1] = dom_sid_parse_talloc(torture, "S-1-5-4"); + sids[2] = dom_sid_parse_talloc(torture, "S-1-5-32"); + sids[3] = dom_sid_parse_talloc(torture, "S-1-5-32-545"); + sids[4] = dom_sid_dup(torture, dom_sid); + sids[5] = dom_sid_add_rid(torture, dom_sid, 512); + sids[6] = dom_sid_dup(torture, trusted_sid); + sids[7] = dom_sid_add_rid(torture, trusted_sid, 512); + + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 0, + NT_STATUS_INVALID_PARAMETER, NULL); + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_WKN_GRP, SID_NAME_WKN_GRP, SID_NAME_DOMAIN, + SID_NAME_ALIAS, SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP }; + + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 1, + NT_STATUS_OK, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP }; + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 2, + STATUS_SOME_UNMAPPED, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 3, + STATUS_SOME_UNMAPPED, types); + } + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 4, + STATUS_SOME_UNMAPPED, types); + } + + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 5, + NT_STATUS_NONE_MAPPED, NULL); + + { + enum lsa_SidType types[NUM_SIDS] = + { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN, + SID_NAME_DOMAIN, SID_NAME_DOM_GRP, + SID_NAME_UNKNOWN, SID_NAME_UNKNOWN }; + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 6, + STATUS_SOME_UNMAPPED, types); + } + + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 7, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 8, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 9, + NT_STATUS_INVALID_PARAMETER, NULL); + ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 10, + NT_STATUS_INVALID_PARAMETER, NULL); + + return ret; +} + +static bool test_LookupSidsReply(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct policy_handle *handle = NULL; + + struct dom_sid **sids = NULL; + uint32_t num_sids = 1; + + struct lsa_LookupSids r; + struct lsa_SidArray sidarray; + struct lsa_RefDomainList *domains = NULL; + struct lsa_TransNameArray names; + uint32_t count = 0; + + uint32_t i; + const char *dom_sid = "S-1-5-21-1111111111-2222222222-3333333333"; + const char *dom_admin_sid; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding); + + ZERO_STRUCT(r); + ZERO_STRUCT(sidarray); + ZERO_STRUCT(names); + + if (transport != NCACN_NP && transport != NCALRPC) { + torture_comment(tctx, + "test_LookupSidsReply is only available " + "over NCACN_NP or NCALRPC"); + return true; + } + + if (!open_policy(tctx, b, &handle)) { + return false; + } + + dom_admin_sid = talloc_asprintf(tctx, "%s-%d", dom_sid, 512); + + sids = talloc_zero_array(tctx, struct dom_sid *, num_sids); + + sids[0] = dom_sid_parse_talloc(tctx, dom_admin_sid); + + names.count = 0; + names.names = NULL; + + sidarray.num_sids = num_sids; + sidarray.sids = talloc_zero_array(tctx, struct lsa_SidPtr, num_sids); + + for (i=0; icount, num_sids, + "unexpected domains count"); + torture_assert(tctx, domains->domains, + "unexpected domains pointer"); + torture_assert_str_equal(tctx, dom_sid_string(tctx, domains->domains[0].sid), dom_sid, + "unexpected domain sid"); +#endif + + return true; +} + +/* check for lookup sids results */ +struct torture_suite *torture_rpc_lsa_lookup_sids(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "lsa.lookupsids"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa", + &ndr_table_lsarpc); + + torture_rpc_tcase_add_test(tcase, "LookupSidsReply", test_LookupSidsReply); + + return suite; +} diff --git a/source4/torture/rpc/mdssvc.c b/source4/torture/rpc/mdssvc.c new file mode 100644 index 0000000..2096ed7 --- /dev/null +++ b/source4/torture/rpc/mdssvc.c @@ -0,0 +1,1056 @@ +/* + Unix SMB/CIFS implementation. + test suite for the mdssvc RPC service + + Copyright (C) Ralph Boehme 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_mdssvc_c.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" +#include "rpc_server/mdssvc/dalloc.h" +#include "rpc_server/mdssvc/marshalling.h" + +struct torture_mdsscv_state { + struct dcerpc_pipe *p; + struct policy_handle ph; + + /* Known fields used across multiple commands */ + uint32_t dev; + uint32_t flags; + + /* cmd specific or unknown fields */ + struct { + const char share_path[1025]; + uint32_t unkn2; + uint32_t unkn3; + } mdscmd_open; + struct { + uint32_t status; + uint32_t unkn7; + } mdscmd_unknown1; + struct { + uint32_t fragment; + uint32_t unkn9; + } mdscmd_cmd; + struct { + uint32_t status; + } mdscmd_close; +}; + +static bool torture_rpc_mdssvc_setup(struct torture_context *tctx, + void **data) +{ + struct torture_mdsscv_state *state = NULL; + NTSTATUS status; + + state = talloc_zero(tctx, struct torture_mdsscv_state); + if (state == NULL) { + return false; + } + *data = state; + + status = torture_rpc_connection(tctx, &state->p, &ndr_table_mdssvc); + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return true; +} + +static bool torture_rpc_mdssvc_teardown(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + + TALLOC_FREE(state->p); + TALLOC_FREE(state); + return true; +} + +static bool torture_rpc_mdssvc_open(struct torture_context *tctx, + void **data) +{ + struct torture_mdsscv_state *state = NULL; + struct dcerpc_binding_handle *b = NULL; + const char *share_name = NULL; + const char *share_mount_path = NULL; + NTSTATUS status; + bool ok = true; + + state = talloc_zero(tctx, struct torture_mdsscv_state); + if (state == NULL) { + return false; + } + *data = state; + + status = torture_rpc_connection(tctx, &state->p, &ndr_table_mdssvc); + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + b = state->p->binding_handle; + + share_name = torture_setting_string( + tctx, "spotlight_share", "spotlight"); + share_mount_path = torture_setting_string( + tctx, "share_mount_path", "/foo/bar"); + + state->dev = generate_random(); + state->mdscmd_open.unkn2 = 23; + state->mdscmd_open.unkn3 = 0; + + ZERO_STRUCT(state->ph); + + status = dcerpc_mdssvc_open(b, + state, + &state->dev, + &state->mdscmd_open.unkn2, + &state->mdscmd_open.unkn3, + share_mount_path, + share_name, + state->mdscmd_open.share_path, + &state->ph); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_open failed\n"); + + status = dcerpc_mdssvc_unknown1(b, + state, + &state->ph, + 0, + state->dev, + state->mdscmd_open.unkn2, + 0, + geteuid(), + getegid(), + &state->mdscmd_unknown1.status, + &state->flags, + &state->mdscmd_unknown1.unkn7); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + +done: + if (!ok) { + (void)dcerpc_mdssvc_close(b, + state, + &state->ph, + 0, + state->dev, + state->mdscmd_open.unkn2, + 0, + &state->ph, + &state->mdscmd_close.status); + ZERO_STRUCTP(state); + } + return ok; +} + +static bool torture_rpc_mdssvc_close(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + NTSTATUS status; + bool ok = true; + + torture_comment(tctx, "test_teardown_mdssvc_disconnect\n"); + + if (state->p == NULL) { + /* We have already been disconnected. */ + goto done; + } + + status = dcerpc_mdssvc_close(state->p->binding_handle, + state, + &state->ph, + 0, + state->dev, + state->mdscmd_open.unkn2, + 0, + &state->ph, + &state->mdscmd_close.status); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_close failed\n"); + + ZERO_STRUCTP(state); + +done: + return ok; +} + +/* + * Test unknown share name + */ +static bool test_mdssvc_open_unknown_share(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + struct policy_handle nullh; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn3; + uint32_t device_id_out; + uint32_t unkn2_out; + uint32_t unkn3_out; + const char *share_mount_path = NULL; + const char *share_name = NULL; + const char share_path[1025] = "X"; + NTSTATUS status; + bool ok = true; + + share_name = torture_setting_string( + tctx, "unknown_share", "choukawoohoo"); + share_mount_path = torture_setting_string( + tctx, "share_mount_path", "/foo/bar"); + + device_id_out = device_id = generate_random(); + unkn2_out = unkn2 = generate_random(); + unkn3_out = unkn3 = generate_random(); + + ZERO_STRUCT(ph); + ZERO_STRUCT(nullh); + + status = dcerpc_mdssvc_open(b, + tctx, + &device_id_out, + &unkn2_out, + &unkn3_out, + share_mount_path, + share_name, + share_path, + &ph); + + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_open failed\n"); + + torture_assert_u32_equal_goto(tctx, device_id_out, device_id, ok, done, + "Bad device_id\n"); + + torture_assert_u32_equal_goto(tctx, unkn2_out, unkn2, ok, done, + "Bad unkn2\n"); + + torture_assert_u32_equal_goto(tctx, unkn3_out, unkn3, ok, done, + "Bad unkn3\n"); + + torture_assert_goto(tctx, share_path[0] == '\0', ok, done, + "Expected empty string as share path\n"); + + torture_assert_mem_equal_goto(tctx, &ph, &nullh, + sizeof(ph), ok, done, + "Expected all-zero policy handle\n"); + +done: + return ok; +} + +/* + * Test on a share where Spotlight is not enabled + */ +static bool test_mdssvc_open_spotlight_disabled(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + struct policy_handle nullh; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn3; + uint32_t device_id_out; + uint32_t unkn2_out; + uint32_t unkn3_out; + const char *share_mount_path = NULL; + const char *share_name = NULL; + const char share_path[1025] = ""; + NTSTATUS status; + bool ok = true; + + share_name = torture_setting_string( + tctx, "no_spotlight_share", "no_spotlight"); + share_mount_path = torture_setting_string( + tctx, "share_mount_path", "/foo/bar"); + + device_id_out = device_id = generate_random(); + unkn2_out = unkn2 = 23; + unkn3_out = unkn3 = 0; + + ZERO_STRUCT(ph); + ZERO_STRUCT(nullh); + + status = dcerpc_mdssvc_open(b, + tctx, + &device_id_out, + &unkn2_out, + &unkn3_out, + share_mount_path, + share_name, + share_path, + &ph); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_open failed\n"); + + torture_assert_u32_equal_goto(tctx, device_id, device_id_out, ok, done, + "Bad device_id\n"); + + torture_assert_u32_equal_goto(tctx, unkn2, unkn2_out, + ok, done, "Bad unkn2\n"); + + torture_assert_u32_equal_goto(tctx, unkn3, unkn3_out, + ok, done, "Bad unkn3\n"); + + torture_assert_goto(tctx, share_path[0] == '\0', ok, done, + "Expected empty string as share path\n"); + + torture_assert_mem_equal_goto(tctx, &ph, &nullh, + sizeof(ph), ok, done, + "Expected all-zero policy handle\n"); + +done: + return ok; +} + +static bool test_mdssvc_close(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + struct policy_handle close_ph; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn3; + const char *share_mount_path = NULL; + const char *share_name = NULL; + const char share_path[1025] = ""; + uint32_t close_status; + DATA_BLOB ph_blob; + DATA_BLOB close_ph_blob; + NTSTATUS status; + bool ok = true; + + share_name = torture_setting_string( + tctx, "spotlight_share", "spotlight"); + share_mount_path = torture_setting_string( + tctx, "share_mount_path", "/foo/bar"); + + device_id = generate_random(); + unkn2 = 23; + unkn3 = 0; + + ZERO_STRUCT(ph); + ZERO_STRUCT(close_ph); + + status = dcerpc_mdssvc_open(b, + tctx, + &device_id, + &unkn2, + &unkn3, + share_mount_path, + share_name, + share_path, + &ph); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_open failed\n"); + + status = dcerpc_mdssvc_close(b, + tctx, + &ph, + 0, + device_id, + unkn2, + 0, + &close_ph, + &close_status); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_open failed\n"); + + ph_blob = (DATA_BLOB) { + .data = (uint8_t *)&ph, + .length = sizeof(struct policy_handle) + }; + close_ph_blob = (DATA_BLOB) { + .data = (uint8_t *)&close_ph, + .length = sizeof(struct policy_handle), + }; + + torture_assert_data_blob_equal(tctx, close_ph_blob, ph_blob, + "bad blob"); + + torture_comment(tctx, "Test close with a all-zero handle\n"); + + ZERO_STRUCT(ph); + status = dcerpc_mdssvc_close(b, + tctx, + &ph, + 0, + device_id, + unkn2, + 0, + &close_ph, + &close_status); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_close failed\n"); + + torture_assert_data_blob_equal(tctx, close_ph_blob, ph_blob, + "bad blob"); + +done: + return ok; +} + +static bool test_mdssvc_null_ph(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle nullh; + struct policy_handle ph; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn7; + uint32_t cmd_status; + uint32_t flags; + NTSTATUS status; + bool ok = true; + + device_id = generate_random(); + unkn2 = 23; + unkn7 = 0; + cmd_status = 0; + + ZERO_STRUCT(nullh); + ZERO_STRUCT(ph); + + status = dcerpc_mdssvc_unknown1(b, + tctx, + &ph, + 0, + device_id, + unkn2, + 0, + geteuid(), + getegid(), + &cmd_status, + &flags, + &unkn7); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + + torture_assert_mem_equal_goto(tctx, &ph, &nullh, + sizeof(ph), ok, done, + "Expected all-zero policy handle\n"); + +done: + return ok; +} + +static bool test_mdssvc_invalid_ph_unknown1(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn7; + uint32_t cmd_status; + uint32_t flags; + NTSTATUS status; + bool ok = true; + + device_id = generate_random(); + unkn2 = 23; + unkn7 = 0; + cmd_status = 0; + + ZERO_STRUCT(ph); + ph.uuid = GUID_random(); + + status = dcerpc_mdssvc_unknown1(b, + tctx, + &ph, + 0, + device_id, + unkn2, + 0, + geteuid(), + getegid(), + &cmd_status, + &flags, + &unkn7); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + + /* Free and set to NULL the no-longer-usable pipe. */ + b = NULL; + TALLOC_FREE(state->p); + +done: + return ok; +} + +static bool test_mdssvc_invalid_ph_cmd(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + struct mdssvc_blob request_blob; + struct mdssvc_blob response_blob; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn9; + uint32_t fragment; + uint32_t flags; + NTSTATUS status; + bool ok = true; + + device_id = generate_random(); + unkn2 = 23; + unkn9 = 0; + fragment = 0; + flags = UINT32_C(0x6b000001); + + ZERO_STRUCT(ph); + ph.uuid = GUID_random(); + + request_blob.spotlight_blob = talloc_array(state, + uint8_t, + 0); + torture_assert_not_null_goto(tctx, request_blob.spotlight_blob, + ok, done, "dalloc_zero failed\n"); + request_blob.size = 0; + request_blob.length = 0; + request_blob.size = 0; + + status = dcerpc_mdssvc_cmd(b, + state, + &ph, + 0, + device_id, + unkn2, + 0, + flags, + request_blob, + 0, + 64 * 1024, + 1, + 64 * 1024, + 0, + 0, + &fragment, + &response_blob, + &unkn9); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + + /* Free and set to NULL the no-longer-usable pipe. */ + b = NULL; + TALLOC_FREE(state->p); + +done: + return ok; +} + +static uint8_t test_sl_unpack_loop_buf[] = { + 0x34, 0x33, 0x32, 0x31, 0x33, 0x30, 0x64, 0x6d, + 0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, 0x00, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x3a, + 0x66, 0x6f, 0x72, 0x4f, 0x49, 0x44, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x3a, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x3a, 0x00, 0x00, 0x00, 0xea, + 0x02, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0x00, + 0x0a, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x6b, 0x4d, 0x44, 0x49, 0x74, 0x65, 0x6d, 0x50, + 0x61, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x87, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xdd, 0x0a, 0x20, 0x00, 0x00, 0x6b, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static bool test_mdssvc_sl_unpack_loop(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct mdssvc_blob request_blob; + struct mdssvc_blob response_blob; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn9; + uint32_t fragment; + uint32_t flags; + NTSTATUS status; + bool ok = true; + + device_id = UINT32_C(0x2f000045); + unkn2 = 23; + unkn9 = 0; + fragment = 0; + flags = UINT32_C(0x6b000001); + + request_blob.spotlight_blob = test_sl_unpack_loop_buf; + request_blob.size = sizeof(test_sl_unpack_loop_buf); + request_blob.length = sizeof(test_sl_unpack_loop_buf); + + status = dcerpc_mdssvc_cmd(b, + state, + &state->ph, + 0, + device_id, + unkn2, + 0, + flags, + request_blob, + 0, + 64 * 1024, + 1, + 64 * 1024, + 0, + 0, + &fragment, + &response_blob, + &unkn9); + torture_assert_ntstatus_ok_goto( + tctx, status, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + +done: + return ok; +} + +static bool test_sl_dict_type_safety(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct mdssvc_blob request_blob; + struct mdssvc_blob response_blob; + uint64_t ctx1 = 0xdeadbeef; + uint64_t ctx2 = 0xcafebabe; + uint32_t device_id; + uint32_t unkn2; + uint32_t unkn9; + uint32_t fragment; + uint32_t flags; + DALLOC_CTX *d = NULL; + sl_array_t *array1 = NULL, *array2 = NULL; + sl_dict_t *arg = NULL; + int result; + NTSTATUS status; + bool ok = true; + + device_id = UINT32_C(0x2f000045); + unkn2 = 23; + unkn9 = 0; + fragment = 0; + flags = UINT32_C(0x6b000001); + + d = dalloc_new(tctx); + torture_assert_not_null_goto(tctx, d, + ok, done, "dalloc_new failed\n"); + + array1 = dalloc_zero(d, sl_array_t); + torture_assert_not_null_goto(tctx, array1, + ok, done, "dalloc_zero failed\n"); + + array2 = dalloc_zero(d, sl_array_t); + torture_assert_not_null_goto(tctx, array2, + ok, done, "dalloc_new failed\n"); + + result = dalloc_stradd(array2, "openQueryWithParams:forContext:"); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_add_copy(array2, &ctx1, uint64_t); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_add_copy(array2, &ctx2, uint64_t); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + arg = dalloc_zero(array1, sl_dict_t); + torture_assert_not_null_goto(tctx, d, + ok, done, "dalloc_zero failed\n"); + + result = dalloc_stradd(arg, "kMDQueryString"); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_stradd(arg, "*"); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_stradd(arg, "kMDScopeArray"); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_stradd(arg, "AAAABBBB"); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_stradd failed\n"); + + result = dalloc_add(array1, array2, sl_array_t); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_add failed\n"); + + result = dalloc_add(array1, arg, sl_dict_t); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_add failed\n"); + + result = dalloc_add(d, array1, sl_array_t); + torture_assert_goto(tctx, result == 0, + ok, done, "dalloc_add failed\n"); + + torture_comment(tctx, "%s", dalloc_dump(d, 0)); + + request_blob.spotlight_blob = talloc_array(tctx, + uint8_t, + 64 * 1024); + torture_assert_not_null_goto(tctx, request_blob.spotlight_blob, + ok, done, "dalloc_new failed\n"); + request_blob.size = 64 * 1024; + + status = sl_pack_alloc(tctx, d, &request_blob, 64 * 1024); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "sl_pack_alloc() failed\n"); + + status = dcerpc_mdssvc_cmd(b, + state, + &state->ph, + 0, + device_id, + unkn2, + 0, + flags, + request_blob, + 0, + 64 * 1024, + 1, + 64 * 1024, + 0, + 0, + &fragment, + &response_blob, + &unkn9); + torture_assert_ntstatus_ok_goto( + tctx, status, ok, done, + "dcerpc_mdssvc_cmd failed\n"); + +done: + return ok; +} + +static bool test_mdssvc_invalid_ph_close(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + struct policy_handle ph; + uint32_t device_id; + uint32_t unkn2; + uint32_t close_status; + NTSTATUS status; + bool ok = true; + + device_id = generate_random(); + unkn2 = 23; + close_status = 0; + + ZERO_STRUCT(ph); + ph.uuid = GUID_random(); + + status = dcerpc_mdssvc_close(b, + state, + &ph, + 0, + device_id, + unkn2, + 0, + &ph, + &close_status); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done, + "dcerpc_mdssvc_unknown1 failed\n"); + + /* Free and set to NULL the no-longer-usable pipe. */ + b = NULL; + TALLOC_FREE(state->p); + +done: + return ok; +} + +/* + * Test fetchAttributes with unknown CNID + */ +static bool test_mdssvc_fetch_attr_unknown_cnid(struct torture_context *tctx, + void *data) +{ + struct torture_mdsscv_state *state = talloc_get_type_abort( + data, struct torture_mdsscv_state); + struct dcerpc_binding_handle *b = state->p->binding_handle; + uint32_t max_fragment_size = 64 * 1024; + struct mdssvc_blob request_blob; + struct mdssvc_blob response_blob; + DALLOC_CTX *d = NULL, *mds_reply = NULL; + uint64_t *uint64var = NULL; + sl_array_t *array = NULL; + sl_array_t *cmd_array = NULL; + sl_array_t *attr_array = NULL; + sl_cnids_t *cnids = NULL; + void *path = NULL; + const char *path_type = NULL; + uint64_t ino64; + NTSTATUS status; + int ret; + bool ok = true; + + d = dalloc_new(state); + torture_assert_not_null_goto(tctx, d, ret, done, "dalloc_new failed\n"); + + array = dalloc_zero(d, sl_array_t); + torture_assert_not_null_goto(tctx, array, ret, done, + "dalloc_zero failed\n"); + + ret = dalloc_add(d, array, sl_array_t); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n"); + + cmd_array = dalloc_zero(d, sl_array_t); + torture_assert_not_null_goto(tctx, cmd_array, ret, done, + "dalloc_zero failed\n"); + + ret = dalloc_add(array, cmd_array, sl_array_t); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n"); + + ret = dalloc_stradd(cmd_array, "fetchAttributes:forOIDArray:context:"); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_stradd failed\n"); + + uint64var = talloc_zero_array(cmd_array, uint64_t, 2); + torture_assert_not_null_goto(tctx, uint64var, ret, done, + "talloc_zero_array failed\n"); + talloc_set_name(uint64var, "uint64_t *"); + + uint64var[0] = 0x500a; + uint64var[1] = 0; + + ret = dalloc_add(cmd_array, &uint64var[0], uint64_t *); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n"); + + attr_array = dalloc_zero(d, sl_array_t); + torture_assert_not_null_goto(tctx, attr_array, ret, done, + "dalloc_zero failed\n"); + + ret = dalloc_add(array, attr_array, sl_array_t); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n"); + + ret = dalloc_stradd(attr_array, "kMDItemPath"); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_stradd failed\n"); + + /* CNIDs */ + cnids = talloc_zero(array, sl_cnids_t); + torture_assert_not_null_goto(tctx, cnids, ret, done, + "talloc_zero failed\n"); + + cnids->ca_cnids = dalloc_new(cnids); + torture_assert_not_null_goto(tctx, cnids->ca_cnids, ret, done, + "dalloc_new failed\n"); + + cnids->ca_unkn1 = 0xadd; + cnids->ca_context = 0x6b000020; + + ino64 = UINT64_C(64382947389618974); + ret = dalloc_add_copy(cnids->ca_cnids, &ino64, uint64_t); + torture_assert_goto(tctx, ret == 0, ret, done, + "dalloc_add_copy failed\n"); + + ret = dalloc_add(array, cnids, sl_cnids_t); + torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n"); + + status = sl_pack_alloc(tctx, d, &request_blob, max_fragment_size); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "sl_pack_alloc() failed\n"); + + status = dcerpc_mdssvc_cmd(b, + state, + &state->ph, + 0, + state->dev, + state->mdscmd_open.unkn2, + 0, + state->flags, + request_blob, + 0, + max_fragment_size, + 1, + max_fragment_size, + 0, + 0, + &state->mdscmd_cmd.fragment, + &response_blob, + &state->mdscmd_cmd.unkn9); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "dcerpc_mdssvc_cmd failed\n"); + + mds_reply = dalloc_new(state); + torture_assert_not_null_goto(tctx, mds_reply, ret, done, + "dalloc_zero failed\n"); + + ok = sl_unpack(mds_reply, + (char *)response_blob.spotlight_blob, + response_blob.length); + torture_assert_goto(tctx, ok, ret, done, "dalloc_add failed\n"); + + torture_comment(tctx, "%s", dalloc_dump(mds_reply, 0)); + + path = dalloc_get(mds_reply, + "DALLOC_CTX", 0, + "DALLOC_CTX", 2, + "DALLOC_CTX", 0, + "sl_nil_t", 1); + torture_assert_not_null_goto(tctx, path, ret, done, + "dalloc_get path failed\n"); + + path_type = talloc_get_name(path); + + torture_assert_str_equal_goto(tctx, path_type, "sl_nil_t", ret, done, + "Wrong dalloc object type\n"); + +done: + return ok; +} + +struct torture_suite *torture_rpc_mdssvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create( + mem_ctx, "mdssvc"); + struct torture_tcase *tcase = NULL; + + tcase = torture_suite_add_tcase(suite, "rpccmd"); + if (tcase == NULL) { + return NULL; + } + torture_tcase_set_fixture(tcase, + torture_rpc_mdssvc_setup, + torture_rpc_mdssvc_teardown); + + torture_tcase_add_simple_test(tcase, + "open_unknown_share", + test_mdssvc_open_unknown_share); + + torture_tcase_add_simple_test(tcase, + "open_spotlight_disabled", + test_mdssvc_open_spotlight_disabled); + + torture_tcase_add_simple_test(tcase, + "close", + test_mdssvc_close); + + torture_tcase_add_simple_test(tcase, + "null_ph", + test_mdssvc_null_ph); + + tcase = torture_suite_add_tcase(suite, "disconnect1"); + if (tcase == NULL) { + return NULL; + } + torture_tcase_set_fixture(tcase, + torture_rpc_mdssvc_open, + torture_rpc_mdssvc_close); + + torture_tcase_add_simple_test(tcase, + "invalid_ph_unknown1", + test_mdssvc_invalid_ph_unknown1); + + tcase = torture_suite_add_tcase(suite, "disconnect2"); + if (tcase == NULL) { + return NULL; + } + torture_tcase_set_fixture(tcase, + torture_rpc_mdssvc_open, + torture_rpc_mdssvc_close); + + torture_tcase_add_simple_test(tcase, + "invalid_ph_cmd", + test_mdssvc_invalid_ph_cmd); + + tcase = torture_suite_add_tcase(suite, "disconnect3"); + if (tcase == NULL) { + return NULL; + } + torture_tcase_set_fixture(tcase, + torture_rpc_mdssvc_open, + torture_rpc_mdssvc_close); + + torture_tcase_add_simple_test(tcase, + "invalid_ph_close", + test_mdssvc_invalid_ph_close); + + tcase = torture_suite_add_tcase(suite, "mdscmd"); + if (tcase == NULL) { + return NULL; + } + torture_tcase_set_fixture(tcase, + torture_rpc_mdssvc_open, + torture_rpc_mdssvc_close); + + torture_tcase_add_simple_test(tcase, + "fetch_unknown_cnid", + test_mdssvc_fetch_attr_unknown_cnid); + + torture_tcase_add_simple_test(tcase, + "mdssvc_sl_unpack_loop", + test_mdssvc_sl_unpack_loop); + + torture_tcase_add_simple_test(tcase, + "sl_dict_type_safety", + test_sl_dict_type_safety); + + return suite; +} diff --git a/source4/torture/rpc/mgmt.c b/source4/torture/rpc/mgmt.c new file mode 100644 index 0000000..e873d4b --- /dev/null +++ b/source4/torture/rpc/mgmt.c @@ -0,0 +1,328 @@ +/* + Unix SMB/CIFS implementation. + test suite for mgmt rpc operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_mgmt_c.h" +#include "auth/gensec/gensec.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + + +/* + ask the server what interface IDs are available on this endpoint +*/ +bool test_inq_if_ids(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + bool (*per_id_test)(struct torture_context *, + const struct ndr_interface_table *iface, + TALLOC_CTX *mem_ctx, + struct ndr_syntax_id *id), + const void *priv) +{ + struct mgmt_inq_if_ids r; + struct rpc_if_id_vector_t *vector; + int i; + + vector = talloc(mem_ctx, struct rpc_if_id_vector_t); + r.out.if_id_vector = &vector; + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_inq_if_ids_r(b, mem_ctx, &r), + "inq_if_ids failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "inq_if_ids gave unexpected error code"); + + if (!vector) { + torture_comment(tctx, "inq_if_ids gave NULL if_id_vector\n"); + return false; + } + + for (i=0;icount;i++) { + struct ndr_syntax_id *id = vector->if_id[i].id; + if (!id) continue; + + torture_comment(tctx, "\tuuid %s version 0x%08x '%s'\n", + GUID_string(mem_ctx, &id->uuid), + id->if_version, + ndr_interface_name(&id->uuid, id->if_version)); + + if (per_id_test) { + per_id_test(tctx, priv, mem_ctx, id); + } + } + + return true; +} + +static bool test_inq_stats(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx) +{ + struct mgmt_inq_stats r; + struct mgmt_statistics statistics; + + r.in.max_count = MGMT_STATS_ARRAY_MAX_SIZE; + r.in.unknown = 0; + r.out.statistics = &statistics; + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_inq_stats_r(b, mem_ctx, &r), + "inq_stats failed"); + + if (statistics.count != MGMT_STATS_ARRAY_MAX_SIZE) { + torture_comment(tctx, "Unexpected array size %d\n", statistics.count); + return false; + } + + torture_comment(tctx, "\tcalls_in %6d calls_out %6d\n\tpkts_in %6d pkts_out %6d\n", + statistics.statistics[MGMT_STATS_CALLS_IN], + statistics.statistics[MGMT_STATS_CALLS_OUT], + statistics.statistics[MGMT_STATS_PKTS_IN], + statistics.statistics[MGMT_STATS_PKTS_OUT]); + + return true; +} + +static bool test_inq_princ_name_size(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t authn_proto, + const char *expected_princ_name) +{ + struct mgmt_inq_princ_name r; + uint32_t len, i; + + len = strlen(expected_princ_name); + + r.in.authn_proto = authn_proto; + + /* + * 0 gives NT_STATUS_RPC_BAD_STUB_DATA + */ + r.in.princ_name_size = 0; + + torture_assert_ntstatus_equal(tctx, + dcerpc_mgmt_inq_princ_name_r(b, tctx, &r), + NT_STATUS_RPC_BAD_STUB_DATA, + "mgmt_inq_princ_name failed"); + + for (i=1; i <= len; i++) { + r.in.princ_name_size = i; + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_inq_princ_name_r(b, tctx, &r), + "mgmt_inq_princ_name failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INSUFFICIENT_BUFFER, + "mgmt_inq_princ_name failed"); + } + + r.in.princ_name_size = len + 1; + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_inq_princ_name_r(b, tctx, &r), + "mgmt_inq_princ_name failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "mgmt_inq_princ_name failed"); + + return true; +} + +static bool test_inq_princ_name(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + struct mgmt_inq_princ_name r; + int i; + bool ret = false; + + for (i=0;i<256;i++) { + r.in.authn_proto = i; /* DCERPC_AUTH_TYPE_* */ + r.in.princ_name_size = 100; + + status = dcerpc_mgmt_inq_princ_name_r(b, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + if (W_ERROR_IS_OK(r.out.result)) { + const char *name = gensec_get_name_by_authtype(NULL, i); + ret = true; + if (name) { + torture_comment(tctx, "\tprinciple name for proto %u (%s) is '%s'\n", + i, name, r.out.princ_name); + } else { + torture_comment(tctx, "\tprinciple name for proto %u is '%s'\n", + i, r.out.princ_name); + } + + switch (i) { + case DCERPC_AUTH_TYPE_KRB5: + case DCERPC_AUTH_TYPE_NTLMSSP: + case DCERPC_AUTH_TYPE_SPNEGO: + torture_assert(tctx, + test_inq_princ_name_size(tctx, b, i, r.out.princ_name), + "failed"); + break; + case DCERPC_AUTH_TYPE_SCHANNEL: + /* + * for some reason schannel behaves differently + * + */ + default: + break; + } + } + } + + if (!ret) { + torture_comment(tctx, "\tno principle names?\n"); + } + + return true; +} + +static bool test_is_server_listening(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx) +{ + struct mgmt_is_server_listening r; + r.out.status = talloc(mem_ctx, uint32_t); + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_is_server_listening_r(b, mem_ctx, &r), + "is_server_listening failed"); + + if (*r.out.status != 0 || r.out.result == 0) { + torture_comment(tctx, "\tserver is NOT listening\n"); + } else { + torture_comment(tctx, "\tserver is listening\n"); + } + + return true; +} + +static bool test_stop_server_listening(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx) +{ + struct mgmt_stop_server_listening r; + + torture_assert_ntstatus_ok(tctx, + dcerpc_mgmt_stop_server_listening_r(b, mem_ctx, &r), + "stop_server_listening failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "\tserver refused to stop listening - %s\n", win_errstr(r.out.result)); + } else { + torture_comment(tctx, "\tserver allowed a stop_server_listening request\n"); + return false; + } + + return true; +} + + +bool torture_rpc_mgmt(struct torture_context *tctx) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *mem_ctx, *loop_ctx; + bool ret = true; + const struct ndr_interface_list *l; + struct dcerpc_binding *b; + + mem_ctx = talloc_init("torture_rpc_mgmt"); + + status = torture_rpc_binding(tctx, &b); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return false; + } + + for (l=ndr_table_list();l;l=l->next) { + struct dcerpc_binding_handle *bh; + + loop_ctx = talloc_named(mem_ctx, 0, "torture_rpc_mgmt loop context"); + + /* some interfaces are not mappable */ + if (l->table->num_calls == 0 || + strcmp(l->table->name, "mgmt") == 0) { + talloc_free(loop_ctx); + continue; + } + + torture_comment(tctx, "\nTesting pipe '%s'\n", l->table->name); + + status = dcerpc_epm_map_binding(loop_ctx, b, l->table, + tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to map port for uuid %s\n", + GUID_string(loop_ctx, &l->table->syntax_id.uuid)); + talloc_free(loop_ctx); + continue; + } + + lpcfg_set_cmdline(tctx->lp_ctx, "torture:binding", dcerpc_binding_string(loop_ctx, b)); + + status = torture_rpc_connection(tctx, &p, &ndr_table_mgmt); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_comment(tctx, "Interface not available - skipping\n"); + talloc_free(loop_ctx); + continue; + } + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + torture_comment(tctx, "Interface not available (%s) - skipping\n", nt_errstr(status)); + ret = false; + continue; + } + bh = p->binding_handle; + + if (!test_is_server_listening(tctx, bh, loop_ctx)) { + ret = false; + } + + if (!test_stop_server_listening(tctx, bh, loop_ctx)) { + ret = false; + } + + if (!test_inq_stats(tctx, bh, loop_ctx)) { + ret = false; + } + + if (!test_inq_princ_name(tctx, bh, loop_ctx)) { + ret = false; + } + + if (!test_inq_if_ids(tctx, bh, loop_ctx, NULL, NULL)) { + ret = false; + } + + } + + return ret; +} diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c new file mode 100644 index 0000000..c371561 --- /dev/null +++ b/source4/torture/rpc/netlogon.c @@ -0,0 +1,6038 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2003-2004 + Copyright (C) Tim Potter 2003 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "../lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "param/param.h" +#include "libcli/security/security.h" +#include +#include "lib/util/util_ldb.h" +#include "ldb_wrap.h" +#include "lib/replace/system/network.h" +#include "dsdb/samdb/samdb.h" + +#undef strcasecmp + +#define TEST_MACHINE_NAME "torturetest" + +static bool test_netr_broken_binding_handle(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetSiteName r; + const char *site = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.computer_name = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.out.site = &site; + + torture_comment(tctx, + "Testing netlogon request with correct binding handle: %s\n", + r.in.computer_name); + + status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "Netlogon request with broken binding handle"); + torture_assert_werr_ok(tctx, r.out.result, + "Netlogon request with broken binding handle"); + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, + "Skipping broken binding handle check against Samba"); + } + + r.in.computer_name = talloc_asprintf(tctx, "\\\\\\\\%s", + dcerpc_server_name(p)); + + torture_comment(tctx, + "Testing netlogon request with broken binding handle: %s\n", + r.in.computer_name); + + status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "Netlogon request with broken binding handle"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_COMPUTERNAME, + "Netlogon request with broken binding handle"); + + r.in.computer_name = "\\\\\\\\THIS_IS_NOT_VALID"; + + torture_comment(tctx, + "Testing netlogon request with broken binding handle: %s\n", + r.in.computer_name); + + status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "Netlogon request with broken binding handle"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_COMPUTERNAME, + "Netlogon request with broken binding handle"); + + return true; +} + +static bool test_LogonUasLogon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonUasLogon r; + struct netr_UasInfo *info = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = NULL; + r.in.account_name = cli_credentials_get_username( + samba_cmdline_get_creds()); + r.in.workstation = TEST_MACHINE_NAME; + r.out.info = &info; + + status = dcerpc_netr_LogonUasLogon_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonUasLogon"); + + return true; +} + +static bool test_LogonUasLogoff(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_LogonUasLogoff r; + struct netr_UasLogoffInfo info; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = NULL; + r.in.account_name = cli_credentials_get_username( + samba_cmdline_get_creds()); + r.in.workstation = TEST_MACHINE_NAME; + r.out.info = &info; + + status = dcerpc_netr_LogonUasLogoff_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonUasLogoff"); + + return true; +} + +bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState **creds_out) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + const struct samr_Password *mach_password; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + machine_name = cli_credentials_get_workstation(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(credentials); + a.in.computer_name = machine_name; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + 0); + torture_assert(tctx, creds != NULL, "memory allocation"); + + + torture_comment(tctx, "Testing ServerAuthenticate\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate_r(b, tctx, &a), + "ServerAuthenticate failed"); + + /* This allows the tests to continue against the more fussy windows 2008 */ + if (NT_STATUS_EQUAL(a.out.result, NT_STATUS_DOWNGRADE_DETECTED)) { + return test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + credentials, + cli_credentials_get_secure_channel_type(credentials), + creds_out); + } + + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), + "Credential chaining failed"); + + *creds_out = creds; + return true; +} + +bool test_SetupCredentials2ex(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + const char *computer_name, + enum netr_SchannelType sec_chan_type, + NTSTATUS expected_result, + struct netlogon_creds_CredentialState **creds_out) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate2 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + const struct samr_Password *mach_password; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *account_name = cli_credentials_get_username(machine_credentials); + + mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = computer_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = computer_name; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal(tctx, a.out.result, expected_result, + "ServerAuthenticate2 unexpected"); + + if (NT_STATUS_IS_OK(expected_result)) { + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), + "Credential chaining failed"); + } else { + torture_assert(tctx, !netlogon_creds_client_check(creds, &credentials3), + "Credential chaining passed unexpected"); + } + + torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); + + *creds_out = creds; + return true; +} + +bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + enum netr_SchannelType sec_chan_type, + struct netlogon_creds_CredentialState **creds_out) +{ + const char *computer_name = + cli_credentials_get_workstation(machine_credentials); + + return test_SetupCredentials2ex(p, tctx, negotiate_flags, + machine_credentials, + computer_name, + sec_chan_type, + NT_STATUS_OK, + creds_out); +} + +bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + struct netlogon_creds_CredentialState **creds_out) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b = NULL; + + if (p == NULL) { + return false; + } + + b = p->binding_handle; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); + + /* Prove that requesting a challenge again won't break it */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + *creds_out = creds; + return true; +} + +bool test_SetupCredentialsDowngrade(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t negotiate_flags = 0; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_DOWNGRADE_DETECTED, "ServerAuthenticate3 should have failed"); + + negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 should succeed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); + + /* Prove that requesting a challenge again won't break it */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + return true; +} + +bool test_SetupCredentialsPipe(const struct dcerpc_pipe *p1, + struct torture_context *tctx, + struct cli_credentials *machine_credentials, + struct netlogon_creds_CredentialState *creds, + uint32_t additional_flags, + struct dcerpc_pipe **_p2) +{ + NTSTATUS status; + struct dcerpc_binding *b2 = NULL; + struct dcerpc_pipe *p2 = NULL; + + b2 = dcerpc_binding_dup(tctx, p1->binding); + torture_assert(tctx, b2 != NULL, "dcerpc_binding_dup"); + dcerpc_binding_set_flags(b2, + DCERPC_SCHANNEL | additional_flags, + DCERPC_AUTH_OPTIONS); + + cli_credentials_set_netlogon_creds(machine_credentials, creds); + status = dcerpc_pipe_connect_b(tctx, &p2, b2, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx); + cli_credentials_set_netlogon_creds(machine_credentials, NULL); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b schannel"); + + *_p2 = p2; + return true; +} + +static bool test_ServerReqChallenge( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_OK, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_zero_challenge( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the client challenge to zero, this should fail + * CVE-2020-1472(ZeroLogon) + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + ZERO_STRUCT(credentials1); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_5_repeats( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the first 5 bytes of the client challenge to the same value, + * this should fail CVE-2020-1472(ZeroLogon) + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + credentials1.data[0] = 'A'; + credentials1.data[1] = 'A'; + credentials1.data[2] = 'A'; + credentials1.data[3] = 'A'; + credentials1.data[4] = 'A'; + credentials1.data[5] = 'B'; + credentials1.data[6] = 'C'; + credentials1.data[7] = 'D'; + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_4_repeats( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the first 4 bytes of the client challenge to the same + * value, this should pass as 5 bytes identical are needed to + * fail for CVE-2020-1472(ZeroLogon) + * + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + credentials1.data[0] = 'A'; + credentials1.data[1] = 'A'; + credentials1.data[2] = 'A'; + credentials1.data[3] = 'A'; + credentials1.data[4] = 'B'; + credentials1.data[5] = 'C'; + credentials1.data[6] = 'D'; + credentials1.data[7] = 'E'; + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_OK, + "ServerAuthenticate2 unexpected"); + + return true; +} + +/* + * Establish a NetLogon session, using a session key that encrypts the + * target character to zero + */ +static bool test_ServerAuthenticate2_encrypts_to_zero( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials, + const char target, + struct netlogon_creds_CredentialState **creds_out) +{ + const char *computer_name = + cli_credentials_get_workstation(machine_credentials); + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate2 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds = NULL; + const struct samr_Password *mach_password; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *account_name = cli_credentials_get_username( + machine_credentials); + uint32_t flags = + NETLOGON_NEG_AUTH2_ADS_FLAGS | + NETLOGON_NEG_SUPPORTS_AES; + enum netr_SchannelType sec_chan_type = + cli_credentials_get_secure_channel_type(machine_credentials); + /* + * Limit the number of attempts to generate a suitable session key. + */ + const unsigned MAX_ITER = 4096; + unsigned i = 0; + + mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx); + + r.in.server_name = NULL; + r.in.computer_name = computer_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + credentials1.data[0] = target; + i = 0; + torture_comment(tctx, "Generating candidate session keys\n"); + do { + TALLOC_FREE(creds); + i++; + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = computer_name; + a.in.negotiate_flags = &flags; + a.out.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init( + tctx, + a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, + &credentials2, + mach_password, + &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + } while (credentials3.data[0] != 0 && i < MAX_ITER); + + if (i >= MAX_ITER) { + torture_comment( + tctx, + "Unable to obtain a suitable session key, " + "after [%u] attempts\n", + i); + torture_fail(tctx, "Unable to obtain suitable session key"); + } + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_OK, + "ServerAuthenticate2 unexpected result code"); + + *creds_out = creds; + return true; +} + +/* + try a change password for our machine account +*/ +static bool test_SetPassword(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet r; + const char *password; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator credential, return_authenticator; + struct samr_Password new_password; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + password = generate_random_password(tctx, 8, 255); + E_md4hash(password, new_password.hash); + + netlogon_creds_des_encrypt(creds, &new_password); + + torture_comment(tctx, "Testing ServerPasswordSet on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", + password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r), + "ServerPasswordSet failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + /* by changing the machine password twice we test the + credentials chaining fully, and we verify that the server + allows the password to be set to the same value twice in a + row (match win2k3) */ + torture_comment(tctx, + "Testing a second ServerPasswordSet on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s' (same as previous run)\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r), + "ServerPasswordSet (2) failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (2) failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + torture_assert(tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + return true; +} + +/* + try a change password for our machine account +*/ +static bool test_SetPassword_flags(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials, + uint32_t negotiate_flags) +{ + struct netr_ServerPasswordSet r; + const char *password; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator credential, return_authenticator; + struct samr_Password new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_SetupCredentials2(p1, tctx, negotiate_flags, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + password = generate_random_password(tctx, 8, 255); + E_md4hash(password, new_password.hash); + + netlogon_creds_des_encrypt(creds, &new_password); + + torture_comment(tctx, "Testing ServerPasswordSet on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", + password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r), + "ServerPasswordSet failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + /* by changing the machine password twice we test the + credentials chaining fully, and we verify that the server + allows the password to be set to the same value twice in a + row (match win2k3) */ + torture_comment(tctx, + "Testing a second ServerPasswordSet on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s' (same as previous run)\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r), + "ServerPasswordSet (2) failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (2) failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + torture_assert(tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + return true; +} + + +/* + generate a random password for password change tests +*/ +static DATA_BLOB netlogon_very_rand_pass(TALLOC_CTX *mem_ctx, int len) +{ + int i; + DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */); + generate_random_buffer(password.data, password.length); + + for (i=0; i < len; i++) { + if (((uint16_t *)password.data)[i] == 0) { + ((uint16_t *)password.data)[i] = 1; + } + } + + return password; +} + +/* + try a change password for our machine account +*/ +static bool test_SetPassword2_with_flags(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials, + uint32_t flags) +{ + struct netr_ServerPasswordSet2 r; + const char *password; + DATA_BLOB new_random_pass; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct samr_Password nt_hash; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_SetupCredentials2(p1, tctx, flags, machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + password = generate_random_password(tctx, 8, 255); + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + } else { + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, "Testing ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet2 failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + /* + * As a consequence of CVE-2020-1472(ZeroLogon) + * Samba explicitly disallows the setting of an empty machine account + * password. + * + * Note that this may fail against Windows, and leave a machine account + * with an empty password. + */ + password = ""; + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + } else { + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, + "Testing ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s'\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 failed"); + torture_assert_ntstatus_equal( + tctx, + r.out.result, + NT_STATUS_WRONG_PASSWORD, + "ServerPasswordSet2 did not return NT_STATUS_WRONG_PASSWORD"); + + /* now try a random password */ + password = generate_random_password(tctx, 8, 255); + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + } else { + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, "Testing second ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, "Changing machine account password to '%s'\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 (2) failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet2 (2) failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + /* by changing the machine password twice we test the + credentials chaining fully, and we verify that the server + allows the password to be set to the same value twice in a + row (match win2k3) */ + torture_comment(tctx, + "Testing a second ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s' (same as previous run)\n", password); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet (3) failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (3) failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); + + torture_assert (tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + new_random_pass = netlogon_very_rand_pass(tctx, 128); + + /* now try a random stream of bytes for a password */ + set_pw_in_buffer(password_buf.data, &new_random_pass); + + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + } else { + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment(tctx, + "Testing a third ServerPasswordSet2 on machine account, with a completely random password\n"); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet (3) failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (3) failed"); + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + mdfour(nt_hash.hash, new_random_pass.data, new_random_pass.length); + + cli_credentials_set_password(machine_credentials, NULL, CRED_UNINITIALISED); + cli_credentials_set_nt_hash(machine_credentials, &nt_hash, CRED_SPECIFIED); + + torture_assert (tctx, + test_SetupCredentials(p, tctx, machine_credentials, &creds), + "ServerPasswordSet failed to actually change the password"); + + return true; +} + +/* + try to change the password of our machine account using a buffer of all zeros, + and a session key that encrypts that to all zeros. + +Note: The test does use sign and seal, it's purpose is to exercise + the detection code in dcesrv_netr_ServerPasswordSet2 +*/ +static bool test_SetPassword2_encrypted_to_all_zeros( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_ServerAuthenticate2_encrypts_to_zero( + tctx, + p1, + machine_credentials, + '\0', + &creds)) { + + return false; + } + + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + ZERO_STRUCT(password_buf); + + if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) { + torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set"); + } + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + if(!all_zero(password_buf.data, 516)) { + torture_fail(tctx, "Password did not encrypt to all zeros\n"); + } + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + torture_assert_int_equal( + tctx, + new_password.length, + 0, + "Length should have encrypted to 0"); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 zero length check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, ""); + + return true; +} + +/* + * Choose a session key that encrypts a password of all zeros to all zeros. + * Then try to set the password, using a zeroed buffer, with a non zero + * length. + * + * This exercises the password self encryption check. + * + * Note: The test does use sign and seal, it's purpose is to exercise + * the detection code in dcesrv_netr_ServerPasswordSet2 +*/ +static bool test_SetPassword2_password_encrypts_to_zero( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_ServerAuthenticate2_encrypts_to_zero( + tctx, + p1, + machine_credentials, + 0x00, + &creds)) { + + return false; + } + + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + ZERO_STRUCT(password_buf); + SIVAL(password_buf.data, 512, 512); + + if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) { + torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set"); + } + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 password encrypts to zero check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, ""); + + return true; +} + +/* + * Check that an all zero confounder, that encrypts to all zeros is + * rejected. + * + * Note: The test does use sign and seal, it's purpose is to exercise + * the detection code in dcesrv_netr_ServerPasswordSet2 + */ +static bool test_SetPassword2_confounder( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_ServerAuthenticate2_encrypts_to_zero( + tctx, + p1, + machine_credentials, + '\0', + &creds)) { + + return false; + } + + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + ZERO_STRUCT(password_buf); + password_buf.data[511] = 'A'; + SIVAL(password_buf.data, 512, 2); + + if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) { + torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set"); + } + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 confounder check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, ""); + + return true; +} + +/* + * try a change password for our machine account, using an all zero + * request. This should fail on the zero length check. + * + * Note: This test uses ARC4 encryption to exercise the desired check. + */ +static bool test_SetPassword2_all_zeros( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; /* no AES desired here */ + + if (!test_SetupCredentials2( + p1, + tctx, + flags, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) + { + return false; + } + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + ZERO_STRUCT(password_buf.data); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES enabled\n"); + } + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment( + tctx, + "Testing ServerPasswordSet2 on machine account\n"); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 zero length check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, ""); + + return true; +} + +/* + try a change password for our machine account, using a maximum length + password +*/ +static bool test_SetPassword2_maximum_length_password( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + DATA_BLOB new_random_pass = data_blob_null; + + if (!test_SetupCredentials2( + p1, + tctx, + flags, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) + { + return false; + } + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + new_random_pass = netlogon_very_rand_pass(tctx, 256); + set_pw_in_buffer(password_buf.data, &new_random_pass); + SIVAL(password_buf.data, 512, 512); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); + } else { + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment( + tctx, + "Testing ServerPasswordSet2 on machine account\n"); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 zero length check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_OK, ""); + + return true; +} + +/* + try a change password for our machine account, using a password of + all zeros, and a non zero password length. + + This test relies on the buffer being encrypted with ARC4, to + trigger the appropriate check in the rpc server code +*/ +static bool test_SetPassword2_all_zero_password( + struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordSet2 r; + struct netlogon_creds_CredentialState *creds; + struct samr_CryptPassword password_buf; + struct netr_Authenticator credential, return_authenticator; + struct netr_CryptPassword new_password; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; /* no AES desired here */ + + if (!test_SetupCredentials2( + p1, + tctx, + flags, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) + { + return false; + } + if (!test_SetupCredentialsPipe( + p1, + tctx, + machine_credentials, + creds, + DCERPC_SIGN | DCERPC_SEAL, + &p)) + { + return false; + } + b = p->binding_handle; + + r.in.server_name = talloc_asprintf( + tctx, + "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + ZERO_STRUCT(password_buf.data); + SIVAL(password_buf.data, 512, 128); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES set"); + } + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); + + torture_comment( + tctx, + "Testing ServerPasswordSet2 on machine account\n"); + + netlogon_creds_client_authenticator(creds, &credential); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 all zero password check failed"); + torture_assert_ntstatus_equal( + tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, ""); + + return true; +} + + +static bool test_SetPassword2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + return test_SetPassword2_with_flags(tctx, p, machine_credentials, NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_SetPassword2_AES(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + return test_SetPassword2_with_flags(tctx, p, machine_credentials, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +static bool test_GetPassword(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerPasswordGet r; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator credential; + NTSTATUS status; + struct netr_Authenticator return_authenticator; + struct samr_Password password; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + netlogon_creds_client_authenticator(creds, &credential); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.out.return_authenticator = &return_authenticator; + r.out.password = &password; + + status = dcerpc_netr_ServerPasswordGet_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordGet"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordGet"); + + return true; +} + +static bool test_GetTrustPasswords(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_ServerTrustPasswordsGet r; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator credential; + struct netr_Authenticator return_authenticator; + struct samr_Password password, password2; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + netlogon_creds_client_authenticator(creds, &credential); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.out.return_authenticator = &return_authenticator; + r.out.new_owf_password = &password; + r.out.old_owf_password = &password2; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerTrustPasswordsGet_r(b, tctx, &r), + "ServerTrustPasswordsGet failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerTrustPasswordsGet failed"); + + return true; +} + +/* + try a netlogon SamLogon +*/ +static bool test_netlogon_ops_args(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState *creds, + bool null_domain) +{ + NTSTATUS status; + struct netr_LogonSamLogon r; + struct netr_Authenticator auth, auth2; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative; + struct netr_NetworkInfo ninfo; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + int flags = CLI_CRED_NTLM_AUTH; + if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx) && !null_domain) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(), + tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + if (null_domain) { + ninfo.identity_info.domain_name.string = NULL; + } + + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(credentials), + cli_credentials_get_domain(credentials)); + + status = cli_credentials_get_ntlm_response( + samba_cmdline_get_creds(), tctx, + &flags, + chal, + NULL, /* server_timestamp */ + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonNetworkInformation; + r.in.logon = &logon; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + + d_printf("Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string); + + for (i=2;i<=3;i++) { + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + + r.in.validation_level = i; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + } + + /* this makes sure we get the unmarshalling right for invalid levels */ + for (i=52;i<53;i++) { + ZERO_STRUCT(auth2); + /* the authenticator should be ignored by the server */ + generate_random_buffer((uint8_t *) &auth, sizeof(auth)); + + r.in.validation_level = i; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, + NT_STATUS_INVALID_INFO_CLASS, + "LogonSamLogon failed"); + + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + torture_assert(tctx, + all_zero((uint8_t *)&auth2, sizeof(auth2)), + "Return authenticator non zero"); + } + + for (i=2;i<=3;i++) { + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + + r.in.validation_level = i; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + } + + r.in.logon_level = 52; + + for (i=2;i<=3;i++) { + ZERO_STRUCT(auth2); + /* the authenticator should be ignored by the server */ + generate_random_buffer((uint8_t *) &auth, sizeof(auth)); + + r.in.validation_level = i; + + torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, + "LogonSamLogon expected INVALID_PARAMETER"); + + torture_assert(tctx, + all_zero((uint8_t *)&auth2, sizeof(auth2)), + "Return authenticator non zero"); + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + } + + r.in.credential = NULL; + + for (i=2;i<=3;i++) { + ZERO_STRUCT(auth2); + + r.in.validation_level = i; + + torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, + "LogonSamLogon expected INVALID_PARAMETER"); + + torture_assert(tctx, + all_zero((uint8_t *)&auth2, sizeof(auth2)), + "Return authenticator non zero"); + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + } + + r.in.logon_level = NetlogonNetworkInformation; + r.in.credential = &auth; + + for (i=2;i<=3;i++) { + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + + r.in.validation_level = i; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + torture_assert_int_equal(tctx, *r.out.authoritative, 1, + "LogonSamLogon invalid *r.out.authoritative"); + } + + return true; +} + +bool test_netlogon_ops(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState *creds) +{ + return test_netlogon_ops_args(p, tctx, credentials, creds, false); +} + +/* + try a netlogon GetCapabilities +*/ +bool test_netlogon_capabilities(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState *creds) +{ + NTSTATUS status; + struct netr_LogonGetCapabilities r; + union netr_Capabilities capabilities; + struct netr_Authenticator auth, return_auth; + struct netlogon_creds_CredentialState tmp_creds; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.credential = &auth; + r.in.return_authenticator = &return_auth; + r.in.query_level = 1; + r.out.capabilities = &capabilities; + r.out.return_authenticator = &return_auth; + + torture_comment(tctx, "Testing LogonGetCapabilities with query_level=0\n"); + + r.in.query_level = 0; + ZERO_STRUCT(return_auth); + + /* + * we need to operate on a temporary copy of creds + * because dcerpc_netr_LogonGetCapabilities with + * an unknown query level returns DCERPC_NCA_S_FAULT_INVALID_TAG + * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE + * without looking at the authenticator. + */ + tmp_creds = *creds; + netlogon_creds_client_authenticator(&tmp_creds, &auth); + + status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE, + "LogonGetCapabilities query_level=0 failed"); + + torture_comment(tctx, "Testing LogonGetCapabilities with query_level=3\n"); + + r.in.query_level = 3; + ZERO_STRUCT(return_auth); + + /* + * we need to operate on a temporary copy of creds + * because dcerpc_netr_LogonGetCapabilities with + * an unknown query level returns DCERPC_NCA_S_FAULT_INVALID_TAG + * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE + * without looking at the authenticator. + */ + tmp_creds = *creds; + netlogon_creds_client_authenticator(&tmp_creds, &auth); + + status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE, + "LogonGetCapabilities query_level=0 failed"); + + torture_comment(tctx, "Testing LogonGetCapabilities with query_level=1\n"); + + r.in.query_level = 1; + ZERO_STRUCT(return_auth); + + /* + * we need to operate on a temporary copy of creds + * because dcerpc_netr_LogonGetCapabilities was + * dcerpc_netr_DummyFunction and returns NT_STATUS_NOT_IMPLEMENTED + * without looking at the authenticator. + */ + tmp_creds = *creds; + netlogon_creds_client_authenticator(&tmp_creds, &auth); + + status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonGetCapabilities failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) { + return true; + } + + *creds = tmp_creds; + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + + torture_assert_int_equal(tctx, creds->negotiate_flags, + capabilities.server_capabilities, + "negotiate flags"); + + torture_comment(tctx, "Testing LogonGetCapabilities with query_level=2\n"); + + r.in.query_level = 2; + ZERO_STRUCT(return_auth); + + /* + * we need to operate on a temporary copy of creds + * because dcerpc_netr_LogonGetCapabilities with + * an query level 2 may returns DCERPC_NCA_S_FAULT_INVALID_TAG + * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE + * without looking at the authenticator. + */ + tmp_creds = *creds; + netlogon_creds_client_authenticator(&tmp_creds, &auth); + + status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) { + /* + * an server without KB5028166 returns + * DCERPC_NCA_S_FAULT_INVALID_TAG => + * NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE + */ + return true; + } + torture_assert_ntstatus_ok(tctx, status, "LogonGetCapabilities query_level=2 failed"); + + *creds = tmp_creds; + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + + torture_assert_int_equal(tctx, creds->negotiate_flags, + capabilities.server_capabilities, + "negotiate flags"); + + return true; +} + +/* + try a netlogon SamLogon +*/ +static bool test_SamLogon(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, credentials, &creds)) { + return false; + } + + return test_netlogon_ops(p, tctx, credentials, creds); +} + +static bool test_invalidAuthenticate2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + + torture_comment(tctx, "Testing invalidAuthenticate2\n"); + + if (!test_SetupCredentials2(p, tctx, flags, + credentials, + cli_credentials_get_secure_channel_type(credentials), + &creds)) { + return false; + } + + if (!test_SetupCredentials2ex(p, tctx, flags, + credentials, + "1234567890123456", + cli_credentials_get_secure_channel_type(credentials), + STATUS_BUFFER_OVERFLOW, + &creds)) { + return false; + } + + if (!test_SetupCredentials2ex(p, tctx, flags, + credentials, + "123456789012345", + cli_credentials_get_secure_channel_type(credentials), + NT_STATUS_OK, + &creds)) { + return false; + } + + return true; +} + +static bool test_ServerReqChallengeGlobal(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + return true; +} + +/* + * Test the re-use of the challenge is not possible on a third + * connection, after first using it second one. + */ + +static bool test_ServerReqChallengeReuseGlobal(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + struct dcerpc_pipe *p3 = NULL; + struct dcerpc_binding_handle *b3 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p3, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b3 = p3->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + /* We have to re-run this part */ + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b3, tctx, &a), + "ServerAuthenticate3 failed on b3"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b3, due to credential reuse"); + return true; +} + +/* + * Test if use of the per-pipe challenge will wipe out the globally cached challenge + */ +static bool test_ServerReqChallengeReuseGlobal2(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a), + "ServerAuthenticate3 failed on b"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + /* We have to re-run this part */ + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b2, due to credential reuse"); + return true; +} + +/* + * Test if use of the globally cached challenge will wipe out the + * per-pipe challenge + */ +static bool test_ServerReqChallengeReuseGlobal3(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + /* We have to re-run this part */ + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a), + "ServerAuthenticate3 failed on b1"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b1, due to credential reuse"); + return true; +} + +/* + * Test if more than one globally cached challenge works + */ +static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials1_random, + credentials2, credentials3, credentials_discard; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = "CHALTEST1"; + r.in.credentials = &credentials1_random; + r.out.return_credentials = &credentials_discard; + + netlogon_creds_random_challenge(&credentials1_random); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + /* Now ask for the actual client name */ + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + r.in.server_name = NULL; + r.in.computer_name = "CHALTEST2"; + r.in.credentials = &credentials1_random; + r.out.return_credentials = &credentials_discard; + + netlogon_creds_random_challenge(&credentials1_random); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2 (must use global credentials)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + /* We have to re-run this part */ + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a), + "ServerAuthenticate3 failed on b1"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b1, due to credential reuse"); + return true; +} + +static bool test_ServerReqChallengeReuse(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b = p->binding_handle; + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert(tctx, machine_name != NULL, "machine_name"); + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert(tctx, plain_pass != NULL, "plain_pass"); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + /* We have to re-run this part */ + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b3, due to credential reuse"); + + ZERO_STRUCT(credentials1.data); + ZERO_STRUCT(credentials2.data); + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 with zero'ed challenge\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate3 should have failed on b3, due to credential reuse"); + return true; +} + +static bool test_SamLogon_NULL_domain(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + + if (!test_SetupCredentials(p, tctx, credentials, &creds)) { + return false; + } + + return test_netlogon_ops_args(p, tctx, credentials, creds, true); +} + +/* we remember the sequence numbers so we can easily do a DatabaseDelta */ +static uint64_t sequence_nums[3]; + +/* + try a netlogon DatabaseSync +*/ +static bool test_DatabaseSync(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_DatabaseSync r; + struct netlogon_creds_CredentialState *creds; + const uint32_t database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; + int i; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + struct netr_Authenticator credential, return_authenticator; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + r.in.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + r.out.return_authenticator = &return_authenticator; + + for (i=0;icred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + if (delta_enum_array && + delta_enum_array->num_deltas > 0 && + delta_enum_array->delta_enum[0].delta_type == NETR_DELTA_DOMAIN && + delta_enum_array->delta_enum[0].delta_union.domain) { + sequence_nums[r.in.database_id] = + delta_enum_array->delta_enum[0].delta_union.domain->sequence_num; + torture_comment(tctx, "\tsequence_nums[%d]=%llu\n", + r.in.database_id, + (unsigned long long)sequence_nums[r.in.database_id]); + } + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + + +/* + try a netlogon DatabaseDeltas +*/ +static bool test_DatabaseDeltas(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_DatabaseDeltas r; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator credential; + struct netr_Authenticator return_authenticator; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(r.in.return_authenticator); + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(null_sid); + + sid = dom_sid_parse_talloc(tctx, "S-1-5-21-1111111111-2222222222-333333333-500"); + + { + + struct { + uint32_t rid; + uint16_t flags; + uint8_t db_index; + uint8_t delta_type; + struct dom_sid sid; + const char *name; + NTSTATUS expected_error; + uint32_t expected_num_results; + uint8_t expected_delta_type_1; + uint8_t expected_delta_type_2; + const char *comment; + } changes[] = { + + /* SAM_DATABASE_DOMAIN */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_DOMAIN, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NETR_DELTA_DOMAIN" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_USER, + .comment = "NETR_DELTA_USER by rid 500" + }, + { + .rid = DOMAIN_RID_GUEST, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_USER, + .comment = "NETR_DELTA_USER by rid 501" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = *sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by null_sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = "administrator", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by name 'administrator'" + }, + { + .rid = DOMAIN_RID_ADMINS, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_GROUP, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_GROUP, + .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER, + .comment = "NETR_DELTA_GROUP by rid 512" + }, + { + .rid = DOMAIN_RID_ADMINS, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_GROUP_MEMBER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_GROUP, + .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER, + .comment = "NETR_DELTA_GROUP_MEMBER by rid 512" + }, + + + /* SAM_DATABASE_BUILTIN */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_DOMAIN, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NETR_DELTA_DOMAIN" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by rid 500" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS by rid 544" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS_MEMBER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS_MEMBER by rid 544" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA by rid 544" + }, + { + .rid = 544, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = 0, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA by rid 544 sid S-1-5-32-544 and flags" + }, + { + .rid = 544, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS by rid 544 and sid S-1-5-32-544 and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_ALIAS, + .comment = "NETR_DELTA_ALIAS by sid S-1-5-32-544 and flags" + }, + + /* SAM_DATABASE_PRIVS */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_ACCESS_DENIED, + .expected_num_results = 0, + .comment = "NULL DELTA" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY by null sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY by sid S-1-5-32 and flags" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, /* strange */ + .expected_num_results = 0, + .comment = "NETR_DELTA_ACCOUNT by rid 500" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED | + NETR_CHANGELOG_IMMEDIATE_REPL_REQUIRED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and 2 flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED | + NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_INVALID_PARAMETER, + .expected_num_results = 0, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and invalid flags" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by rid 500, sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_SECRET, + .sid = null_sid, + .name = "IsurelydontexistIhope", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_SECRET, + .comment = "NETR_DELTA_SECRET by name 'IsurelydontexistIhope' and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_SECRET, + .sid = null_sid, + .name = "G$BCKUPKEY_P", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_SECRET, + .comment = "NETR_DELTA_SECRET by name 'G$BCKUPKEY_P' and flags" + } + }; + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (d=0; d<3; d++) { + const char *database = NULL; + + switch (d) { + case 0: + database = "SAM"; + break; + case 1: + database = "BUILTIN"; + break; + case 2: + database = "LSA"; + break; + default: + break; + } + + torture_comment(tctx, "Testing DatabaseRedo\n"); + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + for (i=0;inum_deltas, + changes[i].expected_num_results, + changes[i].comment); + if (delta_enum_array->num_deltas > 0) { + torture_assert_int_equal(tctx, + delta_enum_array->delta_enum[0].delta_type, + changes[i].expected_delta_type_1, + changes[i].comment); + } + if (delta_enum_array->num_deltas > 1) { + torture_assert_int_equal(tctx, + delta_enum_array->delta_enum[1].delta_type, + changes[i].expected_delta_type_2, + changes[i].comment); + } + } + + if (!netlogon_creds_client_check(creds, &return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + } + } + } + } + + return true; +} + +/* + try a netlogon AccountDeltas +*/ +static bool test_AccountDeltas(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_AccountDeltas r; + struct netlogon_creds_CredentialState *creds; + + struct netr_AccountBuffer buffer; + uint32_t count_returned = 0; + uint32_t total_entries = 0; + struct netr_UAS_INFO_0 recordid; + struct netr_Authenticator return_authenticator; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.return_authenticator = &return_authenticator; + netlogon_creds_client_authenticator(creds, &r.in.credential); + ZERO_STRUCT(r.in.uas); + r.in.count=10; + r.in.level=0; + r.in.buffersize=100; + r.out.buffer = &buffer; + r.out.count_returned = &count_returned; + r.out.total_entries = &total_entries; + r.out.recordid = &recordid; + r.out.return_authenticator = &return_authenticator; + + /* w2k3 returns "NOT IMPLEMENTED" for this call */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_AccountDeltas_r(b, tctx, &r), + "AccountDeltas failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "AccountDeltas"); + + return true; +} + +/* + try a netlogon AccountSync +*/ +static bool test_AccountSync(struct torture_context *tctx, struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_AccountSync r; + struct netlogon_creds_CredentialState *creds; + + struct netr_AccountBuffer buffer; + uint32_t count_returned = 0; + uint32_t total_entries = 0; + uint32_t next_reference = 0; + struct netr_UAS_INFO_0 recordid; + struct netr_Authenticator return_authenticator; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(recordid); + ZERO_STRUCT(return_authenticator); + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.return_authenticator = &return_authenticator; + netlogon_creds_client_authenticator(creds, &r.in.credential); + r.in.recordid = &recordid; + r.in.reference=0; + r.in.level=0; + r.in.buffersize=100; + r.out.buffer = &buffer; + r.out.count_returned = &count_returned; + r.out.total_entries = &total_entries; + r.out.next_reference = &next_reference; + r.out.recordid = &recordid; + r.out.return_authenticator = &return_authenticator; + + /* w2k3 returns "NOT IMPLEMENTED" for this call */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_AccountSync_r(b, tctx, &r), + "AccountSync failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "AccountSync"); + + return true; +} + +/* + try a netlogon GetDcName +*/ +static bool test_GetDcName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct netr_GetDcName r; + const char *dcname = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domainname = lpcfg_workgroup(tctx->lp_ctx); + r.out.dcname = &dcname; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_GetDcName_r(b, tctx, &r), + "GetDcName failed"); + torture_assert_werr_ok(tctx, r.out.result, "GetDcName failed"); + + torture_comment(tctx, "\tDC is at '%s'\n", dcname); + + return true; +} + +static const char *function_code_str(TALLOC_CTX *mem_ctx, + enum netr_LogonControlCode function_code) +{ + switch (function_code) { + case NETLOGON_CONTROL_QUERY: + return "NETLOGON_CONTROL_QUERY"; + case NETLOGON_CONTROL_REPLICATE: + return "NETLOGON_CONTROL_REPLICATE"; + case NETLOGON_CONTROL_SYNCHRONIZE: + return "NETLOGON_CONTROL_SYNCHRONIZE"; + case NETLOGON_CONTROL_PDC_REPLICATE: + return "NETLOGON_CONTROL_PDC_REPLICATE"; + case NETLOGON_CONTROL_REDISCOVER: + return "NETLOGON_CONTROL_REDISCOVER"; + case NETLOGON_CONTROL_TC_QUERY: + return "NETLOGON_CONTROL_TC_QUERY"; + case NETLOGON_CONTROL_TRANSPORT_NOTIFY: + return "NETLOGON_CONTROL_TRANSPORT_NOTIFY"; + case NETLOGON_CONTROL_FIND_USER: + return "NETLOGON_CONTROL_FIND_USER"; + case NETLOGON_CONTROL_CHANGE_PASSWORD: + return "NETLOGON_CONTROL_CHANGE_PASSWORD"; + case NETLOGON_CONTROL_TC_VERIFY: + return "NETLOGON_CONTROL_TC_VERIFY"; + case NETLOGON_CONTROL_FORCE_DNS_REG: + return "NETLOGON_CONTROL_FORCE_DNS_REG"; + case NETLOGON_CONTROL_QUERY_DNS_REG: + return "NETLOGON_CONTROL_QUERY_DNS_REG"; + case NETLOGON_CONTROL_BACKUP_CHANGE_LOG: + return "NETLOGON_CONTROL_BACKUP_CHANGE_LOG"; + case NETLOGON_CONTROL_TRUNCATE_LOG: + return "NETLOGON_CONTROL_TRUNCATE_LOG"; + case NETLOGON_CONTROL_SET_DBFLAG: + return "NETLOGON_CONTROL_SET_DBFLAG"; + case NETLOGON_CONTROL_BREAKPOINT: + return "NETLOGON_CONTROL_BREAKPOINT"; + default: + return talloc_asprintf(mem_ctx, "unknown function code: %d", + function_code); + } +} + + +/* + try a netlogon LogonControl +*/ +static bool test_LogonControl(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) + +{ + NTSTATUS status; + struct netr_LogonControl r; + union netr_CONTROL_QUERY_INFORMATION query; + int i,f; + enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t function_codes[] = { + NETLOGON_CONTROL_QUERY, + NETLOGON_CONTROL_REPLICATE, + NETLOGON_CONTROL_SYNCHRONIZE, + NETLOGON_CONTROL_PDC_REPLICATE, + NETLOGON_CONTROL_REDISCOVER, + NETLOGON_CONTROL_TC_QUERY, + NETLOGON_CONTROL_TRANSPORT_NOTIFY, + NETLOGON_CONTROL_FIND_USER, + NETLOGON_CONTROL_CHANGE_PASSWORD, + NETLOGON_CONTROL_TC_VERIFY, + NETLOGON_CONTROL_FORCE_DNS_REG, + NETLOGON_CONTROL_QUERY_DNS_REG, + NETLOGON_CONTROL_BACKUP_CHANGE_LOG, + NETLOGON_CONTROL_TRUNCATE_LOG, + NETLOGON_CONTROL_SET_DBFLAG, + NETLOGON_CONTROL_BREAKPOINT + }; + + if (machine_credentials) { + secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + } + + torture_comment(tctx, "Testing LogonControl with secure channel type: %d\n", + secure_channel_type); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.function_code = 1; + r.out.query = &query; + + for (f=0;fbinding_handle; + + r.in.domainname = lpcfg_workgroup(tctx->lp_ctx); + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.dcname = &dcname; + + status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + if ((!W_ERROR_IS_OK(r.out.result)) && + (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) { + return false; + } + + if (dcname) { + torture_comment(tctx, "\tDC is at '%s'\n", dcname); + } + + r.in.domainname = NULL; + + status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + if ((!W_ERROR_IS_OK(r.out.result)) && + (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) { + return false; + } + + r.in.domainname = ""; + + status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + if ((!W_ERROR_IS_OK(r.out.result)) && + (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) { + return false; + } + + return true; +} + + +/* + try a netlogon LogonControl2 +*/ +static bool test_LogonControl2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) + +{ + NTSTATUS status; + struct netr_LogonControl2 r; + union netr_CONTROL_DATA_INFORMATION data; + union netr_CONTROL_QUERY_INFORMATION query; + enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + if (machine_credentials) { + secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + } + + torture_comment(tctx, "Testing LogonControl2 with secure channel type: %d\n", + secure_channel_type); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + r.in.function_code = NETLOGON_CONTROL_REDISCOVER; + r.in.data = &data; + r.out.query = &query; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + } + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + r.in.function_code = NETLOGON_CONTROL_TC_QUERY; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + } + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + } + + data.debug_level = ~0; + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + } + + ZERO_STRUCT(data); + r.in.function_code = 52; + r.in.data = &data; + + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + switch (secure_channel_type) { + case SEC_CHAN_NULL: + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, "LogonControl2"); + break; + default: + torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED, "LogonControl2"); + break; + } + data.debug_level = ~0; + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data = &data; + + r.in.level = 52; + torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "LogonControl2"); + + return true; +} + +/* + try a netlogon DatabaseSync2 +*/ +static bool test_DatabaseSync2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct netr_DatabaseSync2 r; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + struct netr_Authenticator return_authenticator, credential; + + struct netlogon_creds_CredentialState *creds; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_FLAGS, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) { + return false; + } + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (i=0;icred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + + +/* + try a netlogon LogonControl2Ex +*/ +static bool test_LogonControl2Ex(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) + +{ + NTSTATUS status; + struct netr_LogonControl2Ex r; + union netr_CONTROL_DATA_INFORMATION data; + union netr_CONTROL_QUERY_INFORMATION query; + enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + if (machine_credentials) { + secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + } + + torture_comment(tctx, "Testing LogonControl2Ex with secure channel type: %d\n", + secure_channel_type); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + r.in.function_code = NETLOGON_CONTROL_REDISCOVER; + r.in.data = &data; + r.out.query = &query; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + } + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + r.in.function_code = NETLOGON_CONTROL_TC_QUERY; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + } + + data.domain = lpcfg_workgroup(tctx->lp_ctx); + + r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + } + + data.debug_level = ~0; + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data = &data; + + for (i=1;i<4;i++) { + r.in.level = i; + + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + } + + ZERO_STRUCT(data); + r.in.function_code = 52; + r.in.data = &data; + + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + switch (secure_channel_type) { + case SEC_CHAN_NULL: + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, "LogonControl2Ex"); + break; + default: + torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED, "LogonControl2Ex"); + break; + } + data.debug_level = ~0; + + r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG; + r.in.data = &data; + + r.in.level = 52; + torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n", + function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level); + + status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "LogonControl2Ex"); + + return true; +} + +static bool test_netr_GetForestTrustInformation(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_GetForestTrustInformation r; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator a; + struct netr_Authenticator return_authenticator; + struct lsa_ForestTrustInformation *forest_trust_info; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + machine_credentials, &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + netlogon_creds_client_authenticator(creds, &a); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + r.in.flags = 0; + r.out.return_authenticator = &return_authenticator; + r.out.forest_trust_info = &forest_trust_info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_GetForestTrustInformation_r(b, tctx, &r), + "netr_GetForestTrustInformation failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) { + torture_comment(tctx, "not considering NT_STATUS_NOT_IMPLEMENTED as an error\n"); + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, + "netr_GetForestTrustInformation failed"); + } + + torture_assert(tctx, + netlogon_creds_client_check(creds, &return_authenticator.cred), + "Credential chaining failed"); + + return true; +} + +static bool test_netr_DsRGetForestTrustInformation(struct torture_context *tctx, + struct dcerpc_pipe *p, const char *trusted_domain_name) +{ + NTSTATUS status; + struct netr_DsRGetForestTrustInformation r; + struct lsa_ForestTrustInformation info, *info_ptr; + struct dcerpc_binding_handle *b = p->binding_handle; + + info_ptr = &info; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.trusted_domain_name = trusted_domain_name; + r.in.flags = 0; + r.out.forest_trust_info = &info_ptr; + + torture_comment(tctx ,"Testing netr_DsRGetForestTrustInformation\n"); + + status = dcerpc_netr_DsRGetForestTrustInformation_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetForestTrustInformation"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetForestTrustInformation"); + + return true; +} + +/* + try a netlogon netr_DsrEnumerateDomainTrusts +*/ +static bool test_DsrEnumerateDomainTrusts(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsrEnumerateDomainTrusts r; + struct netr_DomainTrustList trusts; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.trust_flags = 0x3f; + r.out.trusts = &trusts; + + status = dcerpc_netr_DsrEnumerateDomainTrusts_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsrEnumerateDomaintrusts"); + torture_assert_werr_ok(tctx, r.out.result, "DsrEnumerateDomaintrusts"); + + /* when trusted_domain_name is NULL, netr_DsRGetForestTrustInformation + * will show non-forest trusts and all UPN suffixes of the own forest + * as LSA_FOREST_TRUST_TOP_LEVEL_NAME types */ + + if (r.out.trusts->count) { + if (!test_netr_DsRGetForestTrustInformation(tctx, p, NULL)) { + return false; + } + } + + for (i=0; icount; i++) { + + /* get info for transitive forest trusts */ + + if (r.out.trusts->array[i].trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) { + if (!test_netr_DsRGetForestTrustInformation(tctx, p, + r.out.trusts->array[i].dns_name)) { + return false; + } + } + } + + return true; +} + +static bool test_netr_NetrEnumerateTrustedDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_NetrEnumerateTrustedDomains r; + struct netr_Blob trusted_domains_blob; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.trusted_domains_blob = &trusted_domains_blob; + + status = dcerpc_netr_NetrEnumerateTrustedDomains_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomains"); + torture_assert_ntstatus_ok(tctx, r.out.result, "NetrEnumerateTrustedDomains"); + + return true; +} + +static bool test_netr_NetrEnumerateTrustedDomainsEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_NetrEnumerateTrustedDomainsEx r; + struct netr_DomainTrustList dom_trust_list; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.dom_trust_list = &dom_trust_list; + + status = dcerpc_netr_NetrEnumerateTrustedDomainsEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomainsEx"); + torture_assert_werr_ok(tctx, r.out.result, "NetrEnumerateTrustedDomainsEx"); + + return true; +} + + +static bool test_netr_DsRGetSiteName(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *computer_name, + const char *expected_site) +{ + NTSTATUS status; + struct netr_DsRGetSiteName r; + const char *site = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.computer_name = computer_name; + r.out.site = &site; + torture_comment(tctx, "Testing netr_DsRGetSiteName\n"); + + status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetSiteName"); + torture_assert_str_equal(tctx, expected_site, site, "netr_DsRGetSiteName"); + + return true; +} + +/* + try a netlogon netr_DsRGetDCName +*/ +static bool test_netr_DsRGetDCName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCName r; + struct netr_DsRGetDCNameInfo *info = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx); + r.in.domain_guid = NULL; + r.in.site_guid = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + r.out.info = &info; + + status = dcerpc_netr_DsRGetDCName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), + DS_DNS_CONTROLLER, + "DsRGetDCName"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), + DS_DNS_DOMAIN, + "DsRGetDCName"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCName"); + + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + r.in.flags = 0; + + status = dcerpc_netr_DsRGetDCName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName"); + torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), 0, + "DsRGetDCName"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), 0, + "DsRGetDCName"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCName"); + + if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) { + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_SERVER_CLOSEST)), + DS_SERVER_CLOSEST, + "DsRGetDCName"); + } + + return test_netr_DsRGetSiteName(p, tctx, + info->dc_unc, + info->dc_site_name); +} + +/* + try a netlogon netr_DsRGetDCNameEx +*/ +static bool test_netr_DsRGetDCNameEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCNameEx r; + struct netr_DsRGetDCNameInfo *info = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx); + r.in.domain_guid = NULL; + r.in.site_name = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + r.out.info = &info; + + status = dcerpc_netr_DsRGetDCNameEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), + DS_DNS_CONTROLLER, + "DsRGetDCNameEx"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), + DS_DNS_DOMAIN, + "DsRGetDCNameEx"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCNameEx"); + + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + r.in.flags = 0; + + status = dcerpc_netr_DsRGetDCNameEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), 0, + "DsRGetDCNameEx"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), 0, + "DsRGetDCNameEx"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCNameEx"); + + if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) { + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_SERVER_CLOSEST)), + DS_SERVER_CLOSEST, + "DsRGetDCNameEx"); + } + + return test_netr_DsRGetSiteName(p, tctx, info->dc_unc, + info->dc_site_name); +} + +/* + try a netlogon netr_DsRGetDCNameEx2 +*/ +static bool test_netr_DsRGetDCNameEx2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct netr_DsRGetDCNameEx2 r; + struct netr_DsRGetDCNameInfo *info = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with no inputs\n"); + ZERO_STRUCT(r.in); + r.in.flags = DS_RETURN_DNS_NAME; + r.out.info = &info; + + status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), + DS_DNS_CONTROLLER, + "DsRGetDCNameEx2"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), + DS_DNS_DOMAIN, + "DsRGetDCNameEx2"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCNameEx2"); + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.client_account = NULL; + r.in.mask = 0x00000000; + r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx); + r.in.domain_guid = NULL; + r.in.site_name = NULL; + r.in.flags = DS_RETURN_DNS_NAME; + r.out.info = &info; + + torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 without client account\n"); + + status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + r.in.flags = 0; + + status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_CONTROLLER)), 0, + "DsRGetDCNameEx2"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_DOMAIN)), 0, + "DsRGetDCNameEx2"); + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_DNS_FOREST_ROOT)), + DS_DNS_FOREST_ROOT, + "DsRGetDCNameEx2"); + + if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) { + torture_assert_int_equal(tctx, + (info->dc_flags & (DS_SERVER_CLOSEST)), + DS_SERVER_CLOSEST, + "DsRGetDCNameEx2"); + } + + torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with client account\n"); + r.in.client_account = TEST_MACHINE_NAME"$"; + r.in.mask = ACB_SVRTRUST; + r.in.flags = DS_RETURN_FLAT_NAME; + r.out.info = &info; + + status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2"); + torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2"); + + return test_netr_DsRGetSiteName(p, tctx, info->dc_unc, + info->dc_site_name); +} + +/* This is a substitution for "samdb_server_site_name" which relies on the + * correct "lp_ctx" and therefore can't be used here. */ +static const char *server_site_name(struct torture_context *tctx, + struct ldb_context *ldb) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *dn, *server_dn; + const struct ldb_val *site_name_val; + const char *server_dn_str, *site_name; + + tmp_ctx = talloc_new(ldb); + if (tmp_ctx == NULL) { + goto failed; + } + + dn = ldb_dn_new(tmp_ctx, ldb, ""); + if (dn == NULL) { + goto failed; + } + + server_dn_str = samdb_search_string(ldb, tmp_ctx, dn, "serverName", + NULL); + if (server_dn_str == NULL) { + goto failed; + } + + server_dn = ldb_dn_new(tmp_ctx, ldb, server_dn_str); + if (server_dn == NULL) { + goto failed; + } + + /* CN=, CN=Servers, CN=, CN=Sites, ... */ + site_name_val = ldb_dn_get_component_val(server_dn, 2); + if (site_name_val == NULL) { + goto failed; + } + + site_name = (const char *) site_name_val->data; + + talloc_steal(tctx, site_name); + talloc_free(tmp_ctx); + + return site_name; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + +static bool test_netr_DsrGetDcSiteCoverageW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + char *url; + struct ldb_context *sam_ctx = NULL; + NTSTATUS status; + struct netr_DsrGetDcSiteCoverageW r; + struct DcSitesCtr *ctr = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "This does only pass with the default site\n"); + + /* We won't double-check this when we are over 'local' transports */ + if (dcerpc_server_name(p)) { + /* Set up connection to SAMDB on DC */ + url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p)); + sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + + torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!"); + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.out.ctr = &ctr; + + status = dcerpc_netr_DsrGetDcSiteCoverageW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + torture_assert(tctx, ctr->num_sites == 1, + "we should per default only get the default site"); + if (sam_ctx != NULL) { + torture_assert_casestr_equal(tctx, ctr->sites[0].string, + server_site_name(tctx, sam_ctx), + "didn't return default site"); + } + + return true; +} + +static bool test_netr_DsRAddressToSitenamesW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + char *url; + struct ldb_context *sam_ctx = NULL; + NTSTATUS status; + struct netr_DsRAddressToSitenamesW r; + struct netr_DsRAddress addrs[6]; + struct sockaddr_in *addr; +#ifdef HAVE_IPV6 + struct sockaddr_in6 *addr6; +#endif + struct netr_DsRAddressToSitenamesWCtr *ctr; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t i; + int ret; + + torture_comment(tctx, "This does only pass with the default site\n"); + + /* We won't double-check this when we are over 'local' transports */ + if (dcerpc_server_name(p)) { + /* Set up connection to SAMDB on DC */ + url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p)); + sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + + torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!"); + } + + /* First try valid IP addresses */ + + addrs[0].size = sizeof(struct sockaddr_in); + addrs[0].buffer = talloc_zero_array(tctx, uint8_t, addrs[0].size); + addr = (struct sockaddr_in *) addrs[0].buffer; + addrs[0].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[1].size = sizeof(struct sockaddr_in); + addrs[1].buffer = talloc_zero_array(tctx, uint8_t, addrs[1].size); + addr = (struct sockaddr_in *) addrs[1].buffer; + addrs[1].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[2].size = sizeof(struct sockaddr_in); + addrs[2].buffer = talloc_zero_array(tctx, uint8_t, addrs[2].size); + addr = (struct sockaddr_in *) addrs[2].buffer; + addrs[2].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + +#ifdef HAVE_IPV6 + addrs[3].size = sizeof(struct sockaddr_in6); + addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size); + addr6 = (struct sockaddr_in6 *) addrs[3].buffer; + addrs[3].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "::1", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[4].size = sizeof(struct sockaddr_in6); + addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size); + addr6 = (struct sockaddr_in6 *) addrs[4].buffer; + addrs[4].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "::", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[5].size = sizeof(struct sockaddr_in6); + addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size); + addr6 = (struct sockaddr_in6 *) addrs[5].buffer; + addrs[5].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "ff02::1", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); +#else + /* the test cases are repeated to have exactly 6. This is for + * compatibility with IPv4-only machines */ + addrs[3].size = sizeof(struct sockaddr_in); + addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size); + addr = (struct sockaddr_in *) addrs[3].buffer; + addrs[3].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[4].size = sizeof(struct sockaddr_in); + addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size); + addr = (struct sockaddr_in *) addrs[4].buffer; + addrs[4].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[5].size = sizeof(struct sockaddr_in); + addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size); + addr = (struct sockaddr_in *) addrs[5].buffer; + addrs[5].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); +#endif + + ctr = talloc(tctx, struct netr_DsRAddressToSitenamesWCtr); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.count = 6; + r.in.addresses = addrs; + r.out.ctr = &ctr; + + status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + if (sam_ctx != NULL) { + for (i = 0; i < 3; i++) { + torture_assert_casestr_equal(tctx, + ctr->sitename[i].string, + server_site_name(tctx, sam_ctx), + "didn't return default site"); + } + for (i = 3; i < 6; i++) { + /* Windows returns "NULL" for the sitename if it isn't + * IPv6 configured */ + if (torture_setting_bool(tctx, "samba4", false)) { + torture_assert_casestr_equal(tctx, + ctr->sitename[i].string, + server_site_name(tctx, sam_ctx), + "didn't return default site"); + } + } + } + + /* Now try invalid ones (too short buffers) */ + + addrs[0].size = 0; + addrs[1].size = 1; + addrs[2].size = 4; + + addrs[3].size = 0; + addrs[4].size = 1; + addrs[5].size = 4; + + status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + for (i = 0; i < 6; i++) { + torture_assert(tctx, ctr->sitename[i].string == NULL, + "sitename should be null"); + } + + /* Now try invalid ones (wrong address types) */ + + addrs[0].size = 10; + addrs[0].buffer[0] = AF_UNSPEC; + addrs[1].size = 10; + addrs[1].buffer[0] = AF_UNIX; /* AF_LOCAL = AF_UNIX */ + addrs[2].size = 10; + addrs[2].buffer[0] = AF_UNIX; + + addrs[3].size = 10; + addrs[3].buffer[0] = 250; + addrs[4].size = 10; + addrs[4].buffer[0] = 251; + addrs[5].size = 10; + addrs[5].buffer[0] = 252; + + status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + for (i = 0; i < 6; i++) { + torture_assert(tctx, ctr->sitename[i].string == NULL, + "sitename should be null"); + } + + return true; +} + +static bool test_netr_DsRAddressToSitenamesExW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + char *url; + struct ldb_context *sam_ctx = NULL; + NTSTATUS status; + struct netr_DsRAddressToSitenamesExW r; + struct netr_DsRAddress addrs[6]; + struct sockaddr_in *addr; +#ifdef HAVE_IPV6 + struct sockaddr_in6 *addr6; +#endif + struct netr_DsRAddressToSitenamesExWCtr *ctr; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t i; + int ret; + + torture_comment(tctx, "This does pass with the default site\n"); + + /* We won't double-check this when we are over 'local' transports */ + if (dcerpc_server_name(p)) { + /* Set up connection to SAMDB on DC */ + url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p)); + sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + + torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!"); + } + + /* First try valid IP addresses */ + + addrs[0].size = sizeof(struct sockaddr_in); + addrs[0].buffer = talloc_zero_array(tctx, uint8_t, addrs[0].size); + addr = (struct sockaddr_in *) addrs[0].buffer; + addrs[0].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[1].size = sizeof(struct sockaddr_in); + addrs[1].buffer = talloc_zero_array(tctx, uint8_t, addrs[1].size); + addr = (struct sockaddr_in *) addrs[1].buffer; + addrs[1].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[2].size = sizeof(struct sockaddr_in); + addrs[2].buffer = talloc_zero_array(tctx, uint8_t, addrs[2].size); + addr = (struct sockaddr_in *) addrs[2].buffer; + addrs[2].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + +#ifdef HAVE_IPV6 + addrs[3].size = sizeof(struct sockaddr_in6); + addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size); + addr6 = (struct sockaddr_in6 *) addrs[3].buffer; + addrs[3].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "::1", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[4].size = sizeof(struct sockaddr_in6); + addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size); + addr6 = (struct sockaddr_in6 *) addrs[4].buffer; + addrs[4].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "::", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[5].size = sizeof(struct sockaddr_in6); + addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size); + addr6 = (struct sockaddr_in6 *) addrs[5].buffer; + addrs[5].buffer[0] = AF_INET6; + ret = inet_pton(AF_INET6, "ff02::1", &addr6->sin6_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); +#else + /* the test cases are repeated to have exactly 6. This is for + * compatibility with IPv4-only machines */ + addrs[3].size = sizeof(struct sockaddr_in); + addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size); + addr = (struct sockaddr_in *) addrs[3].buffer; + addrs[3].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[4].size = sizeof(struct sockaddr_in); + addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size); + addr = (struct sockaddr_in *) addrs[4].buffer; + addrs[4].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); + + addrs[5].size = sizeof(struct sockaddr_in); + addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size); + addr = (struct sockaddr_in *) addrs[5].buffer; + addrs[5].buffer[0] = AF_INET; + ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr); + torture_assert(tctx, ret > 0, "inet_pton failed"); +#endif + + ctr = talloc(tctx, struct netr_DsRAddressToSitenamesExWCtr); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.count = 6; + r.in.addresses = addrs; + r.out.ctr = &ctr; + + status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + if (sam_ctx != NULL) { + for (i = 0; i < 3; i++) { + torture_assert_casestr_equal(tctx, + ctr->sitename[i].string, + server_site_name(tctx, sam_ctx), + "didn't return default site"); + torture_assert(tctx, ctr->subnetname[i].string == NULL, + "subnet should be null"); + } + for (i = 3; i < 6; i++) { + /* Windows returns "NULL" for the sitename if it isn't + * IPv6 configured */ + if (torture_setting_bool(tctx, "samba4", false)) { + torture_assert_casestr_equal(tctx, + ctr->sitename[i].string, + server_site_name(tctx, sam_ctx), + "didn't return default site"); + } + torture_assert(tctx, ctr->subnetname[i].string == NULL, + "subnet should be null"); + } + } + + /* Now try invalid ones (too short buffers) */ + + addrs[0].size = 0; + addrs[1].size = 1; + addrs[2].size = 4; + + addrs[3].size = 0; + addrs[4].size = 1; + addrs[5].size = 4; + + status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + for (i = 0; i < 6; i++) { + torture_assert(tctx, ctr->sitename[i].string == NULL, + "sitename should be null"); + torture_assert(tctx, ctr->subnetname[i].string == NULL, + "subnet should be null"); + } + + addrs[0].size = 10; + addrs[0].buffer[0] = AF_UNSPEC; + addrs[1].size = 10; + addrs[1].buffer[0] = AF_UNIX; /* AF_LOCAL = AF_UNIX */ + addrs[2].size = 10; + addrs[2].buffer[0] = AF_UNIX; + + addrs[3].size = 10; + addrs[3].buffer[0] = 250; + addrs[4].size = 10; + addrs[4].buffer[0] = 251; + addrs[5].size = 10; + addrs[5].buffer[0] = 252; + + status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "failed"); + torture_assert_werr_ok(tctx, r.out.result, "failed"); + + for (i = 0; i < 6; i++) { + torture_assert(tctx, ctr->sitename[i].string == NULL, + "sitename should be null"); + torture_assert(tctx, ctr->subnetname[i].string == NULL, + "subnet should be null"); + } + + return true; +} + +static bool test_netr_ServerGetTrustInfo_flags(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials, + uint32_t negotiate_flags) +{ + struct netr_ServerGetTrustInfo r; + + struct netr_Authenticator a; + struct netr_Authenticator return_authenticator; + struct samr_Password new_owf_password; + struct samr_Password old_owf_password; + struct netr_TrustInfo *trust_info; + + struct netlogon_creds_CredentialState *creds; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Password nt_hash; + + if (!test_SetupCredentials3(p1, tctx, negotiate_flags, + machine_credentials, &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + netlogon_creds_client_authenticator(creds, &a); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + + r.out.return_authenticator = &return_authenticator; + r.out.new_owf_password = &new_owf_password; + r.out.old_owf_password = &old_owf_password; + r.out.trust_info = &trust_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerGetTrustInfo_r(b, tctx, &r), + "ServerGetTrustInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerGetTrustInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &return_authenticator.cred), "Credential chaining failed"); + + E_md4hash(cli_credentials_get_password(machine_credentials), nt_hash.hash); + + netlogon_creds_des_decrypt(creds, &new_owf_password); + + dump_data(1, new_owf_password.hash, 16); + dump_data(1, nt_hash.hash, 16); + + torture_assert_mem_equal(tctx, new_owf_password.hash, nt_hash.hash, 16, + "received unexpected owf password\n"); + + return true; +} + +static bool test_netr_ServerGetTrustInfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + return test_netr_ServerGetTrustInfo_flags(tctx, p, machine_credentials, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_netr_ServerGetTrustInfo_AES(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + return test_netr_ServerGetTrustInfo_flags(tctx, p, machine_credentials, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +static bool test_GetDomainInfo(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + struct netr_LogonGetDomainInfo r; + struct netr_WorkstationInformation q1; + struct netr_Authenticator a; + struct netlogon_creds_CredentialState *creds; + struct netr_OsVersion os; + union netr_WorkstationInfo query; + union netr_DomainInfo info; + const char* const attrs[] = { "dNSHostName", "operatingSystem", + "operatingSystemServicePack", "operatingSystemVersion", + "servicePrincipalName", NULL }; + char *url; + struct ldb_context *sam_ctx = NULL; + struct ldb_message **res; + struct ldb_message_element *spn_el; + int ret, i; + char *version_str; + const char *old_dnsname = NULL; + char **spns = NULL; + int num_spns = 0; + char *temp_str = NULL; + char *temp_str2 = NULL; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + struct netr_OneDomainInfo *odi1 = NULL; + struct netr_OneDomainInfo *odi2 = NULL; + struct netr_trust_extension_info *tex2 = NULL; + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo\n"); + + if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + machine_credentials, &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + /* We won't double-check this when we are over 'local' transports */ + if (dcerpc_server_name(p)) { + /* Set up connection to SAMDB on DC */ + url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p)); + sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, + NULL, + samba_cmdline_get_creds(), + 0); + + torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!"); + } + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 1st call (no variation of DNS hostname)\n"); + netlogon_creds_client_authenticator(creds, &a); + + ZERO_STRUCT(r); + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + r.in.level = 1; + r.in.return_authenticator = &a; + r.in.query = &query; + r.out.return_authenticator = &a; + r.out.info = &info; + + ZERO_STRUCT(os); + os.os.MajorVersion = 123; + os.os.MinorVersion = 456; + os.os.BuildNumber = 789; + os.os.CSDVersion = "Service Pack 10"; + os.os.ServicePackMajor = 10; + os.os.ServicePackMinor = 1; + os.os.SuiteMask = NETR_VER_SUITE_SINGLEUSERTS; + os.os.ProductType = NETR_VER_NT_SERVER; + os.os.Reserved = 0; + + version_str = talloc_asprintf(tctx, "%d.%d (%d)", os.os.MajorVersion, + os.os.MinorVersion, os.os.BuildNumber); + + ZERO_STRUCT(q1); + q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + q1.sitename = "Default-First-Site-Name"; + q1.os_version.os = &os; + q1.os_name.string = talloc_asprintf(tctx, + "Tortured by Samba4 RPC-NETLOGON: %s", + timestring(tctx, time(NULL))); + + /* The workstation handles the "servicePrincipalName" and DNS hostname + updates */ + q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE; + + query.workstation_info = &q1; + + if (sam_ctx) { + /* Gets back the old DNS hostname in AD */ + ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs, + "(sAMAccountName=%s$)", TEST_MACHINE_NAME); + old_dnsname = + ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL); + + /* Gets back the "servicePrincipalName"s in AD */ + spn_el = ldb_msg_find_element(res[0], "servicePrincipalName"); + if (spn_el != NULL) { + for (i=0; i < spn_el->num_values; i++) { + spns = talloc_realloc(tctx, spns, char *, i + 1); + spns[i] = (char *) spn_el->values[i].data; + } + num_spns = i; + } + } + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + smb_msleep(250); + + if (sam_ctx) { + /* AD workstation infos entry check */ + ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs, + "(sAMAccountName=%s$)", TEST_MACHINE_NAME); + torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?"); + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL), + q1.os_name.string, "'operatingSystem' wrong!"); + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL), + os.os.CSDVersion, "'operatingSystemServicePack' wrong!"); + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL), + version_str, "'operatingSystemVersion' wrong!"); + + if (old_dnsname != NULL) { + /* If before a DNS hostname was set then it should remain + the same in combination with the "servicePrincipalName"s. + The DNS hostname should also be returned by our + "LogonGetDomainInfo" call (in the domain info structure). */ + + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL), + old_dnsname, "'DNS hostname' was not set!"); + + spn_el = ldb_msg_find_element(res[0], "servicePrincipalName"); + torture_assert(tctx, ((spns != NULL) && (spn_el != NULL)), + "'servicePrincipalName's not set!"); + torture_assert(tctx, spn_el->num_values == num_spns, + "'servicePrincipalName's incorrect!"); + for (i=0; (i < spn_el->num_values) && (i < num_spns); i++) + torture_assert_str_equal(tctx, + (char *) spn_el->values[i].data, + spns[i], "'servicePrincipalName's incorrect!"); + + torture_assert_str_equal(tctx, + info.domain_info->dns_hostname.string, + old_dnsname, + "Out 'DNS hostname' doesn't match the old one!"); + } else { + /* If no DNS hostname was set then also now none should be set, + the "servicePrincipalName"s should remain empty and no DNS + hostname should be returned by our "LogonGetDomainInfo" + call (in the domain info structure). */ + + torture_assert(tctx, + ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL) == NULL, + "'DNS hostname' was set!"); + + spn_el = ldb_msg_find_element(res[0], "servicePrincipalName"); + torture_assert(tctx, ((spns == NULL) && (spn_el == NULL)), + "'servicePrincipalName's were set!"); + + torture_assert(tctx, + info.domain_info->dns_hostname.string == NULL, + "Out 'DNS host name' was set!"); + } + } + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags + == NETR_WS_FLAG_HANDLES_SPN_UPDATE, + "Out 'workstation flags' don't match!"); + + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 2nd call (variation of DNS hostname doesn't work)\n"); + netlogon_creds_client_authenticator(creds, &a); + + /* Wipe out the CSDVersion, and prove which values still 'stick' */ + os.os.CSDVersion = ""; + + /* Change also the DNS hostname to test differences in behaviour */ + talloc_free(discard_const_p(char, q1.dns_hostname)); + q1.dns_hostname = talloc_asprintf(tctx, "%s2.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + + /* The workstation handles the "servicePrincipalName" and DNS hostname + updates */ + q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + smb_msleep(250); + + if (sam_ctx) { + /* AD workstation infos entry check */ + ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs, + "(sAMAccountName=%s$)", TEST_MACHINE_NAME); + torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?"); + + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL), + q1.os_name.string, "'operatingSystem' should stick!"); + torture_assert(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL) == NULL, + "'operatingSystemServicePack' shouldn't stick!"); + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL), + version_str, "'operatingSystemVersion' wrong!"); + + /* The DNS host name shouldn't have been updated by the server */ + + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL), + old_dnsname, "'DNS host name' did change!"); + + /* Find the two "servicePrincipalName"s which the DC shouldn't have been + updated (HOST/ and HOST/) - see MS-NRPC + 3.5.4.3.9 */ + spn_el = ldb_msg_find_element(res[0], "servicePrincipalName"); + torture_assert(tctx, spn_el != NULL, + "There should exist 'servicePrincipalName's in AD!"); + temp_str = talloc_asprintf(tctx, "HOST/%s", TEST_MACHINE_NAME); + for (i=0; i < spn_el->num_values; i++) + if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0) + break; + torture_assert(tctx, i != spn_el->num_values, + "'servicePrincipalName' HOST/ not found!"); + temp_str = talloc_asprintf(tctx, "HOST/%s", old_dnsname); + for (i=0; i < spn_el->num_values; i++) + if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0) + break; + torture_assert(tctx, i != spn_el->num_values, + "'servicePrincipalName' HOST/ not found!"); + + /* Check that the out DNS hostname was set properly */ + torture_assert_str_equal(tctx, info.domain_info->dns_hostname.string, + old_dnsname, "Out 'DNS hostname' doesn't match the old one!"); + } + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags == NETR_WS_FLAG_HANDLES_SPN_UPDATE, + "Out 'workstation flags' don't match!"); + + + /* Now try the same but the workstation flags set to 0 */ + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 3rd call (variation of DNS hostname doesn't work)\n"); + netlogon_creds_client_authenticator(creds, &a); + + /* Change also the DNS hostname to test differences in behaviour */ + talloc_free(discard_const_p(char, q1.dns_hostname)); + q1.dns_hostname = talloc_asprintf(tctx, "%s2.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + + /* Wipe out the osVersion, and prove which values still 'stick' */ + q1.os_version.os = NULL; + + /* Let the DC handle the "servicePrincipalName" and DNS hostname + updates */ + q1.workstation_flags = 0; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + smb_msleep(250); + + if (sam_ctx) { + /* AD workstation infos entry check */ + ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs, + "(sAMAccountName=%s$)", TEST_MACHINE_NAME); + torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?"); + + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL), + q1.os_name.string, "'operatingSystem' should stick!"); + torture_assert(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL) == NULL, + "'operatingSystemServicePack' shouldn't stick!"); + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL), + version_str, "'operatingSystemVersion' wrong!"); + + /* The DNS host name shouldn't have been updated by the server */ + + torture_assert_str_equal(tctx, + ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL), + old_dnsname, "'DNS host name' did change!"); + + /* Find the two "servicePrincipalName"s which the DC shouldn't have been + updated (HOST/ and HOST/) - see MS-NRPC + 3.5.4.3.9 */ + spn_el = ldb_msg_find_element(res[0], "servicePrincipalName"); + torture_assert(tctx, spn_el != NULL, + "There should exist 'servicePrincipalName's in AD!"); + temp_str = talloc_asprintf(tctx, "HOST/%s", TEST_MACHINE_NAME); + for (i=0; i < spn_el->num_values; i++) + if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0) + break; + torture_assert(tctx, i != spn_el->num_values, + "'servicePrincipalName' HOST/ not found!"); + temp_str = talloc_asprintf(tctx, "HOST/%s", old_dnsname); + for (i=0; i < spn_el->num_values; i++) + if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0) + break; + torture_assert(tctx, i != spn_el->num_values, + "'servicePrincipalName' HOST/ not found!"); + + /* Here the server gives us NULL as the out DNS hostname */ + torture_assert(tctx, info.domain_info->dns_hostname.string == NULL, + "Out 'DNS hostname' should be NULL!"); + } + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags == 0, + "Out 'workstation flags' don't match!"); + + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 4th call (verification of DNS hostname and check for trusted domains)\n"); + netlogon_creds_client_authenticator(creds, &a); + + /* Put the DNS hostname back */ + talloc_free(discard_const_p(char, q1.dns_hostname)); + q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + + /* The workstation handles the "servicePrincipalName" and DNS hostname + updates */ + q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + smb_msleep(250); + + /* Now the in/out DNS hostnames should be the same */ + torture_assert_str_equal(tctx, + info.domain_info->dns_hostname.string, + query.workstation_info->dns_hostname, + "In/Out 'DNS hostnames' don't match!"); + old_dnsname = info.domain_info->dns_hostname.string; + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags + == NETR_WS_FLAG_HANDLES_SPN_UPDATE, + "Out 'workstation flags' don't match!"); + + /* Checks for trusted domains */ + torture_assert(tctx, + (info.domain_info->trusted_domain_count != 0) + && (info.domain_info->trusted_domains != NULL), + "Trusted domains have been requested!"); + + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 5th call (check for trusted domains)\n"); + netlogon_creds_client_authenticator(creds, &a); + + /* The workstation handles the "servicePrincipalName" and DNS hostname + updates and requests inbound trusts */ + q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE + | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + smb_msleep(250); + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags + == (NETR_WS_FLAG_HANDLES_SPN_UPDATE + | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS), + "Out 'workstation flags' don't match!"); + + /* Checks for trusted domains */ + torture_assert(tctx, + (info.domain_info->trusted_domain_count != 0) + && (info.domain_info->trusted_domains != NULL), + "Trusted domains have been requested!"); + + odi1 = &info.domain_info->primary_domain; + + torture_assert(tctx, !GUID_all_zero(&odi1->domain_guid), + "primary domain_guid needs to be valid"); + + for (i=0; i < info.domain_info->trusted_domain_count; i++) { + struct netr_OneDomainInfo *odiT = + &info.domain_info->trusted_domains[i]; + struct netr_trust_extension_info *texT = NULL; + + torture_assert_int_equal(tctx, odiT->trust_extension.length, 16, + "trust_list should have extension"); + torture_assert(tctx, odiT->trust_extension.info != NULL, + "trust_list should have extension"); + texT = &odiT->trust_extension.info->info; + + if (GUID_equal(&odiT->domain_guid, &odi1->domain_guid)) { + odi2 = odiT; + tex2 = texT; + continue; + } + + torture_assert_int_equal(tctx, + texT->flags & NETR_TRUST_FLAG_PRIMARY, + 0, + "trust_list flags should not have PRIMARY"); + + torture_assert(tctx, odiT->domainname.string != NULL, + "trust_list domainname should be valid"); + if (texT->trust_type == LSA_TRUST_TYPE_DOWNLEVEL || + texT->trust_type == LSA_TRUST_TYPE_MIT) + { + torture_assert(tctx, odiT->dns_domainname.string == NULL, + "trust_list dns_domainname should be NULL for downlevel or MIT"); + } else { + torture_assert(tctx, odiT->dns_domainname.string != NULL, + "trust_list dns_domainname should be valid for uplevel"); + } + torture_assert(tctx, odiT->dns_forestname.string == NULL, + "trust_list dns_forestname needs to be NULL"); + + torture_assert(tctx, odiT->domain_sid != NULL, + "trust_list domain_sid needs to be valid"); + } + + torture_assert(tctx, odi2 != NULL, + "trust_list primary domain not found."); + + torture_assert_str_equal(tctx, + odi1->domainname.string, + odi2->domainname.string, + "netbios name should match"); + + temp_str = talloc_strdup(tctx, odi1->dns_domainname.string); + torture_assert(tctx, temp_str != NULL, + "primary_domain dns_domainname copy"); + temp_str2 = strrchr(temp_str, '.'); + torture_assert(tctx, temp_str2 != NULL && temp_str2[1] == '\0', + "primary_domain dns_domainname needs trailing '.'"); + temp_str2[0] = '\0'; + torture_assert_str_equal(tctx, + temp_str, + odi2->dns_domainname.string, + "dns domainname should match " + "(without trailing '.')"); + + temp_str = talloc_strdup(tctx, odi1->dns_forestname.string); + torture_assert(tctx, temp_str != NULL, + "primary_domain dns_forestname copy"); + temp_str2 = strrchr(temp_str, '.'); + torture_assert(tctx, temp_str2 != NULL && temp_str2[1] == '\0', + "primary_domain dns_forestname needs trailing '.'"); + temp_str2[0] = '\0'; + torture_assert(tctx, odi2->dns_forestname.string == NULL, + "trust_list dns_forestname needs to be NULL"); + + torture_assert_guid_equal(tctx, odi1->domain_guid, odi2->domain_guid, + "domain_guid should match"); + torture_assert(tctx, odi1->domain_sid != NULL, + "primary domain_sid needs to be valid"); + torture_assert(tctx, odi2->domain_sid != NULL, + "trust_list domain_sid needs to be valid"); + torture_assert_sid_equal(tctx, odi1->domain_sid, odi2->domain_sid, + "domain_sid should match"); + + torture_assert_int_equal(tctx, odi1->trust_extension.length, 0, + "primary_domain should not have extension"); + torture_assert_int_equal(tctx, odi2->trust_extension.length, 16, + "trust_list should have extension"); + torture_assert(tctx, odi2->trust_extension.info != NULL, + "trust_list should have extension"); + tex2 = &odi2->trust_extension.info->info; + torture_assert_int_equal(tctx, + tex2->flags & NETR_TRUST_FLAG_PRIMARY, + NETR_TRUST_FLAG_PRIMARY, + "trust_list flags should have PRIMARY"); + torture_assert_int_equal(tctx, + tex2->flags & NETR_TRUST_FLAG_IN_FOREST, + NETR_TRUST_FLAG_IN_FOREST, + "trust_list flags should have IN_FOREST"); + torture_assert_int_equal(tctx, + tex2->flags & NETR_TRUST_FLAG_NATIVE, + NETR_TRUST_FLAG_NATIVE, + "trust_list flags should have NATIVE"); + torture_assert_int_equal(tctx, + tex2->flags & ~NETR_TRUST_FLAG_TREEROOT, + NETR_TRUST_FLAG_IN_FOREST | + NETR_TRUST_FLAG_PRIMARY | + NETR_TRUST_FLAG_NATIVE, + "trust_list flags IN_FOREST, PRIMARY, NATIVE " + "(TREEROOT optional)"); + if (strcmp(odi1->dns_domainname.string, odi1->dns_forestname.string) == 0) { + torture_assert_int_equal(tctx, + tex2->flags & NETR_TRUST_FLAG_TREEROOT, + NETR_TRUST_FLAG_TREEROOT, + "trust_list flags TREEROOT on forest root"); + torture_assert_int_equal(tctx, + tex2->parent_index, 0, + "trust_list no parent on forest root"); + } + torture_assert_int_equal(tctx, + tex2->trust_type, LSA_TRUST_TYPE_UPLEVEL, + "trust_list uplevel"); + torture_assert_int_equal(tctx, + tex2->trust_attributes, 0, + "trust_list no attributes"); + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 6th call (no DNS hostname)\n"); + netlogon_creds_client_authenticator(creds, &a); + + query.workstation_info->dns_hostname = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + /* The old DNS hostname should stick */ + torture_assert_str_equal(tctx, + info.domain_info->dns_hostname.string, + old_dnsname, + "'DNS hostname' changed!"); + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 7th call (extra workstation flags)\n"); + netlogon_creds_client_authenticator(creds, &a); + + q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE + | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS | 0x4; + + /* Put the DNS hostname back */ + talloc_free(discard_const_p(char, q1.dns_hostname)); + q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + + /* Checks "workstation flags" */ + torture_assert(tctx, + info.domain_info->workstation_flags + == (NETR_WS_FLAG_HANDLES_SPN_UPDATE + | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS), + "Out 'workstation flags' don't match!"); + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_comment(tctx, "Not testing netr_LogonGetDomainInfo 8th call (no workstation info) - enable dangerous tests in order to do so\n"); + } else { + /* Try a call without the workstation information structure */ + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo 8th call (no workstation info)\n"); + netlogon_creds_client_authenticator(creds, &a); + + query.workstation_info = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r), + "LogonGetDomainInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed"); + torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed"); + } + + return true; +} + +static bool test_GetDomainInfo_async(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_LogonGetDomainInfo r; + struct netr_WorkstationInformation q1; + struct netr_Authenticator a; +#define ASYNC_COUNT 100 + struct netlogon_creds_CredentialState *creds; + struct netlogon_creds_CredentialState *creds_async[ASYNC_COUNT]; + struct tevent_req *req[ASYNC_COUNT]; + int i; + union netr_WorkstationInfo query; + union netr_DomainInfo info; + struct dcerpc_pipe *p = NULL; + + torture_comment(tctx, "Testing netr_LogonGetDomainInfo - async count %d\n", ASYNC_COUNT); + + if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES, + machine_credentials, &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + + ZERO_STRUCT(r); + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + r.in.level = 1; + r.in.return_authenticator = &a; + r.in.query = &query; + r.out.return_authenticator = &a; + r.out.info = &info; + + ZERO_STRUCT(q1); + q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME, + lpcfg_dnsdomain(tctx->lp_ctx)); + q1.sitename = "Default-First-Site-Name"; + q1.os_name.string = "UNIX/Linux or similar"; + + query.workstation_info = &q1; + + for (i=0;iev, p->binding_handle, &r); + + /* even with this flush per request a w2k3 server seems to + clag with multiple outstanding requests. bleergh. */ + torture_assert_int_equal(tctx, tevent_loop_once(tctx->ev), 0, + "tevent_loop_once failed"); + } + + for (i=0;iev), true, + "tevent_req_poll() failed"); + + status = dcerpc_netr_LogonGetDomainInfo_r_recv(req[i], tctx); + + torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo_async"); + torture_assert_ntstatus_ok(tctx, r.out.result, "netr_LogonGetDomainInfo_async"); + + torture_assert(tctx, netlogon_creds_client_check(creds_async[i], &a.cred), + "Credential chaining failed at async"); + } + + torture_comment(tctx, + "Testing netr_LogonGetDomainInfo - async count %d OK\n", ASYNC_COUNT); + + return true; +} + +static bool test_ManyGetDCName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct cli_credentials *anon_creds; + struct dcerpc_binding *binding2; + struct dcerpc_pipe *p2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 o; + struct policy_handle lsa_handle; + struct lsa_DomainList domains; + + struct lsa_EnumTrustDom t; + uint32_t resume_handle = 0; + struct netr_GetAnyDCName d; + const char *dcname = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_binding_handle *b2; + + int i; + + if (p->conn->transport.transport != NCACN_NP) { + torture_skip(tctx, "test_ManyGetDCName works only with NCACN_NP"); + } + + torture_comment(tctx, "Torturing GetDCName\n"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed"); + + binding2 = dcerpc_binding_dup(tctx, p->binding); + /* Swap the binding details from NETLOGON to LSA */ + status = dcerpc_epm_map_binding(tctx, binding2, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx); + dcerpc_binding_set_assoc_group_id(binding2, 0); + torture_assert_ntstatus_ok(tctx, status, "epm map"); + + status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_lsarpc, + anon_creds, tctx->lp_ctx, + tctx, &p2); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + b2 = p2->binding_handle; + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + o.in.system_name = "\\"; + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &lsa_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b2, tctx, &o), + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, o.out.result, "OpenPolicy2 failed"); + + t.in.handle = &lsa_handle; + t.in.resume_handle = &resume_handle; + t.in.max_size = 1000; + t.out.domains = &domains; + t.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b2, tctx, &t), + "EnumTrustDom failed"); + + if ((!NT_STATUS_IS_OK(t.out.result) && + (!NT_STATUS_EQUAL(t.out.result, NT_STATUS_NO_MORE_ENTRIES)))) + torture_fail(tctx, "Could not list domains"); + + talloc_free(p2); + + d.in.logon_server = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + d.out.dcname = &dcname; + + for (i=0; iname.string; + + status = dcerpc_netr_GetAnyDCName_r(b, tctx, &d); + torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName"); + + torture_comment(tctx, "\tDC for domain %s is %s\n", info->name.string, + dcname ? dcname : "unknown"); + } + + return true; +} + +static bool test_lsa_over_netlogon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct cli_credentials *anon_creds; + const struct dcerpc_binding *binding2; + struct dcerpc_pipe *p2; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 o; + struct policy_handle lsa_handle; + + struct dcerpc_binding_handle *b2; + + + if (p->conn->transport.transport != NCACN_NP) { + torture_skip(tctx, "test_lsa_over_netlogon works only with NCACN_NP"); + } + + torture_comment(tctx, "Testing if we can access the LSA server over\n" + " \\\\pipe\\netlogon rather than \\\\pipe\\lsarpc\n"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed"); + + binding2 = p->binding; + + status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_lsarpc, + anon_creds, tctx->lp_ctx, + tctx, &p2); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + b2 = p2->binding_handle; + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + o.in.system_name = "\\"; + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &lsa_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b2, tctx, &o), + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(tctx, o.out.result, "OpenPolicy2 failed"); + + talloc_free(p2); + + return true; +} + +static bool test_SetPassword_with_flags(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + uint32_t flags[] = { 0, NETLOGON_NEG_STRONG_KEYS }; + struct netlogon_creds_CredentialState *creds; + int i; + + if (!test_SetupCredentials2(p, tctx, 0, + machine_credentials, + cli_credentials_get_secure_channel_type(machine_credentials), + &creds)) { + torture_skip(tctx, "DC does not support negotiation of 64bit session keys"); + } + + for (i=0; i < ARRAY_SIZE(flags); i++) { + torture_assert(tctx, + test_SetPassword_flags(tctx, p, machine_credentials, flags[i]), + talloc_asprintf(tctx, "failed to test SetPassword negotiating with 0x%08x flags", flags[i])); + } + + return true; +} + +struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "netlogon"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netlogon", + &ndr_table_netlogon, TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds(tcase, "SetupCredentialsDowngrade", test_SetupCredentialsDowngrade); + torture_rpc_tcase_add_test(tcase, "lsa_over_netlogon", test_lsa_over_netlogon); + + torture_rpc_tcase_add_test_creds(tcase, "GetForestTrustInformation", test_netr_GetForestTrustInformation); + torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo_AES", test_netr_ServerGetTrustInfo_AES); + torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo", test_netr_ServerGetTrustInfo); + torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesExW", test_netr_DsRAddressToSitenamesExW); + torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesW", test_netr_DsRAddressToSitenamesW); + torture_rpc_tcase_add_test(tcase, "DsrGetDcSiteCoverageW", test_netr_DsrGetDcSiteCoverageW); + torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx2", test_netr_DsRGetDCNameEx2); + torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx", test_netr_DsRGetDCNameEx); + torture_rpc_tcase_add_test(tcase, "DsRGetDCName", test_netr_DsRGetDCName); + test = torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo_async", test_GetDomainInfo_async); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomainsEx", test_netr_NetrEnumerateTrustedDomainsEx); + torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains); + torture_rpc_tcase_add_test(tcase, "DsrEnumerateDomainTrusts", test_DsrEnumerateDomainTrusts); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync2", test_DatabaseSync2); + torture_rpc_tcase_add_test(tcase, "GetAnyDCName", test_GetAnyDCName); + torture_rpc_tcase_add_test(tcase, "ManyGetDCName", test_ManyGetDCName); + torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName); + torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync); + torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseRedo", test_DatabaseRedo); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync); + torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo); + torture_rpc_tcase_add_test_creds(tcase, "GetTrustPasswords", test_GetTrustPasswords); + torture_rpc_tcase_add_test_creds(tcase, "GetPassword", test_GetPassword); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuse", test_ServerReqChallengeReuse); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal4", test_ServerReqChallengeReuseGlobal4); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal3", test_ServerReqChallengeReuseGlobal3); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal2", test_ServerReqChallengeReuseGlobal2); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal", test_ServerReqChallengeReuseGlobal); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeGlobal", test_ServerReqChallengeGlobal); + torture_rpc_tcase_add_test_creds(tcase, "invalidAuthenticate2", test_invalidAuthenticate2); + torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon); + torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff); + torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon); + + torture_rpc_tcase_add_test(tcase, "Broken RPC binding handle", + test_netr_broken_binding_handle); + + return suite; +} + +struct torture_suite *torture_rpc_netlogon_s3(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "netlogon-s3"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netlogon", + &ndr_table_netlogon, TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon); + torture_rpc_tcase_add_test_creds(tcase, "SamLogon_NULL_domain", test_SamLogon_NULL_domain); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword_with_flags", test_SetPassword_with_flags); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2); + torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES); + torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains); + + return suite; +} + +struct torture_suite *torture_rpc_netlogon_zerologon(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create( + mem_ctx, + "netlogon.zerologon"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase( + suite, + "netlogon", + &ndr_table_netlogon, + TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge", + test_ServerReqChallenge); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_zero_challenge", + test_ServerReqChallenge_zero_challenge); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_5_repeats", + test_ServerReqChallenge_5_repeats); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_4_repeats", + test_ServerReqChallenge_4_repeats); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_encrypted_to_all_zeros", + test_SetPassword2_encrypted_to_all_zeros); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_password_encrypts_to_zero", + test_SetPassword2_password_encrypts_to_zero); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_confounder", + test_SetPassword2_confounder); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_all_zeros", + test_SetPassword2_all_zeros); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_all_zero_password", + test_SetPassword2_all_zero_password); + torture_rpc_tcase_add_test_creds( + tcase, + "test_SetPassword2_maximum_length_password", + test_SetPassword2_maximum_length_password); + + return suite; +} + +struct torture_suite *torture_rpc_netlogon_admin(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "netlogon.admin"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "bdc", + &ndr_table_netlogon, TEST_MACHINE_NAME); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "wkst", + &ndr_table_netlogon, TEST_MACHINE_NAME); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "admin", + &ndr_table_netlogon); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2); + torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex); + + return suite; +} diff --git a/source4/torture/rpc/netlogon.h b/source4/torture/rpc/netlogon.h new file mode 100644 index 0000000..a4ab8f0 --- /dev/null +++ b/source4/torture/rpc/netlogon.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Bartlett 2008-2009 + Copyright (C) Sumit Bose 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + int sec_chan_type, + struct netlogon_creds_CredentialState **creds_out); + +bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + struct netlogon_creds_CredentialState **creds_out); + +bool test_SetupCredentialsPipe(const struct dcerpc_pipe *p1, + struct torture_context *tctx, + struct cli_credentials *machine_credentials, + struct netlogon_creds_CredentialState *creds, + uint32_t additional_flags, + struct dcerpc_pipe **_p2); diff --git a/source4/torture/rpc/netlogon_crypto.c b/source4/torture/rpc/netlogon_crypto.c new file mode 100644 index 0000000..8584460 --- /dev/null +++ b/source4/torture/rpc/netlogon_crypto.c @@ -0,0 +1,274 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2003-2004 + Copyright (C) Tim Potter 2003 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/replace/system/network.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "param/param.h" +#include "lib/param/loadparm.h" +#include "libcli/security/security.h" + +#undef strcasecmp + +#define TEST_MACHINE_NAME "torturetest" + +static bool test_ServerAuth3Crypto(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + bool force_client_rc4) +{ + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential netr_creds1 = { + .data = {0}, + }; + struct netr_Credential netr_creds2 = { + .data = {0}, + }; + struct netr_Credential netr_creds3 = { + .data = {0}, + }; + struct netlogon_creds_CredentialState *creds_state = NULL; + struct samr_Password machine_password = { + .hash = {0}, + }; + const char *machine_name = NULL; + const char *plain_pass = NULL; + struct dcerpc_binding_handle *b = NULL; + uint32_t rid = 0; + NTSTATUS status; + bool weak_crypto_allowed = + (lpcfg_weak_crypto(tctx->lp_ctx) == + SAMBA_WEAK_CRYPTO_ALLOWED); + + if (p == NULL) { + return false; + } + b = p->binding_handle; + + ZERO_STRUCT(r); + ZERO_STRUCT(a); + + torture_comment(tctx, "client negotiate_flags=0x%08x\n", negotiate_flags); + + machine_name = cli_credentials_get_workstation(machine_credentials); + torture_assert_not_null(tctx, machine_name, "machine name is not set"); + + plain_pass = cli_credentials_get_password(machine_credentials); + torture_assert_not_null(tctx, plain_pass, "plain_pass is not set"); + + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &netr_creds1; + r.out.return_credentials = &netr_creds2; + + netlogon_creds_random_challenge(&netr_creds1); + + status = dcerpc_netr_ServerReqChallenge_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, + status, + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, + r.out.result, + "ServerReqChallenge failed"); + + E_md4hash(plain_pass, machine_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = + cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &netr_creds3; + a.out.return_credentials = &netr_creds3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + if (force_client_rc4) { + GNUTLS_FIPS140_SET_LAX_MODE(); + } + creds_state = netlogon_creds_client_init(tctx, + a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &netr_creds1, + &netr_creds2, + &machine_password, + &netr_creds3, + negotiate_flags); + GNUTLS_FIPS140_SET_STRICT_MODE(); + /* Test that we fail to encrypt with RC4 */ + if (creds_state == NULL && + !weak_crypto_allowed && !force_client_rc4 && + (negotiate_flags & NETLOGON_NEG_ARCFOUR)) { + return false; + } + torture_assert_not_null(tctx, + creds_state, + "Failed init netlogon client creds"); + + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + status = dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a); + torture_assert_ntstatus_ok(tctx, + status, + "ServerAuthenticate3 failed"); + + /* Check that the server denies RC4 */ + if (!NT_STATUS_IS_OK(a.out.result) && + !weak_crypto_allowed && + force_client_rc4) { + torture_assert_ntstatus_equal(tctx, + a.out.result, + NT_STATUS_DOWNGRADE_DETECTED, + "Unexpected status code"); + return false; + } + torture_assert_ntstatus_ok(tctx, + a.out.result, + "ServerAuthenticate3 failed"); + torture_assert(tctx, + netlogon_creds_client_check(creds_state, &netr_creds3), + "Credential chaining failed"); + + torture_comment(tctx, + "server negotiate_flags=0x%08x\n", + negotiate_flags); + + if (!weak_crypto_allowed) { + torture_assert(tctx, + (negotiate_flags & NETLOGON_NEG_ARCFOUR) == 0, + "Server should not announce RC4 support"); + } + + /* Prove that requesting a challenge again won't break it */ + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); + + return true; +} + + +/* Test that we can successfully authenticate using AES. */ +static bool test_AES_Crypto(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + uint32_t negotiate_flags = + NETLOGON_NEG_AUTH2_ADS_FLAGS| + NETLOGON_NEG_SUPPORTS_AES; + bool ok; + + ok = test_ServerAuth3Crypto(p, + tctx, + negotiate_flags, + machine_credentials, + false); + if (!ok) { + return false; + } + + return true; +} + +/* If we try to use RC4, the client code should fail to encrypt. */ +static bool test_RC4_Crypto_Fail(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + uint32_t negotiate_flags = + NETLOGON_NEG_AUTH2_ADS_FLAGS| + NETLOGON_NEG_ARCFOUR; + bool ok; + + ok = test_ServerAuth3Crypto(p, + tctx, + negotiate_flags, + machine_credentials, + false); + if (!ok) { + return true; + } + + return false; +} + +/* + * Enforce the use of RC4 and try to authenticate. The server should fail + * in this case as it doesn't allow RC4 + */ +static bool test_RC4_Crypto_Force(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + uint32_t negotiate_flags = + NETLOGON_NEG_AUTH2_ADS_FLAGS| + NETLOGON_NEG_ARCFOUR; + bool ok; + + ok = test_ServerAuth3Crypto(p, + tctx, + negotiate_flags, + machine_credentials, + true); + if (!ok) { + return true; + } + + return false; +} + +struct torture_suite *torture_rpc_netlogon_crypto_fips(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "fips.netlogon.crypto"); + struct torture_rpc_tcase *tcase = NULL; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, + "netlogon", + &ndr_table_netlogon, + TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds(tcase, + "test_AES_Crytpo", + test_AES_Crypto); + torture_rpc_tcase_add_test_creds(tcase, + "test_RC4_Crytpo_Fail", + test_RC4_Crypto_Fail); + torture_rpc_tcase_add_test_creds(tcase, + "test_RC4_Crytpo_Force", + test_RC4_Crypto_Force); + + return suite; +} diff --git a/source4/torture/rpc/ntsvcs.c b/source4/torture/rpc/ntsvcs.c new file mode 100644 index 0000000..a25129d --- /dev/null +++ b/source4/torture/rpc/ntsvcs.c @@ -0,0 +1,189 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc ntsvcs operations + + Copyright (C) Guenther Deschner 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_ntsvcs_c.h" + +static bool test_PNP_GetVersion(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + struct PNP_GetVersion r; + uint16_t version = 0; + + r.out.version = &version; + + status = dcerpc_PNP_GetVersion_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "PNP_GetVersion"); + torture_assert_werr_ok(tctx, r.out.result, "PNP_GetVersion"); + torture_assert_int_equal(tctx, version, 0x400, "invalid version"); + + return true; +} + +static bool test_PNP_GetDeviceListSize(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct PNP_GetDeviceListSize r; + uint32_t size = 0; + + r.in.devicename = NULL; + r.in.flags = CM_GETIDLIST_FILTER_SERVICE; + r.out.size = &size; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceListSize_r(b, tctx, &r), + "PNP_GetDeviceListSize"); + torture_assert_werr_equal(tctx, r.out.result, WERR_CM_INVALID_POINTER, + "PNP_GetDeviceListSize"); + + r.in.devicename = "Spooler"; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceListSize_r(b, tctx, &r), + "PNP_GetDeviceListSize"); + torture_assert_werr_ok(tctx, r.out.result, + "PNP_GetDeviceListSize"); + + return true; +} + +static bool test_PNP_GetDeviceList(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct PNP_GetDeviceList r; + uint16_t *buffer = NULL; + uint32_t length = 0; + + buffer = talloc_array(tctx, uint16_t, 0); + + r.in.filter = NULL; + r.in.flags = CM_GETIDLIST_FILTER_SERVICE; + r.in.length = &length; + r.out.length = &length; + r.out.buffer = buffer; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceList_r(b, tctx, &r), + "PNP_GetDeviceList failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_CM_INVALID_POINTER, + "PNP_GetDeviceList failed"); + + r.in.filter = "Spooler"; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceList_r(b, tctx, &r), + "PNP_GetDeviceList failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) { + struct PNP_GetDeviceListSize s; + + s.in.devicename = "Spooler"; + s.in.flags = CM_GETIDLIST_FILTER_SERVICE; + s.out.size = &length; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceListSize_r(b, tctx, &s), + "PNP_GetDeviceListSize failed"); + torture_assert_werr_ok(tctx, s.out.result, + "PNP_GetDeviceListSize failed"); + } + + buffer = talloc_array(tctx, uint16_t, length); + + r.in.length = &length; + r.out.length = &length; + r.out.buffer = buffer; + + torture_assert_ntstatus_ok(tctx, + dcerpc_PNP_GetDeviceList_r(b, tctx, &r), + "PNP_GetDeviceList failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "PNP_GetDeviceList failed"); + + return true; +} + +static bool test_PNP_GetDeviceRegProp(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + struct PNP_GetDeviceRegProp r; + + enum winreg_Type reg_data_type = REG_NONE; + uint32_t buffer_size = 0; + uint32_t needed = 0; + uint8_t *buffer; + + buffer = talloc(tctx, uint8_t); + + r.in.devicepath = "ACPI\\ACPI0003\\1"; + r.in.property = DEV_REGPROP_DESC; + r.in.flags = 0; + r.in.reg_data_type = ®_data_type; + r.in.buffer_size = &buffer_size; + r.in.needed = &needed; + r.out.buffer = buffer; + r.out.reg_data_type = ®_data_type; + r.out.buffer_size = &buffer_size; + r.out.needed = &needed; + + status = dcerpc_PNP_GetDeviceRegProp_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp"); + + if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) { + + buffer = talloc_array(tctx, uint8_t, needed); + r.in.buffer_size = &needed; + + status = dcerpc_PNP_GetDeviceRegProp_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp"); + } + + return true; +} + +struct torture_suite *torture_rpc_ntsvcs(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "ntsvcs"); + + tcase = torture_suite_add_rpc_iface_tcase(suite, "ntsvcs", + &ndr_table_ntsvcs); + + torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceRegProp", + test_PNP_GetDeviceRegProp); + torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceList", + test_PNP_GetDeviceList); + torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceListSize", + test_PNP_GetDeviceListSize); + torture_rpc_tcase_add_test(tcase, "PNP_GetVersion", + test_PNP_GetVersion); + + return suite; +} diff --git a/source4/torture/rpc/object_uuid.c b/source4/torture/rpc/object_uuid.c new file mode 100644 index 0000000..2209954 --- /dev/null +++ b/source4/torture/rpc/object_uuid.c @@ -0,0 +1,85 @@ +/* + Unix SMB/CIFS implementation. + + test suite for behaviour of object uuids in rpc requests + + Copyright (C) Stefan Metzmacher 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_dssetup.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "torture/rpc/torture_rpc.h" + +/* + this tests the send object uuids in the dcerpc request +*/ + +static bool test_random_uuid(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct GUID uuid; + struct dssetup_DsRoleGetPrimaryDomainInformation r1; + struct lsa_GetUserName r2; + struct lsa_String *authority_name_p = NULL; + struct lsa_String *account_name_p = NULL; + + torture_comment(torture, "RPC-OBJECTUUID-RANDOM\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_dssetup); + torture_assert_ntstatus_ok(torture, status, "opening dsetup pipe1"); + + status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + + uuid = GUID_random(); + + r1.in.level = DS_ROLE_BASIC_INFORMATION; + status = dcerpc_binding_handle_call(p1->binding_handle, + &uuid, + &ndr_table_dssetup, + NDR_DSSETUP_DSROLEGETPRIMARYDOMAININFORMATION, + torture, &r1); + torture_assert_ntstatus_ok(torture, status, "DsRoleGetPrimaryDomainInformation failed"); + torture_assert_werr_ok(torture, r1.out.result, "DsRoleGetPrimaryDomainInformation failed"); + + uuid = GUID_random(); + + r2.in.system_name = "\\"; + r2.in.account_name = &account_name_p; + r2.in.authority_name = &authority_name_p; + r2.out.account_name = &account_name_p; + r2.out.authority_name = &authority_name_p; + + status = dcerpc_binding_handle_call(p2->binding_handle, + &uuid, + &ndr_table_lsarpc, + NDR_LSA_GETUSERNAME, + torture, &r2); + torture_assert_ntstatus_ok(torture, status, "lsaClose failed"); + torture_assert_ntstatus_ok(torture, r2.out.result, "lsaClose failed"); + + return true; +} + +struct torture_suite *torture_rpc_object_uuid(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + suite = torture_suite_create(mem_ctx, "objectuuid"); + torture_suite_add_simple_test(suite, "random-uuid", test_random_uuid); + return suite; +} diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c new file mode 100644 index 0000000..8f4ee2b --- /dev/null +++ b/source4/torture/rpc/remote_pac.c @@ -0,0 +1,1425 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon PAC operations + + Copyright (C) Andrew Bartlett 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/auth_sam_reply.h" +#include "auth/gensec/gensec.h" +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "param/param.h" +#include +#include "ldb_wrap.h" +#include "dsdb/samdb/samdb.h" + +#define TEST_MACHINE_NAME_BDC "torturepacbdc" +#define TEST_MACHINE_NAME_WKSTA "torturepacwksta" +#define TEST_MACHINE_NAME_S4U2SELF_BDC "tests4u2selfbdc" +#define TEST_MACHINE_NAME_S4U2SELF_WKSTA "tests4u2selfwk" +#define TEST_MACHINE_NAME_S4U2PROXY_WKSTA "tests4u2proxywk" + +struct pac_data { + DATA_BLOB pac_blob; + struct PAC_SIGNATURE_DATA *pac_srv_sig; + struct PAC_SIGNATURE_DATA *pac_kdc_sig; +}; + +/* A helper function which avoids touching the local databases to + * generate the session info, as we just want to verify the PAC + * details, not the full local token */ +static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *principal_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct auth_user_info_dc *user_info_dc; + TALLOC_CTX *tmp_ctx; + struct pac_data *pac_data; + + if (pac_blob == NULL) { + DBG_ERR("pac_blob missing\n"); + return NT_STATUS_NO_IMPERSONATION_TOKEN; + } + + tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data); + + pac_data->pac_blob = data_blob_dup_talloc(pac_data, *pac_blob); + if (pac_data->pac_blob.length != pac_blob->length) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + pac_data->pac_srv_sig = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA); + if (!pac_data->pac_srv_sig) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + pac_data->pac_kdc_sig = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA); + if (!pac_data->pac_kdc_sig) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx, + *pac_blob, + smb_krb5_context->krb5_context, + &user_info_dc, + pac_data->pac_srv_sig, + pac_data->pac_kdc_sig); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + talloc_steal(pac_data, pac_data->pac_srv_sig); + talloc_steal(pac_data, pac_data->pac_kdc_sig); + + if (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + nt_status = auth_generate_session_info(mem_ctx, + NULL, + NULL, + user_info_dc, session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + talloc_free(tmp_ctx); + return nt_status; +} + +/* Check to see if we can pass the PAC across to the NETLOGON server for validation */ + +static const struct PAC_BUFFER *get_pac_buffer(const struct PAC_DATA *pac_data, + enum PAC_TYPE type) +{ + const struct PAC_BUFFER *pac_buf = NULL; + uint32_t i; + + for (i = 0; i < pac_data->num_buffers; ++i) { + pac_buf = &pac_data->buffers[i]; + + if (pac_buf->type == type) { + break; + } + } + + return pac_buf; +} + +/* Also happens to be a really good one-step verification of our Kerberos stack */ + +static bool netlogon_validate_pac(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *server_creds, + enum netr_SchannelType secure_channel_type, + const char *test_machine_name, + uint32_t negotiate_flags, + struct pac_data *pac_data, + struct auth_session_info *session_info); + +static bool test_PACVerify(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials, + enum netr_SchannelType secure_channel_type, + const char *test_machine_name, + uint32_t negotiate_flags) +{ + NTSTATUS status; + bool ok; + const char *pkinit_ccache = torture_setting_string(tctx, "pkinit_ccache", NULL); + bool pkinit_in_use = pkinit_ccache != NULL; + bool expect_pac_upn_dns_info = torture_setting_bool(tctx, "expect_pac_upn_dns_info", true); + size_t num_pac_buffers; + struct gensec_security *gensec_client_context; + struct gensec_security *gensec_server_context; + struct cli_credentials *client_creds; + struct cli_credentials *server_creds; + + DATA_BLOB client_to_server, server_to_client; + struct PAC_DATA pac_data_struct; + enum ndr_err_code ndr_err; + + struct auth4_context *auth_context; + struct auth_session_info *session_info; + struct pac_data *pac_data; + const struct PAC_BUFFER *pac_buf = NULL; + + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + torture_comment(tctx, + "Testing PAC Verify (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n", + secure_channel_type, test_machine_name, negotiate_flags); + + if (pkinit_in_use) { + struct cli_credentials *tmp_creds = NULL; + const char *error_string = NULL; + int rc; + + torture_comment(tctx, + "Using pkinit_ccache=%s\n", + pkinit_ccache); + + tmp_creds = cli_credentials_init(tctx); + torture_assert(tctx, tmp_creds, "Failed to create credentials"); + + rc = cli_credentials_set_ccache(tmp_creds, + tctx->lp_ctx, + pkinit_ccache, + CRED_SPECIFIED, + &error_string); + torture_assert_int_equal(tctx, + rc, + 0, + "cli_credentials_set_ccache failed"); + cli_credentials_set_kerberos_state(tmp_creds, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + + /* + * Copy the credentials in order to use a different MEMORY krb5 + * ccache for each client/server setup. The MEMORY cache + * identifier is a pointer to the creds container. If we copy + * it the pointer changes and we will get a new clean memory + * cache. + */ + client_creds = + cli_credentials_shallow_copy(tmp_ctx, tmp_creds); + torture_assert(tctx, + client_creds, + "Failed to copy of credentials"); + } else { + /* + * Copy the credentials in order to use a different MEMORY krb5 + * ccache for each client/server setup. The MEMORY cache + * identifier is a pointer to the creds container. If we copy + * it the pointer changes and we will get a new clean memory + * cache. + */ + client_creds = + cli_credentials_shallow_copy(tmp_ctx, + samba_cmdline_get_creds()); + torture_assert(tctx, + client_creds, + "Failed to copy of credentials"); + cli_credentials_invalidate_ccache(client_creds, CRED_SPECIFIED); + } + + + server_creds = cli_credentials_shallow_copy(tmp_ctx, + credentials); + torture_assert(tctx, server_creds, "Failed to copy of credentials"); + + auth_context = talloc_zero(tmp_ctx, struct auth4_context); + torture_assert(tctx, auth_context != NULL, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_hostname(gensec_client_context, test_machine_name); + + status = gensec_set_credentials(gensec_client_context, client_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (NT_STATUS_IS_OK(status)) { + break; + } + } while (1); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_data = talloc_get_type(auth_context->private_data, struct pac_data); + + torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context"); + torture_assert(tctx, pac_data->pac_srv_sig != NULL, "pac_srv_sig not present"); + torture_assert(tctx, pac_data->pac_kdc_sig != NULL, "pac_kdc_sig not present"); + + ndr_err = ndr_pull_struct_blob(&pac_data->pac_blob, tmp_ctx, &pac_data_struct, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); + + num_pac_buffers = 7; + if (expect_pac_upn_dns_info) { + num_pac_buffers += 1; + } + if (pkinit_in_use) { + num_pac_buffers += 1; + } + + torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version"); + torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_INFO"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_LOGON_INFO info"); + + if (pkinit_in_use) { + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CREDENTIAL_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CREDENTIAL_INFO"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_CREDENTIAL_INFO info"); + } + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_NAME); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_NAME"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_LOGON_NAME info"); + + if (expect_pac_upn_dns_info) { + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_UPN_DNS_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_UPN_DNS_INFO"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_UPN_DNS_INFO info"); + } + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_SRV_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_SRV_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_SRV_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_KDC_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_KDC_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_KDC_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_TICKET_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_FULL_CHECKSUM info"); + + ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, + negotiate_flags, pac_data, session_info); + + talloc_free(tmp_ctx); + + return ok; +} + +static bool netlogon_validate_pac(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *server_creds, + enum netr_SchannelType secure_channel_type, + const char *test_machine_name, + uint32_t negotiate_flags, + struct pac_data *pac_data, + struct auth_session_info *session_info) +{ + struct PAC_Validate pac_wrapped_struct; + struct netlogon_creds_CredentialState *creds = NULL; + struct netr_Authenticator return_authenticator; + struct netr_Authenticator auth, auth2; + struct netr_GenericInfo generic; + struct netr_LogonSamLogon r; + union netr_Validation validation; + union netr_LogonLevel logon; + uint8_t authoritative; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + enum ndr_err_code ndr_err; + DATA_BLOB payload, pac_wrapped; + + if (!test_SetupCredentials2(p1, tctx, negotiate_flags, + server_creds, secure_channel_type, + &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, server_creds, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length; + pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type; + pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length; + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + pac_data->pac_srv_sig->signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + pac_data->pac_kdc_sig->signature.data, + pac_wrapped_struct.SignatureLength); + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tctx, &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length); + } else { + netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + } + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + /* Validate it over the netlogon pipe */ + + generic.identity_info.parameter_control = 0; + generic.identity_info.logon_id = 0; + generic.identity_info.domain_name.string = session_info->info->domain_name; + generic.identity_info.account_name.string = session_info->info->account_name; + generic.identity_info.workstation.string = test_machine_name; + + generic.package_name.string = "Kerberos"; + + logon.generic = &generic; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon = &logon; + r.in.logon_level = NetlogonGenericInformation; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.validation_level = NetlogonValidationGenericInfo2; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + r.out.return_authenticator = &return_authenticator; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); + + /* This will break the signature nicely (even in the crypto wrapping), check we get a logon failure */ + generic.data[generic.length-1]++; + + logon.generic = &generic; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon = &logon; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.validation_level = NetlogonValidationGenericInfo2; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + /* This will break the parsing nicely (even in the crypto wrapping), check we get INVALID_PARAMETER */ + generic.length--; + + logon.generic = &generic; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon = &logon; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.validation_level = NetlogonValidationGenericInfo2; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + + pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length; + pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type; + + /* Break the SignatureType */ + pac_wrapped_struct.SignatureType++; + + pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length; + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + pac_data->pac_srv_sig->signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + pac_data->pac_kdc_sig->signature.data, + pac_wrapped_struct.SignatureLength); + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tctx, &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length); + } else { + netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + } + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + logon.generic = &generic; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon = &logon; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.validation_level = NetlogonValidationGenericInfo2; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length; + pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type; + pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length; + + pac_wrapped_struct.ChecksumAndSignature = payload + = data_blob_talloc(tctx, NULL, + pac_wrapped_struct.ChecksumLength + + pac_wrapped_struct.SignatureLength); + memcpy(&payload.data[0], + pac_data->pac_srv_sig->signature.data, + pac_wrapped_struct.ChecksumLength); + memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], + pac_data->pac_kdc_sig->signature.data, + pac_wrapped_struct.SignatureLength); + + /* Break the signature length */ + pac_wrapped_struct.SignatureLength++; + + ndr_err = ndr_push_struct_blob(&pac_wrapped, tctx, &pac_wrapped_struct, + (ndr_push_flags_fn_t)ndr_push_PAC_Validate); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); + + torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length); + } else { + netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); + } + + generic.length = pac_wrapped.length; + generic.data = pac_wrapped.data; + + logon.generic = &generic; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonGenericInformation; + r.in.logon = &logon; + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.validation_level = NetlogonValidationGenericInfo2; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r), + "LogonSamLogon failed"); + + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), + "Credential chaining failed"); + + return true; +} + +static bool test_PACVerify_bdc_arcfour(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_PACVerify(tctx, p, credentials, SEC_CHAN_BDC, + TEST_MACHINE_NAME_BDC, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_PACVerify_bdc_aes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_PACVerify(tctx, p, credentials, SEC_CHAN_BDC, + TEST_MACHINE_NAME_BDC, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +static bool test_PACVerify_workstation_arcfour(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_PACVerify(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_PACVerify_workstation_aes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_PACVerify(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +#ifdef SAMBA4_USES_HEIMDAL +static NTSTATUS check_primary_group_in_validation(TALLOC_CTX *mem_ctx, + uint16_t validation_level, + const union netr_Validation *validation) +{ + const struct netr_SamBaseInfo *base = NULL; + int i; + switch (validation_level) { + case 2: + if (!validation || !validation->sam2) { + return NT_STATUS_INVALID_PARAMETER; + } + base = &validation->sam2->base; + break; + case 3: + if (!validation || !validation->sam3) { + return NT_STATUS_INVALID_PARAMETER; + } + base = &validation->sam3->base; + break; + case 6: + if (!validation || !validation->sam6) { + return NT_STATUS_INVALID_PARAMETER; + } + base = &validation->sam6->base; + break; + default: + return NT_STATUS_INVALID_LEVEL; + } + + for (i = 0; i < base->groups.count; i++) { + if (base->groups.rids[i].rid == base->primary_gid) { + return NT_STATUS_OK; + } + } + return NT_STATUS_INVALID_PARAMETER; +} + +/* Check various ways to get the PAC, in particular check the group membership and + * other details between the PAC from a normal kinit, S4U2Self and a SamLogon */ +static bool test_S4U2Self(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *credentials, + enum netr_SchannelType secure_channel_type, + const char *test_machine_name, + uint32_t negotiate_flags) +{ + NTSTATUS status; + struct dcerpc_pipe *p = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct netr_LogonSamLogon r; + + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative; + + struct netr_Authenticator auth, auth2; + + DATA_BLOB client_to_server, server_to_client; + + struct netlogon_creds_CredentialState *creds; + struct gensec_security *gensec_client_context; + struct gensec_security *gensec_server_context; + struct cli_credentials *client_creds; + struct cli_credentials *server_creds; + + struct auth4_context *auth_context; + struct auth_session_info *kinit_session_info; + struct auth_session_info *s4u2self_session_info; + struct auth_user_info_dc *netlogon_user_info_dc; + + struct netr_NetworkInfo ninfo = {}; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + size_t i; + size_t j; + size_t k; + int flags = CLI_CRED_NTLMv2_AUTH; + + struct dom_sid *builtin_domain; + + struct dom_sid *ai_auth_authority = NULL; + struct dom_sid *ai_service = NULL; + struct dom_sid *ai_claims_valid = NULL; + size_t ai_auth_authority_count = 0; + size_t ai_service_count = 0; + size_t ai_claims_valid_count = 0; + size_t kinit_asserted_identity_index = 0; + size_t kinit_claims_valid_index = 0; + size_t s4u2self_asserted_identity_index = 0; + size_t s4u2self_claims_valid_index = 0; + bool ok; + + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + torture_comment(tctx, + "Testing S4U2SELF (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n", + secure_channel_type, test_machine_name, negotiate_flags); + + /* + * Copy the credentials in order to use a different MEMORY krb5 ccache + * for each client/server setup. The MEMORY cache identifier is a + * pointer to the creds container. If we copy it the pointer changes and + * we will get a new clean memory cache. + */ + client_creds = cli_credentials_shallow_copy(tmp_ctx, + samba_cmdline_get_creds()); + torture_assert(tctx, client_creds, "Failed to copy of credentials"); + /* We use cli_credentials_get_ntlm_response(), so relax krb5 requirements. */ + cli_credentials_set_kerberos_state(client_creds, + CRED_USE_KERBEROS_DESIRED, + CRED_SPECIFIED); + + server_creds = cli_credentials_shallow_copy(tmp_ctx, + credentials); + torture_assert(tctx, server_creds, "Failed to copy of credentials"); + + if (!test_SetupCredentials2(p1, tctx, negotiate_flags, + server_creds, secure_channel_type, + &creds)) { + return false; + } + if (!test_SetupCredentialsPipe(p1, tctx, server_creds, creds, + DCERPC_SIGN | DCERPC_SEAL, &p)) { + return false; + } + b = p->binding_handle; + + auth_context = talloc_zero(tmp_ctx, struct auth4_context); + torture_assert(tctx, auth_context != NULL, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + /* First, do a normal Kerberos connection */ + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_hostname(gensec_client_context, test_machine_name); + + status = gensec_set_credentials(gensec_client_context, client_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (NT_STATUS_IS_OK(status)) { + break; + } + } while (1); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &kinit_session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + + /* Now do the dance with S4U2Self */ + + /* Wipe out any existing ccache */ + cli_credentials_invalidate_ccache(client_creds, CRED_SPECIFIED); + cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED); + cli_credentials_set_impersonate_principal(server_creds, + cli_credentials_get_principal(client_creds, tmp_ctx), + talloc_asprintf(tmp_ctx, "host/%s", test_machine_name)); + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_hostname(gensec_client_context, test_machine_name); + + /* We now set the same credentials on both client and server contexts */ + status = gensec_set_credentials(gensec_client_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (NT_STATUS_IS_OK(status)) { + break; + } + } while (1); + + /* Don't pollute the remaining tests with the changed credentials */ + cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED); + cli_credentials_set_target_service(server_creds, NULL); + cli_credentials_set_impersonate_principal(server_creds, NULL, NULL); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &s4u2self_session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + cli_credentials_get_ntlm_username_domain(client_creds, tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + /* Now try with SamLogon */ + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(server_creds), + cli_credentials_get_domain(server_creds)); + + status = cli_credentials_get_ntlm_response(client_creds, tctx, + &flags, + chal, + NULL, /* server_timestamp */ + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(server_creds); + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(server_creds); + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonNetworkInformation; + r.in.logon = &logon; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + + r.in.validation_level = 3; + + status = dcerpc_netr_LogonSamLogon_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); + + torture_assert(tctx, netlogon_creds_client_check(creds, + &r.out.return_authenticator->cred), + "Credential chaining failed"); + + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); + + status = make_user_info_dc_netlogon_validation(tmp_ctx, + ninfo.identity_info.account_name.string, + r.in.validation_level, + r.out.validation, + true, /* This user was authenticated */ + &netlogon_user_info_dc); + + torture_assert_ntstatus_ok(tctx, status, "make_user_info_dc_netlogon_validation failed"); + + /* Check that the primary group is present in validation's RID array */ + status = check_primary_group_in_validation(tmp_ctx, r.in.validation_level, r.out.validation); + torture_assert_ntstatus_ok(tctx, status, "check_primary_group_in_validation failed"); + + torture_assert_str_equal(tctx, netlogon_user_info_dc->info->account_name == NULL ? "" : netlogon_user_info_dc->info->account_name, + kinit_session_info->info->account_name, "Account name differs for kinit-based PAC"); + torture_assert_str_equal(tctx,netlogon_user_info_dc->info->account_name == NULL ? "" : netlogon_user_info_dc->info->account_name, + s4u2self_session_info->info->account_name, "Account name differs for S4U2Self"); + torture_assert_str_equal(tctx, netlogon_user_info_dc->info->full_name == NULL ? "" : netlogon_user_info_dc->info->full_name, kinit_session_info->info->full_name, "Full name differs for kinit-based PAC"); + torture_assert_str_equal(tctx, netlogon_user_info_dc->info->full_name == NULL ? "" : netlogon_user_info_dc->info->full_name, s4u2self_session_info->info->full_name, "Full name differs for S4U2Self"); + + builtin_domain = dom_sid_parse_talloc(tmp_ctx, SID_BUILTIN); + torture_assert_not_null(tctx, builtin_domain, "failed to parse SID"); + + /* KRB5 might have an additional sid, the asserted identity */ + ai_auth_authority = dom_sid_parse_talloc( + tmp_ctx, + SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY); + torture_assert_not_null(tctx, ai_auth_authority, "failed to parse SID"); + + ai_service = dom_sid_parse_talloc( + tmp_ctx, + SID_SERVICE_ASSERTED_IDENTITY); + torture_assert_not_null(tctx, ai_service, "failed to parse SID"); + + /* ...and the Claims Valid SID. */ + ai_claims_valid = dom_sid_parse_talloc( + tmp_ctx, + SID_CLAIMS_VALID); + torture_assert_not_null(tctx, ai_claims_valid, "failed to parse SID"); + + ai_auth_authority_count = 0; + ai_service_count = 0; + ai_claims_valid_count = 0; + for (i = 0; i < kinit_session_info->torture->num_dc_sids; i++) { + ok = dom_sid_equal(&kinit_session_info->torture->dc_sids[i].sid, + ai_auth_authority); + if (ok) { + ai_auth_authority_count++; + kinit_asserted_identity_index = i; + } + + ok = dom_sid_equal(&kinit_session_info->torture->dc_sids[i].sid, + ai_service); + if (ok) { + ai_service_count++; + kinit_asserted_identity_index = i; + } + + ok = dom_sid_equal(&kinit_session_info->torture->dc_sids[i].sid, + ai_claims_valid); + if (ok) { + ai_claims_valid_count++; + kinit_claims_valid_index = i; + } + } + + torture_assert_int_equal(tctx, ai_auth_authority_count, 1, + "Kinit authority asserted identity should be (1)"); + torture_assert_int_equal(tctx, ai_service_count, 0, + "Kinit service asserted identity should be (0)"); + torture_assert_int_equal(tctx, ai_claims_valid_count, 1, + "Kinit Claims Valid should be (1)"); + + ai_auth_authority_count = 0; + ai_service_count = 0; + ai_claims_valid_count = 0; + for (i = 0; i < s4u2self_session_info->torture->num_dc_sids; i++) { + ok = dom_sid_equal(&s4u2self_session_info->torture->dc_sids[i].sid, + ai_auth_authority); + if (ok) { + ai_auth_authority_count++; + s4u2self_asserted_identity_index = i; + } + + ok = dom_sid_equal(&s4u2self_session_info->torture->dc_sids[i].sid, + ai_service); + if (ok) { + ai_service_count++; + s4u2self_asserted_identity_index = i; + } + + ok = dom_sid_equal(&s4u2self_session_info->torture->dc_sids[i].sid, + ai_claims_valid); + if (ok) { + ai_claims_valid_count++; + s4u2self_claims_valid_index = i; + } + } + + torture_assert_int_equal(tctx, ai_auth_authority_count, 0, + "S4U2Self authority asserted identity should be (0)"); + torture_assert_int_equal(tctx, ai_service_count, 1, + "S4U2Self service asserted identity should be (1)"); + torture_assert_int_equal(tctx, ai_claims_valid_count, 1, + "S4U2Self Claims Valid should be (1)"); + + /* + * Subtract 2 to account for the Asserted Identity and Claims Valid + * SIDs. + */ + torture_assert_int_equal(tctx, netlogon_user_info_dc->num_sids, kinit_session_info->torture->num_dc_sids - 2, "Different numbers of domain groups for kinit-based PAC"); + torture_assert_int_equal(tctx, netlogon_user_info_dc->num_sids, s4u2self_session_info->torture->num_dc_sids - 2, "Different numbers of domain groups for S4U2Self"); + + /* Loop over all three SID arrays. */ + for (i = 0, j = 0, k = 0; i < netlogon_user_info_dc->num_sids; i++, j++, k++) { + while (j == kinit_asserted_identity_index || j == kinit_claims_valid_index) { + /* Skip over the asserted identity and Claims Valid SIDs. */ + ++j; + } + while (k == s4u2self_asserted_identity_index || k == s4u2self_claims_valid_index) { + /* Skip over the asserted identity and Claims Valid SIDs. */ + ++k; + } + torture_assert_sid_equal(tctx, &netlogon_user_info_dc->sids[i].sid, &kinit_session_info->torture->dc_sids[j].sid, "Different domain groups for kinit-based PAC"); + torture_assert_u32_equal(tctx, netlogon_user_info_dc->sids[i].attrs, kinit_session_info->torture->dc_sids[j].attrs, "Different domain group attrs for kinit-based PAC"); + torture_assert_sid_equal(tctx, &netlogon_user_info_dc->sids[i].sid, &s4u2self_session_info->torture->dc_sids[k].sid, "Different domain groups for S4U2Self"); + torture_assert_u32_equal(tctx, netlogon_user_info_dc->sids[i].attrs, s4u2self_session_info->torture->dc_sids[k].attrs, "Different domain group attrs for S4U2Self"); + torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &s4u2self_session_info->torture->dc_sids[k].sid), "Returned BUILTIN domain in groups for S4U2Self"); + torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &kinit_session_info->torture->dc_sids[j].sid), "Returned BUILTIN domain in groups kinit-based PAC"); + torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &netlogon_user_info_dc->sids[i].sid), "Returned BUILTIN domain in groups from NETLOGON SamLogon reply"); + } + + return true; +} + +static bool test_S4U2Self_bdc_arcfour(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_S4U2Self(tctx, p, credentials, SEC_CHAN_BDC, + TEST_MACHINE_NAME_S4U2SELF_BDC, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_S4U2Self_bdc_aes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_S4U2Self(tctx, p, credentials, SEC_CHAN_BDC, + TEST_MACHINE_NAME_S4U2SELF_BDC, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +static bool test_S4U2Self_workstation_arcfour(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_S4U2Self(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_S4U2SELF_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_S4U2Self_workstation_aes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + return test_S4U2Self(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_S4U2SELF_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} + +static bool test_S4U2Proxy(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials, + enum netr_SchannelType secure_channel_type, + const char *test_machine_name, + uint32_t negotiate_flags) +{ + NTSTATUS status; + struct gensec_security *gensec_client_context = NULL; + struct gensec_security *gensec_server_context = NULL; + struct cli_credentials *server_creds = NULL; + size_t num_pac_buffers; + struct auth4_context *auth_context = NULL; + struct auth_session_info *session_info = NULL; + struct pac_data *pac_data = NULL; + const struct PAC_BUFFER *pac_buf = NULL; + char *impersonate_princ = NULL, *self_princ = NULL, *target_princ = NULL; + enum ndr_err_code ndr_err; + struct PAC_DATA pac_data_struct; + struct PAC_CONSTRAINED_DELEGATION *deleg = NULL; + + DATA_BLOB client_to_server, server_to_client; + + auth_context = talloc_zero(tctx, struct auth4_context); + torture_assert_not_null(tctx, auth_context, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + torture_comment(tctx, + "Testing S4U2Proxy (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n", + secure_channel_type, test_machine_name, negotiate_flags); + + impersonate_princ = cli_credentials_get_principal(samba_cmdline_get_creds(), tctx); + torture_assert_not_null(tctx, impersonate_princ, "Failed to get impersonate client name"); + + server_creds = cli_credentials_shallow_copy(tctx, credentials); + torture_assert_not_null(tctx, server_creds, "Failed to copy of credentials"); + + self_princ = talloc_asprintf(tctx, "host/%s", test_machine_name); + cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED); + cli_credentials_set_impersonate_principal(server_creds, impersonate_princ, self_princ); + + /* Trigger S4U2Proxy by setting a target_service different than self_principal */ + target_princ = talloc_asprintf(tctx, "%s$", test_machine_name); + cli_credentials_set_target_service(server_creds, target_princ); + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_principal(gensec_client_context, target_princ); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + + /* We now set the same credentials on both client and server contexts */ + status = gensec_set_credentials(gensec_client_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, server_creds); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + status = gensec_update(gensec_server_context, tctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (NT_STATUS_IS_OK(status)) { + break; + } + } while (1); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_data = talloc_get_type(auth_context->private_data, struct pac_data); + + torture_assert_not_null(tctx, pac_data, "gensec_update failed to fill in pac_data in auth_context"); + torture_assert_not_null(tctx, pac_data->pac_srv_sig, "pac_srv_sig not present"); + torture_assert_not_null(tctx, pac_data->pac_kdc_sig, "pac_kdc_sig not present"); + + ndr_err = ndr_pull_struct_blob(&pac_data->pac_blob, tctx, &pac_data_struct, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); + + num_pac_buffers = 9; + + torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version"); + torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_INFO"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_LOGON_INFO info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_NAME); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_NAME"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_LOGON_NAME info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_UPN_DNS_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_UPN_DNS_INFO"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_UPN_DNS_INFO info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_SRV_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_SRV_CHECKSUM"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_SRV_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_KDC_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_KDC_CHECKSUM"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_KDC_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_TICKET_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_FULL_CHECKSUM info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CLIENT_CLAIMS_INFO); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CLIENT_CLAIMS_INFO"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_CLIENT_CLAIMS_INFO info"); + + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CONSTRAINED_DELEGATION); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CONSTRAINED_DELEGATION"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_CONSTRAINED_DELEGATION info"); + + deleg = pac_buf->info->constrained_delegation.info; + torture_assert_str_equal(tctx, deleg->proxy_target.string, target_princ, "wrong proxy_target"); + torture_assert_int_equal(tctx, deleg->num_transited_services, 1, "wrong transited_services number"); + torture_assert_str_equal(tctx, deleg->transited_services[0].string, + talloc_asprintf(tctx, "%s@%s", self_princ, cli_credentials_get_realm(credentials)), + "wrong transited_services[0]"); + + return netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, + negotiate_flags, pac_data, session_info); +} + +static bool setup_constrained_delegation(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_join *join_ctx, + const char *machine_name) +{ + struct samr_SetUserInfo r; + union samr_UserInfo user_info; + struct dcerpc_pipe *samr_pipe = torture_join_samr_pipe(join_ctx); + const char *server_dn_str = NULL; + struct ldb_context *sam_ctx = NULL; + struct ldb_dn *server_dn = NULL; + struct ldb_message *msg = NULL; + char *url = NULL; + int ret; + + url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p)); + sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, NULL, samba_cmdline_get_creds(), 0); + torture_assert_not_null(tctx, sam_ctx, "Connection to the SAMDB on DC failed!"); + + server_dn_str = samdb_search_string(sam_ctx, tctx, ldb_get_default_basedn(sam_ctx), "distinguishedName", + "samaccountname=%s$", machine_name); + torture_assert_not_null(tctx, server_dn_str, "samdb_search_string()"); + + server_dn = ldb_dn_new(tctx, sam_ctx, server_dn_str); + torture_assert_not_null(tctx, server_dn, "ldb_dn_new()"); + + msg = ldb_msg_new(tctx); + torture_assert_not_null(tctx, msg, "ldb_msg_new()"); + + msg->dn = server_dn; + ret = ldb_msg_add_string(msg, "msDS-AllowedToDelegateTo", talloc_asprintf(tctx, "%s$", machine_name)); + torture_assert_int_equal(tctx, ret, 0, "ldb_msg_add_string())"); + + ret = ldb_modify(sam_ctx, msg); + torture_assert_int_equal(tctx, ret, 0, "ldb_modify()"); + + /* Allow forwardable flag in S4U2Self */ + user_info.info16.acct_flags = ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | ACB_WSTRUST; + r.in.user_handle = torture_join_samr_user_policy(join_ctx); + r.in.level = 16; + r.in.info = &user_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(samr_pipe->binding_handle, tctx, &r), + "failed to set ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION info account flags"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to set ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION into account flags"); + + return true; +} + +static bool test_S4U2Proxy_workstation_arcfour(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials, + struct test_join *join_ctx) +{ + torture_assert(tctx, setup_constrained_delegation(tctx, p, join_ctx, + TEST_MACHINE_NAME_S4U2PROXY_WKSTA), + "setup_constrained_delegation() failed"); + return test_S4U2Proxy(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_S4U2PROXY_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS); +} + +static bool test_S4U2Proxy_workstation_aes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials, + struct test_join *join_ctx) +{ + torture_assert(tctx, setup_constrained_delegation(tctx, p, join_ctx, + TEST_MACHINE_NAME_S4U2PROXY_WKSTA), + "setup_constrained_delegation() failed"); + return test_S4U2Proxy(tctx, p, credentials, SEC_CHAN_WKSTA, + TEST_MACHINE_NAME_S4U2PROXY_WKSTA, + NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES); +} +#endif + +struct torture_suite *torture_rpc_remote_pac(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "pac"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bdc-arcfour", + &ndr_table_netlogon, TEST_MACHINE_NAME_BDC); + torture_rpc_tcase_add_test_creds(tcase, "verify-sig-arcfour", test_PACVerify_bdc_arcfour); + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bdc-aes", + &ndr_table_netlogon, TEST_MACHINE_NAME_BDC); + torture_rpc_tcase_add_test_creds(tcase, "verify-sig-aes", test_PACVerify_bdc_aes); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour", + &ndr_table_netlogon, TEST_MACHINE_NAME_WKSTA); + torture_rpc_tcase_add_test_creds(tcase, "verify-sig-arcfour", test_PACVerify_workstation_arcfour); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes", + &ndr_table_netlogon, TEST_MACHINE_NAME_WKSTA); + torture_rpc_tcase_add_test_creds(tcase, "verify-sig-aes", test_PACVerify_workstation_aes); + +#ifdef SAMBA4_USES_HEIMDAL + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bdc-arcfour", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_BDC); + torture_rpc_tcase_add_test_creds(tcase, "s4u2self-arcfour", test_S4U2Self_bdc_arcfour); + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bcd-aes", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_BDC); + torture_rpc_tcase_add_test_creds(tcase, "s4u2self-aes", test_S4U2Self_bdc_aes); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_WKSTA); + torture_rpc_tcase_add_test_creds(tcase, "s4u2self-arcfour", test_S4U2Self_workstation_arcfour); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_WKSTA); + torture_rpc_tcase_add_test_creds(tcase, "s4u2self-aes", test_S4U2Self_workstation_aes); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2PROXY_WKSTA); + torture_rpc_tcase_add_test_join(tcase, "s4u2proxy-arcfour", test_S4U2Proxy_workstation_arcfour); + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes", + &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2PROXY_WKSTA); + torture_rpc_tcase_add_test_join(tcase, "s4u2proxy-aes", test_S4U2Proxy_workstation_aes); +#endif + return suite; +} diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c new file mode 100644 index 0000000..ea214e0 --- /dev/null +++ b/source4/torture/rpc/rpc.c @@ -0,0 +1,661 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "torture/smbtorture.h" +#include "librpc/ndr/ndr_table.h" +#include "../lib/util/dlinklist.h" + +static bool torture_rpc_teardown (struct torture_context *tcase, + void *data) +{ + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)data; + if (tcase_data->join_ctx != NULL) + torture_leave_domain(tcase, tcase_data->join_ctx); + talloc_free(tcase_data); + return true; +} + +/** + * Obtain the DCE/RPC binding context associated with a torture context. + * + * @param tctx Torture context + * @param binding Pointer to store DCE/RPC binding + */ +NTSTATUS torture_rpc_binding(struct torture_context *tctx, + struct dcerpc_binding **binding) +{ + NTSTATUS status; + const char *binding_string = torture_setting_string(tctx, "binding", + NULL); + + if (binding_string == NULL) { + torture_comment(tctx, + "You must specify a DCE/RPC binding string\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_parse_binding(tctx, binding_string, binding); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, + "Failed to parse dcerpc binding '%s'\n", + binding_string); + return status; + } + + return NT_STATUS_OK; +} + +/** + * open a rpc connection to the chosen binding string + */ +_PUBLIC_ NTSTATUS torture_rpc_connection(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + return torture_rpc_connection_with_binding(tctx, binding, p, table); +} + +/** + * open a rpc connection to the chosen binding string + */ +_PUBLIC_ NTSTATUS torture_rpc_connection_with_binding(struct torture_context *tctx, + struct dcerpc_binding *binding, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table) +{ + NTSTATUS status; + + dcerpc_init(); + + status = dcerpc_pipe_connect_b(tctx, + p, binding, table, + samba_cmdline_get_creds(), + tctx->ev, tctx->lp_ctx); + + if (NT_STATUS_IS_ERR(status)) { + torture_warning(tctx, "Failed to connect to remote server: %s %s\n", + dcerpc_binding_string(tctx, binding), nt_errstr(status)); + } + + return status; +} + +/** + * open a rpc connection to a specific transport + */ +NTSTATUS torture_rpc_connection_transport(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table, + enum dcerpc_transport_t transport, + uint32_t assoc_group_id, + uint32_t extra_flags) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + + *p = NULL; + + status = torture_rpc_binding(tctx, &binding); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_binding_set_transport(binding, transport); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_binding_set_assoc_group_id(binding, assoc_group_id); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_binding_set_flags(binding, extra_flags, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_pipe_connect_b(tctx, p, binding, table, + samba_cmdline_get_creds(), + tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + *p = NULL; + return status; + } + + return NT_STATUS_OK; +} + +static bool torture_rpc_setup_machine_workstation(struct torture_context *tctx, + void **data) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, + struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return false; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = samba_cmdline_get_creds(); + tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name, + ACB_WSTRUST, + &tcase_data->credentials); + if (tcase_data->join_ctx == NULL) + torture_fail(tctx, "Failed to join as WORKSTATION"); + + status = dcerpc_pipe_connect_b(tctx, + &(tcase_data->pipe), + binding, + tcase->table, + tcase_data->credentials, tctx->ev, tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return NT_STATUS_IS_OK(status); +} + +static bool torture_rpc_setup_machine_bdc(struct torture_context *tctx, + void **data) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, + struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return false; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = samba_cmdline_get_creds(); + tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name, + ACB_SVRTRUST, + &tcase_data->credentials); + if (tcase_data->join_ctx == NULL) + torture_fail(tctx, "Failed to join as BDC"); + + status = dcerpc_pipe_connect_b(tctx, + &(tcase_data->pipe), + binding, + tcase->table, + tcase_data->credentials, tctx->ev, tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return NT_STATUS_IS_OK(status); +} + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_workstation_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name) +{ + struct torture_rpc_tcase *tcase = talloc(suite, + struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->machine_name = talloc_strdup(tcase, machine_name); + tcase->tcase.setup = torture_rpc_setup_machine_workstation; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_bdc_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name) +{ + struct torture_rpc_tcase *tcase = talloc(suite, + struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->machine_name = talloc_strdup(tcase, machine_name); + tcase->tcase.setup = torture_rpc_setup_machine_bdc; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +_PUBLIC_ bool torture_suite_init_rpc_tcase(struct torture_suite *suite, + struct torture_rpc_tcase *tcase, + const char *name, + const struct ndr_interface_table *table) +{ + if (!torture_suite_init_tcase(suite, (struct torture_tcase *)tcase, name)) + return false; + + tcase->table = table; + + return true; +} + +static bool torture_rpc_setup_anonymous(struct torture_context *tctx, + void **data) +{ + NTSTATUS status; + struct dcerpc_binding *binding; + struct torture_rpc_tcase_data *tcase_data; + struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, + struct torture_rpc_tcase); + + status = torture_rpc_binding(tctx, &binding); + if (NT_STATUS_IS_ERR(status)) + return false; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = cli_credentials_init_anon(tctx); + + status = dcerpc_pipe_connect_b(tctx, + &(tcase_data->pipe), + binding, + tcase->table, + tcase_data->credentials, tctx->ev, tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return NT_STATUS_IS_OK(status); +} + +static bool torture_rpc_setup (struct torture_context *tctx, void **data) +{ + NTSTATUS status; + struct torture_rpc_tcase *tcase = talloc_get_type( + tctx->active_tcase, struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data; + + *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); + tcase_data->credentials = samba_cmdline_get_creds(); + + status = torture_rpc_connection(tctx, + &(tcase_data->pipe), + tcase->table); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + return NT_STATUS_IS_OK(status); +} + + + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table) +{ + struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->tcase.setup = torture_rpc_setup_anonymous; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table) +{ + struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->tcase.setup = torture_rpc_setup; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +static bool torture_rpc_wrap_test(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe); +} + +static bool torture_rpc_wrap_test_ex(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, const void *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe, test->data); +} + + +static bool torture_rpc_wrap_test_creds(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe, tcase_data->credentials); +} + +static bool torture_rpc_wrap_test_join(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *, struct test_join *); + struct torture_rpc_tcase_data *tcase_data = + (struct torture_rpc_tcase_data *)tcase->data; + + fn = test->fn; + + return fn(tctx, tcase_data->pipe, tcase_data->credentials, tcase_data->join_ctx); +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *)) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test; + test->dangerous = false; + test->data = NULL; + test->fn = fn; + + DLIST_ADD_END(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_creds( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *)) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_creds; + test->dangerous = false; + test->data = NULL; + test->fn = fn; + + DLIST_ADD_END(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_join( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + struct cli_credentials *, struct test_join *)) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_join; + test->dangerous = false; + test->data = NULL; + test->fn = fn; + + DLIST_ADD_END(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_ex( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + void *), + void *userdata) +{ + struct torture_test *test; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_ex; + test->dangerous = false; + test->data = userdata; + test->fn = fn; + + DLIST_ADD_END(tcase->tcase.tests, test); + + return test; +} + +static bool torture_rpc_wrap_test_setup(struct torture_context *tctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn)(struct torture_context *, struct dcerpc_pipe *, const void *); + struct torture_rpc_tcase *rpc_tcase = talloc_get_type_abort( + tctx->active_tcase, struct torture_rpc_tcase); + struct torture_rpc_tcase_data *tcase_data = talloc_get_type_abort( + tcase->data, struct torture_rpc_tcase_data); + void *data = discard_const_p(void, test->data); + bool ok; + + ok = rpc_tcase->setup_fn(tctx, tcase_data->pipe, data); + if (!ok) { + return false; + } + + fn = test->fn; + + ok = fn(tctx, tcase_data->pipe, data); + if (!ok) { + return false; + } + + ok = rpc_tcase->teardown_fn(tctx, tcase_data->pipe, data); + if (!ok) { + return false; + } + + return true; +} + +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_setup( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn)(struct torture_context *, + struct dcerpc_pipe *, + void *), + void *userdata) +{ + struct torture_test *test = NULL; + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = torture_rpc_wrap_test_setup; + test->dangerous = false; + test->data = userdata; + test->fn = fn; + + DLIST_ADD_END(tcase->tcase.tests, test); + + return test; +} + +_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_rpc_setup_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + bool (*setup_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *), + bool (*teardown_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *)) +{ + struct torture_rpc_tcase *tcase = talloc( + suite, struct torture_rpc_tcase); + + torture_suite_init_rpc_tcase(suite, tcase, name, table); + + tcase->setup_fn = setup_fn; + tcase->teardown_fn = teardown_fn; + tcase->tcase.setup = torture_rpc_setup; + tcase->tcase.teardown = torture_rpc_teardown; + + return tcase; +} + +NTSTATUS torture_rpc_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "rpc"); + + ndr_table_init(); + + torture_suite_add_simple_test(suite, "lsa", torture_rpc_lsa); + torture_suite_add_simple_test(suite, "lsalookup", torture_rpc_lsa_lookup); + torture_suite_add_simple_test(suite, "lsa-getuser", torture_rpc_lsa_get_user); + torture_suite_add_suite(suite, torture_rpc_lsa_lookup_sids(suite)); + torture_suite_add_suite(suite, torture_rpc_lsa_lookup_names(suite)); + torture_suite_add_suite(suite, torture_rpc_lsa_secrets(suite)); + torture_suite_add_suite(suite, torture_rpc_lsa_trusted_domains(suite)); + torture_suite_add_suite(suite, torture_rpc_lsa_forest_trust(suite)); + torture_suite_add_suite(suite, torture_rpc_lsa_privileges(suite)); + torture_suite_add_suite(suite, torture_rpc_echo(suite)); + torture_suite_add_suite(suite, torture_rpc_dfs(suite)); + torture_suite_add_suite(suite, torture_rpc_frsapi(suite)); + torture_suite_add_suite(suite, torture_rpc_unixinfo(suite)); + torture_suite_add_suite(suite, torture_rpc_eventlog(suite)); + torture_suite_add_suite(suite, torture_rpc_atsvc(suite)); + torture_suite_add_suite(suite, torture_rpc_wkssvc(suite)); + torture_suite_add_suite(suite, torture_rpc_handles(suite)); + torture_suite_add_suite(suite, torture_rpc_object_uuid(suite)); + torture_suite_add_suite(suite, torture_rpc_winreg(suite)); + torture_suite_add_suite(suite, torture_rpc_spoolss(suite)); +#ifdef WITH_NTVFS_FILESERVER + torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite)); +#endif + torture_suite_add_suite(suite, torture_rpc_spoolss_win(suite)); + torture_suite_add_suite(suite, torture_rpc_spoolss_driver(suite)); + torture_suite_add_suite(suite, torture_rpc_spoolss_access(suite)); + torture_suite_add_suite(suite, torture_rpc_iremotewinspool(suite)); + torture_suite_add_suite(suite, torture_rpc_iremotewinspool_drv(suite)); + torture_suite_add_simple_test(suite, "samr", torture_rpc_samr); + torture_suite_add_simple_test(suite, "samr.users", torture_rpc_samr_users); + torture_suite_add_simple_test(suite, "samr.passwords.default", torture_rpc_samr_passwords); + torture_suite_add_suite(suite, torture_rpc_netlogon(suite)); + torture_suite_add_suite(suite, torture_rpc_netlogon_s3(suite)); + torture_suite_add_suite(suite, torture_rpc_netlogon_admin(suite)); + torture_suite_add_suite(suite, torture_rpc_netlogon_zerologon(suite)); + torture_suite_add_suite(suite, torture_rpc_netlogon_crypto_fips(suite)); + torture_suite_add_suite(suite, torture_rpc_remote_pac(suite)); + torture_suite_add_simple_test(suite, "samlogon", torture_rpc_samlogon); + torture_suite_add_simple_test(suite, "samsync", torture_rpc_samsync); + torture_suite_add_simple_test(suite, "schannel", torture_rpc_schannel); + torture_suite_add_simple_test(suite, "schannel2", torture_rpc_schannel2); + torture_suite_add_simple_test(suite, "bench-schannel1", torture_rpc_schannel_bench1); + torture_suite_add_simple_test(suite, "schannel_anon_setpw", torture_rpc_schannel_anon_setpw); + torture_suite_add_suite(suite, torture_rpc_srvsvc(suite)); + torture_suite_add_suite(suite, torture_rpc_svcctl(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_handletype(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_workstation_auth(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_passwords_pwdlastset(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_passwords_badpwdcount(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_passwords_lockout(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_passwords_validate(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_user_privileges(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_large_dc(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_priv(suite)); + torture_suite_add_suite(suite, torture_rpc_epmapper(suite)); + torture_suite_add_suite(suite, torture_rpc_initshutdown(suite)); + torture_suite_add_simple_test(suite, "mgmt", torture_rpc_mgmt); + torture_suite_add_simple_test(suite, "scanner", torture_rpc_scanner); + torture_suite_add_simple_test(suite, "countcalls", torture_rpc_countcalls); + torture_suite_add_simple_test(suite, "authcontext", torture_bind_authcontext); + torture_suite_add_suite(suite, torture_rpc_samba3(suite)); + torture_rpc_drsuapi_tcase(suite); + torture_rpc_drsuapi_w2k8_tcase(suite); + torture_rpc_drsuapi_cracknames_tcase(suite); + torture_suite_add_suite(suite, torture_rpc_dssetup(suite)); + torture_suite_add_suite(suite, torture_rpc_browser(suite)); + torture_suite_add_simple_test(suite, "altercontext", torture_rpc_alter_context); + torture_suite_add_simple_test(suite, "join", torture_rpc_join); + torture_drs_rpc_dsgetinfo_tcase(suite); + torture_suite_add_simple_test(suite, "bench-rpc", torture_bench_rpc); + torture_suite_add_simple_test(suite, "asyncbind", torture_async_bind); + torture_suite_add_suite(suite, torture_rpc_ntsvcs(suite)); + torture_suite_add_suite(suite, torture_rpc_bind(suite)); +#ifdef AD_DC_BUILD_IS_ENABLED + torture_suite_add_suite(suite, torture_rpc_backupkey(suite)); +#endif + torture_suite_add_suite(suite, torture_rpc_fsrvp(suite)); + torture_suite_add_suite(suite, torture_rpc_clusapi(suite)); + torture_suite_add_suite(suite, torture_rpc_witness(suite)); + torture_suite_add_suite(suite, torture_rpc_mdssvc(suite)); + + suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c new file mode 100644 index 0000000..10f0fcc --- /dev/null +++ b/source4/torture/rpc/samba3rpc.c @@ -0,0 +1,4729 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc torture tests, designed to walk Samba3 code paths + + Copyright (C) Volker Lendecke 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "torture/util.h" +#include "libcli/rap/rap.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "librpc/gen_ndr/ndr_wkssvc_c.h" +#include "librpc/gen_ndr/ndr_svcctl_c.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/libcli.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "param/param.h" +#include "lib/registry/registry.h" +#include "libcli/resolve/resolve.h" +#include "torture/ndr/ndr.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "librpc/rpc/dcerpc.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "libcli/smb/smbXcli_base.h" +#include "source3/rpc_client/init_samr.h" + +/* + * open pipe and bind, given an IPC$ context + */ + +static NTSTATUS pipe_bind_smb(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_tree *tree, + const char *pipe_name, + const struct ndr_interface_table *iface, + struct dcerpc_pipe **p) +{ + struct dcerpc_pipe *result; + NTSTATUS status; + + if (!(result = dcerpc_pipe_init(mem_ctx, tctx->ev))) { + return NT_STATUS_NO_MEMORY; + } + + status = dcerpc_pipe_open_smb(result, tree, pipe_name); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + talloc_free(result); + return status; + } + + status = dcerpc_bind_auth_none(result, iface); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_bind_auth_none failed: %s\n", nt_errstr(status)); + talloc_free(result); + return status; + } + + *p = result; + return NT_STATUS_OK; +} + +static NTSTATUS pipe_bind_smb2(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + const char *pipe_name, + const struct ndr_interface_table *iface, + struct dcerpc_pipe **p) +{ + struct dcerpc_pipe *result; + NTSTATUS status; + + if (!(result = dcerpc_pipe_init(mem_ctx, tctx->ev))) { + return NT_STATUS_NO_MEMORY; + } + + status = dcerpc_pipe_open_smb2(result, tree, pipe_name); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_pipe_open_smb2 failed: %s\n", + nt_errstr(status)); + talloc_free(result); + return status; + } + + status = dcerpc_bind_auth_none(result, iface); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_bind_auth_none failed: %s\n", nt_errstr(status)); + talloc_free(result); + return status; + } + + *p = result; + return NT_STATUS_OK; +} + +static NTSTATUS pipe_bind_smb_auth(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_tree *tree, + struct cli_credentials *creds, + uint8_t auth_type, + uint8_t auth_level, + const char *pipe_name, + const struct ndr_interface_table *iface, + struct dcerpc_pipe **p) +{ + struct dcerpc_pipe *result; + NTSTATUS status; + + if (!(result = dcerpc_pipe_init(mem_ctx, tctx->ev))) { + return NT_STATUS_NO_MEMORY; + } + + status = dcerpc_pipe_open_smb(result, tree, pipe_name); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_pipe_open_smb failed: %s\n", + nt_errstr(status)); + talloc_free(result); + return status; + } + + status = dcerpc_bind_auth(result, iface, creds, + lpcfg_gensec_settings(tctx->lp_ctx, tctx->lp_ctx), + auth_type, auth_level, NULL); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_bind_auth failed: %s\n", nt_errstr(status)); + talloc_free(result); + return status; + } + + *p = result; + return NT_STATUS_OK; +} + +/* + * This tests a RPC call using an invalid vuid + */ + +bool torture_bind_authcontext(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 openpolicy; + struct policy_handle handle; + struct lsa_Close close_handle; + struct smbcli_session *tmp; + uint16_t tmp_vuid; + struct smbcli_session *session2; + struct smbcli_state *cli; + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct cli_credentials *anon_creds; + struct smb_composite_sesssetup setup; + struct smbcli_options options; + struct smbcli_session_options session_options; + + mem_ctx = talloc_init("torture_bind_authcontext"); + + if (mem_ctx == NULL) { + torture_comment(torture, "talloc_init failed\n"); + return false; + } + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc", + &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb failed"); + lsa_handle = lsa_pipe->binding_handle; + + openpolicy.in.system_name =talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe)); + ZERO_STRUCT(objectattr); + openpolicy.in.attr = &objectattr; + openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + openpolicy.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2_r(lsa_handle, mem_ctx, &openpolicy); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(openpolicy.out.result)) { + torture_comment(torture, "dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(openpolicy.out.result)); + goto done; + } + + close_handle.in.handle = &handle; + close_handle.out.handle = &handle; + + status = dcerpc_lsa_Close_r(lsa_handle, mem_ctx, &close_handle); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "dcerpc_lsa_Close failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(close_handle.out.result)) { + torture_comment(torture, "dcerpc_lsa_Close failed: %s\n", + nt_errstr(close_handle.out.result)); + goto done; + } + + session2 = smbcli_session_init(cli->transport, mem_ctx, false, session_options); + if (session2 == NULL) { + torture_comment(torture, "smbcli_session_init failed\n"); + goto done; + } + + if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) { + torture_comment(torture, "create_anon_creds failed\n"); + goto done; + } + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = ""; + setup.in.credentials = anon_creds; + setup.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(session2, &setup); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "anon session setup failed: %s\n", + nt_errstr(status)); + goto done; + } + session2->vuid = setup.out.vuid; + + tmp = cli->tree->session; + tmp_vuid = smb1cli_session_current_id(tmp->smbXcli); + smb1cli_session_set_id(tmp->smbXcli, session2->vuid); + cli->tree->session = session2; + + status = dcerpc_lsa_OpenPolicy2_r(lsa_handle, mem_ctx, &openpolicy); + + torture_assert(torture, smbXcli_conn_is_connected(cli->transport->conn), + "smb still connected"); + torture_assert(torture, !dcerpc_binding_handle_is_connected(lsa_handle), + "dcerpc disconnected"); + + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { + torture_comment(torture, "dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, " + "expected NT_STATUS_CONNECTION_DISCONNECTED\n", + nt_errstr(status)); + status = NT_STATUS_CONNECTION_DISCONNECTED; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR)) { + torture_comment(torture, "dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, " + "expected NT_STATUS_CONNECTION_DISCONNECTED\n", + nt_errstr(status)); + status = NT_STATUS_CONNECTION_DISCONNECTED; + } + + torture_assert_ntstatus_equal(torture, status, NT_STATUS_CONNECTION_DISCONNECTED, + "lsa connection disconnected"); + + smb1cli_session_set_id(tmp->smbXcli, tmp_vuid); + cli->tree->session = tmp; + talloc_free(lsa_pipe); + lsa_pipe = NULL; + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Bind to lsa using a specific auth method + */ + +static bool bindtest(struct torture_context *tctx, + struct smbcli_state *cli, + struct cli_credentials *credentials, + uint8_t auth_type, uint8_t auth_level) +{ + TALLOC_CTX *mem_ctx; + bool ret = false; + NTSTATUS status; + + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_ObjectAttribute objectattr; + struct lsa_OpenPolicy2 openpolicy; + struct lsa_QueryInfoPolicy query; + union lsa_PolicyInformation *info = NULL; + struct policy_handle handle; + struct lsa_Close close_handle; + + if ((mem_ctx = talloc_init("bindtest")) == NULL) { + torture_comment(tctx, "talloc_init failed\n"); + return false; + } + + status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree, + credentials, auth_type, auth_level, + "\\lsarpc", &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "pipe_bind_smb_auth failed"); + lsa_handle = lsa_pipe->binding_handle; + + openpolicy.in.system_name =talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe)); + ZERO_STRUCT(objectattr); + openpolicy.in.attr = &objectattr; + openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + openpolicy.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2_r(lsa_handle, mem_ctx, &openpolicy); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(openpolicy.out.result)) { + torture_comment(tctx, "dcerpc_lsa_OpenPolicy2 failed: %s\n", + nt_errstr(openpolicy.out.result)); + goto done; + } + + query.in.handle = &handle; + query.in.level = LSA_POLICY_INFO_DOMAIN; + query.out.info = &info; + + status = dcerpc_lsa_QueryInfoPolicy_r(lsa_handle, mem_ctx, &query); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_lsa_QueryInfoPolicy failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(query.out.result)) { + torture_comment(tctx, "dcerpc_lsa_QueryInfoPolicy failed: %s\n", + nt_errstr(query.out.result)); + goto done; + } + + close_handle.in.handle = &handle; + close_handle.out.handle = &handle; + + status = dcerpc_lsa_Close_r(lsa_handle, mem_ctx, &close_handle); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_lsa_Close failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(close_handle.out.result)) { + torture_comment(tctx, "dcerpc_lsa_Close failed: %s\n", + nt_errstr(close_handle.out.result)); + goto done; + } + + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * test authenticated RPC binds with the variants Samba3 does support + */ + +static bool torture_bind_samba3(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct smbcli_options options; + struct smbcli_session_options session_options; + + mem_ctx = talloc_init("torture_bind_authcontext"); + + if (mem_ctx == NULL) { + torture_comment(torture, "talloc_init failed\n"); + return false; + } + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "smbcli_full_connection failed: %s\n", + nt_errstr(status)); + goto done; + } + + ret = true; + + ret &= bindtest(torture, cli, samba_cmdline_get_creds(), + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY); + ret &= bindtest(torture, cli, samba_cmdline_get_creds(), + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY); + ret &= bindtest(torture, cli, samba_cmdline_get_creds(), + DCERPC_AUTH_TYPE_SPNEGO, + DCERPC_AUTH_LEVEL_INTEGRITY); + ret &= bindtest(torture, cli, samba_cmdline_get_creds(), + DCERPC_AUTH_TYPE_SPNEGO, + DCERPC_AUTH_LEVEL_PRIVACY); + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Lookup or create a user and return all necessary info + */ + +static bool get_usr_handle(struct torture_context *tctx, + struct smbcli_state *cli, + TALLOC_CTX *mem_ctx, + struct cli_credentials *admin_creds, + uint8_t auth_type, + uint8_t auth_level, + const char *username, + char **domain, + struct dcerpc_pipe **result_pipe, + struct policy_handle **result_handle, + struct dom_sid **sid_p) +{ + struct dcerpc_pipe *samr_pipe; + struct dcerpc_binding_handle *samr_handle; + NTSTATUS status; + struct policy_handle conn_handle; + struct policy_handle domain_handle; + struct policy_handle *user_handle; + struct samr_Connect2 conn; + struct samr_EnumDomains enumdom; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + struct samr_SamArray *sam = NULL; + struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; + int dom_idx; + struct lsa_String domain_name; + struct lsa_String user_name; + struct samr_OpenDomain o; + struct samr_CreateUser2 c; + uint32_t user_rid,access_granted; + + if (admin_creds != NULL) { + status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree, + admin_creds, auth_type, auth_level, + "\\samr", &ndr_table_samr, &samr_pipe); + torture_assert_ntstatus_ok(tctx, status, "pipe_bind_smb_auth failed"); + } else { + /* We must have an authenticated SMB connection */ + status = pipe_bind_smb(tctx, mem_ctx, cli->tree, + "\\samr", &ndr_table_samr, &samr_pipe); + torture_assert_ntstatus_ok(tctx, status, "pipe_bind_smb_auth failed"); + } +#if 0 + samr_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + samr_handle = samr_pipe->binding_handle; + + conn.in.system_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(samr_pipe)); + conn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + conn.out.connect_handle = &conn_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect2_r(samr_handle, mem_ctx, &conn), + "samr_Connect2 failed"); + torture_assert_ntstatus_ok(tctx, conn.out.result, + "samr_Connect2 failed"); + + enumdom.in.connect_handle = &conn_handle; + enumdom.in.resume_handle = &resume_handle; + enumdom.in.buf_size = (uint32_t)-1; + enumdom.out.resume_handle = &resume_handle; + enumdom.out.num_entries = &num_entries; + enumdom.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_EnumDomains_r(samr_handle, mem_ctx, &enumdom), + "samr_EnumDomains failed"); + torture_assert_ntstatus_ok(tctx, enumdom.out.result, + "samr_EnumDomains failed"); + + torture_assert_int_equal(tctx, *enumdom.out.num_entries, 2, + "samr_EnumDomains returned unexpected num_entries"); + + dom_idx = strequal(sam->entries[0].name.string, + "builtin") ? 1:0; + + l.in.connect_handle = &conn_handle; + domain_name.string = sam->entries[dom_idx].name.string; + *domain = talloc_strdup(mem_ctx, domain_name.string); + l.in.domain_name = &domain_name; + l.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupDomain_r(samr_handle, mem_ctx, &l), + "samr_LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, l.out.result, + "samr_LookupDomain failed"); + + o.in.connect_handle = &conn_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = *l.out.sid; + o.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(samr_handle, mem_ctx, &o), + "samr_OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, o.out.result, + "samr_OpenDomain failed"); + + c.in.domain_handle = &domain_handle; + user_name.string = username; + c.in.account_name = &user_name; + c.in.acct_flags = ACB_NORMAL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + user_handle = talloc(mem_ctx, struct policy_handle); + c.out.user_handle = user_handle; + c.out.access_granted = &access_granted; + c.out.rid = &user_rid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_CreateUser2_r(samr_handle, mem_ctx, &c), + "samr_CreateUser2 failed"); + + if (NT_STATUS_EQUAL(c.out.result, NT_STATUS_USER_EXISTS)) { + struct samr_LookupNames ln; + struct samr_OpenUser ou; + struct samr_Ids rids, types; + + ln.in.domain_handle = &domain_handle; + ln.in.num_names = 1; + ln.in.names = &user_name; + ln.out.rids = &rids; + ln.out.types = &types; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_LookupNames_r(samr_handle, mem_ctx, &ln), + "samr_LookupNames failed"); + torture_assert_ntstatus_ok(tctx, ln.out.result, + "samr_LookupNames failed"); + + ou.in.domain_handle = &domain_handle; + ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + user_rid = ou.in.rid = ln.out.rids->ids[0]; + ou.out.user_handle = user_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenUser_r(samr_handle, mem_ctx, &ou), + "samr_OpenUser failed"); + status = ou.out.result; + } else { + status = c.out.result; + } + + torture_assert_ntstatus_ok(tctx, status, + "samr_CreateUser failed"); + + *result_pipe = samr_pipe; + *result_handle = user_handle; + if (sid_p != NULL) { + *sid_p = dom_sid_add_rid(mem_ctx, *l.out.sid, user_rid); + } + return true; + +} + +/* + * Create a test user + */ + +static bool create_user(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct smbcli_state *cli, + struct cli_credentials *admin_creds, + const char *username, const char *password, + char **domain_name, + struct dom_sid **user_sid) +{ + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + struct dcerpc_pipe *samr_pipe; + struct dcerpc_binding_handle *samr_handle; + struct policy_handle *wks_handle; + bool ret = false; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + torture_comment(tctx, "talloc_init failed\n"); + return false; + } + + ret = get_usr_handle(tctx, cli, tmp_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY, + username, domain_name, &samr_pipe, &wks_handle, + user_sid); + if (ret == false) { + torture_comment(tctx, "get_usr_handle failed\n"); + goto done; + } + samr_handle = samr_pipe->binding_handle; + + { + struct samr_SetUserInfo2 sui2; + struct samr_SetUserInfo sui; + struct samr_QueryUserInfo qui; + union samr_UserInfo u_info; + union samr_UserInfo *info; + DATA_BLOB session_key; + + ZERO_STRUCT(u_info); + encode_pw_buffer(u_info.info23.password.data, password, + STR_UNICODE); + + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed\n"); + goto done; + } + + status = init_samr_CryptPassword(password, + &session_key, + &u_info.info23.password); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "init_samr_CryptPassword failed\n"); + goto done; + } + + u_info.info23.info.password_expired = 0; + u_info.info23.info.fields_present = SAMR_FIELD_NT_PASSWORD_PRESENT | + SAMR_FIELD_LM_PASSWORD_PRESENT | + SAMR_FIELD_EXPIRED_FLAG; + sui2.in.user_handle = wks_handle; + sui2.in.info = &u_info; + sui2.in.level = 23; + + status = dcerpc_samr_SetUserInfo2_r(samr_handle, tmp_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "samr_SetUserInfo(23) failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(sui2.out.result)) { + torture_comment(tctx, "samr_SetUserInfo(23) failed: %s\n", + nt_errstr(sui2.out.result)); + goto done; + } + + u_info.info16.acct_flags = ACB_NORMAL; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 16; + + status = dcerpc_samr_SetUserInfo_r(samr_handle, tmp_ctx, &sui); + if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) { + torture_comment(tctx, "samr_SetUserInfo(16) failed\n"); + goto done; + } + + qui.in.user_handle = wks_handle; + qui.in.level = 21; + qui.out.info = &info; + + status = dcerpc_samr_QueryUserInfo_r(samr_handle, tmp_ctx, &qui); + if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(qui.out.result)) { + torture_comment(tctx, "samr_QueryUserInfo(21) failed\n"); + goto done; + } + + info->info21.allow_password_change = 0; + info->info21.force_password_change = 0; + info->info21.account_name.string = NULL; + info->info21.rid = 0; + info->info21.acct_expiry = 0; + info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */ + + u_info.info21 = info->info21; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 21; + + status = dcerpc_samr_SetUserInfo_r(samr_handle, tmp_ctx, &sui); + if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) { + torture_comment(tctx, "samr_SetUserInfo(21) failed\n"); + goto done; + } + } + + *domain_name= talloc_steal(mem_ctx, *domain_name); + *user_sid = talloc_steal(mem_ctx, *user_sid); + ret = true; + done: + talloc_free(tmp_ctx); + return ret; +} + +/* + * Delete a test user + */ + +static bool delete_user(struct torture_context *tctx, + struct smbcli_state *cli, + struct cli_credentials *admin_creds, + const char *username) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + char *dom_name; + struct dcerpc_pipe *samr_pipe; + struct dcerpc_binding_handle *samr_handle; + struct policy_handle *user_handle; + bool ret = false; + + if ((mem_ctx = talloc_init("leave")) == NULL) { + torture_comment(tctx, "talloc_init failed\n"); + return false; + } + + ret = get_usr_handle(tctx, cli, mem_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_INTEGRITY, + username, &dom_name, &samr_pipe, + &user_handle, NULL); + if (ret == false) { + torture_comment(tctx, "get_wks_handle failed\n"); + goto done; + } + samr_handle = samr_pipe->binding_handle; + + { + struct samr_DeleteUser d; + + d.in.user_handle = user_handle; + d.out.user_handle = user_handle; + + status = dcerpc_samr_DeleteUser_r(samr_handle, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "samr_DeleteUser failed %s\n", nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(d.out.result)) { + torture_comment(tctx, "samr_DeleteUser failed %s\n", nt_errstr(d.out.result)); + goto done; + } + + } + + ret = true; + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a Samba3-style join + */ + +static bool join3(struct torture_context *tctx, + struct smbcli_state *cli, + bool use_level25, + struct cli_credentials *admin_creds, + struct cli_credentials *wks_creds) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + char *dom_name; + struct dcerpc_pipe *samr_pipe; + struct dcerpc_binding_handle *samr_handle; + struct policy_handle *wks_handle; + bool ret = false; + NTTIME last_password_change; + + if ((mem_ctx = talloc_init("join3")) == NULL) { + torture_comment(tctx, "talloc_init failed\n"); + return false; + } + + ret = get_usr_handle( + tctx, cli, mem_ctx, admin_creds, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY, + talloc_asprintf(mem_ctx, "%s$", + cli_credentials_get_workstation(wks_creds)), + &dom_name, &samr_pipe, &wks_handle, NULL); + if (ret == false) { + torture_comment(tctx, "get_wks_handle failed\n"); + goto done; + } + samr_handle = samr_pipe->binding_handle; + ret = false; + { + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + + q.in.user_handle = wks_handle; + q.in.level = 21; + q.out.info = &info; + + status = dcerpc_samr_QueryUserInfo_r(samr_handle, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "QueryUserInfo failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_warning(tctx, "QueryUserInfo failed: %s\n", + nt_errstr(q.out.result)); + goto done; + } + + + last_password_change = info->info21.last_password_change; + } + + cli_credentials_set_domain(wks_creds, dom_name, CRED_SPECIFIED); + + if (use_level25) { + struct samr_SetUserInfo2 sui2; + union samr_UserInfo u_info; + struct samr_UserInfo21 *i21 = &u_info.info25.info; + DATA_BLOB session_key; + + ZERO_STRUCT(u_info); + + i21->full_name.string = talloc_asprintf( + mem_ctx, "%s$", + cli_credentials_get_workstation(wks_creds)); + i21->acct_flags = ACB_WSTRUST; + i21->fields_present = SAMR_FIELD_FULL_NAME | + SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_NT_PASSWORD_PRESENT; + /* this would break the test result expectations + i21->fields_present |= SAMR_FIELD_EXPIRED_FLAG; + i21->password_expired = 1; + */ + + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = init_samr_CryptPasswordEx(cli_credentials_get_password(wks_creds), + &session_key, + &u_info.info25.password); + + sui2.in.user_handle = wks_handle; + sui2.in.level = 25; + sui2.in.info = &u_info; + + status = dcerpc_samr_SetUserInfo2_r(samr_handle, mem_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "samr_SetUserInfo2(25) failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(sui2.out.result)) { + torture_comment(tctx, "samr_SetUserInfo2(25) failed: %s\n", + nt_errstr(sui2.out.result)); + goto done; + } + } else { + struct samr_SetUserInfo2 sui2; + struct samr_SetUserInfo sui; + union samr_UserInfo u_info; + DATA_BLOB session_key; + + encode_pw_buffer(u_info.info24.password.data, + cli_credentials_get_password(wks_creds), + STR_UNICODE); + /* just to make this test pass */ + u_info.info24.password_expired = 1; + + status = dcerpc_fetch_session_key(samr_pipe, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed\n"); + goto done; + } + + status = init_samr_CryptPassword(cli_credentials_get_password(wks_creds), + &session_key, + &u_info.info24.password); + + sui2.in.user_handle = wks_handle; + sui2.in.info = &u_info; + sui2.in.level = 24; + + status = dcerpc_samr_SetUserInfo2_r(samr_handle, mem_ctx, &sui2); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "samr_SetUserInfo(24) failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(sui2.out.result)) { + torture_comment(tctx, "samr_SetUserInfo(24) failed: %s\n", + nt_errstr(sui2.out.result)); + goto done; + } + + u_info.info16.acct_flags = ACB_WSTRUST; + sui.in.user_handle = wks_handle; + sui.in.info = &u_info; + sui.in.level = 16; + + status = dcerpc_samr_SetUserInfo_r(samr_handle, mem_ctx, &sui); + if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) { + torture_comment(tctx, "samr_SetUserInfo(16) failed\n"); + goto done; + } + } + + { + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + + q.in.user_handle = wks_handle; + q.in.level = 21; + q.out.info = &info; + + status = dcerpc_samr_QueryUserInfo_r(samr_handle, mem_ctx, &q); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "QueryUserInfo failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_warning(tctx, "QueryUserInfo failed: %s\n", + nt_errstr(q.out.result)); + goto done; + } + + if (use_level25) { + if (last_password_change + == info->info21.last_password_change) { + torture_warning(tctx, "last_password_change unchanged " + "during join, level25 must change " + "it\n"); + goto done; + } + } + else { + if (last_password_change + != info->info21.last_password_change) { + torture_warning(tctx, "last_password_change changed " + "during join, level24 doesn't " + "change it\n"); + goto done; + } + } + } + + ret = true; + + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Do a ReqChallenge/Auth2 and get the wks creds + */ + +static bool auth2(struct torture_context *tctx, + struct smbcli_state *cli, + struct cli_credentials *wks_cred) +{ + TALLOC_CTX *mem_ctx; + struct dcerpc_pipe *net_pipe; + struct dcerpc_binding_handle *net_handle; + bool result = false; + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_Credential netr_cli_creds; + struct netr_Credential netr_srv_creds; + uint32_t negotiate_flags; + struct netr_ServerAuthenticate2 a; + struct netlogon_creds_CredentialState *creds_state; + struct netr_Credential netr_cred; + struct samr_Password mach_pw; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + torture_comment(tctx, "talloc_new failed\n"); + return false; + } + + status = pipe_bind_smb(tctx, mem_ctx, cli->tree, "\\netlogon", + &ndr_table_netlogon, &net_pipe); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "pipe_bind_smb failed"); + net_handle = net_pipe->binding_handle; + + r.in.computer_name = cli_credentials_get_workstation(wks_cred); + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + if (r.in.server_name == NULL) { + torture_comment(tctx, "talloc_asprintf failed\n"); + goto done; + } + generate_random_buffer(netr_cli_creds.data, + sizeof(netr_cli_creds.data)); + r.in.credentials = &netr_cli_creds; + r.out.return_credentials = &netr_srv_creds; + + status = dcerpc_netr_ServerReqChallenge_r(net_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "netr_ServerReqChallenge failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "netr_ServerReqChallenge failed: %s\n", + nt_errstr(r.out.result)); + goto done; + } + + negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + E_md4hash(cli_credentials_get_password(wks_cred), mach_pw.hash); + + a.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + a.in.account_name = talloc_asprintf( + mem_ctx, "%s$", cli_credentials_get_workstation(wks_cred)); + a.in.computer_name = cli_credentials_get_workstation(wks_cred); + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &netr_cred; + a.out.return_credentials = &netr_cred; + + creds_state = netlogon_creds_client_init(mem_ctx, + a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + r.in.credentials, + r.out.return_credentials, &mach_pw, + &netr_cred, negotiate_flags); + torture_assert(tctx, (creds_state != NULL), "memory allocation failed"); + + status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "netr_ServerServerAuthenticate2 failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(a.out.result)) { + torture_comment(tctx, "netr_ServerServerAuthenticate2 failed: %s\n", + nt_errstr(a.out.result)); + goto done; + } + + if (!netlogon_creds_client_check(creds_state, a.out.return_credentials)) { + torture_comment(tctx, "creds_client_check failed\n"); + goto done; + } + + cli_credentials_set_netlogon_creds(wks_cred, creds_state); + + result = true; + + done: + talloc_free(mem_ctx); + return result; +} + +/* + * Do a couple of schannel protected Netlogon ops: Interactive and Network + * login, and change the wks password + */ + +static bool schan(struct torture_context *tctx, + struct smbcli_state *cli, + struct cli_credentials *wks_creds, + struct cli_credentials *user_creds) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct dcerpc_pipe *net_pipe; + struct dcerpc_binding_handle *net_handle; + int i; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + torture_comment(tctx, "talloc_new failed\n"); + return false; + } + +#if 1 + status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree, + wks_creds, + DCERPC_AUTH_TYPE_SCHANNEL, + DCERPC_AUTH_LEVEL_PRIVACY, + "\\netlogon", &ndr_table_netlogon, &net_pipe); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "pipe_bind_smb_auth failed"); + net_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL); +#else + status = pipe_bind_smb(tctx, mem_ctx, cli->tree, + "\\netlogon", &ndr_table_netlogon, &net_pipe); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "pipe_bind_smb failed"); +#endif +#if 0 + net_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN | + DCERPC_DEBUG_PRINT_OUT; +#endif + net_handle = net_pipe->binding_handle; + + + for (i=2; i<4; i++) { + int flags; + DATA_BLOB chal, nt_resp, lm_resp, names_blob; + struct netlogon_creds_CredentialState *creds_state; + struct netr_Authenticator netr_auth, netr_auth2; + struct netr_NetworkInfo ninfo; + struct netr_PasswordInfo pinfo; + struct netr_LogonSamLogon r; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative; + struct netr_Authenticator return_authenticator; + + flags = CLI_CRED_LANMAN_AUTH | CLI_CRED_NTLM_AUTH | + CLI_CRED_NTLMv2_AUTH; + + chal = data_blob_talloc(mem_ctx, NULL, 8); + if (chal.data == NULL) { + torture_comment(tctx, "data_blob_talloc failed\n"); + goto done; + } + + generate_random_buffer(chal.data, chal.length); + names_blob = NTLMv2_generate_names_blob( + mem_ctx, + cli_credentials_get_workstation(wks_creds), + cli_credentials_get_domain(wks_creds)); + status = cli_credentials_get_ntlm_response( + user_creds, mem_ctx, &flags, chal, NULL, names_blob, + &lm_resp, &nt_resp, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "cli_credentials_get_ntlm_response failed:" + " %s\n", nt_errstr(status)); + goto done; + } + + creds_state = cli_credentials_get_netlogon_creds(wks_creds); + netlogon_creds_client_authenticator(creds_state, &netr_auth); + + ninfo.identity_info.account_name.string = + cli_credentials_get_username(user_creds); + ninfo.identity_info.domain_name.string = + cli_credentials_get_domain(user_creds); + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.workstation.string = + cli_credentials_get_workstation(user_creds); + memcpy(ninfo.challenge, chal.data, sizeof(ninfo.challenge)); + ninfo.nt.length = nt_resp.length; + ninfo.nt.data = nt_resp.data; + ninfo.lm.length = lm_resp.length; + ninfo.lm.data = lm_resp.data; + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + ZERO_STRUCT(netr_auth2); + r.in.computer_name = + cli_credentials_get_workstation(wks_creds); + r.in.credential = &netr_auth; + r.in.return_authenticator = &netr_auth2; + r.in.logon_level = NetlogonNetworkInformation; + r.in.validation_level = i; + r.in.logon = &logon; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + r.out.return_authenticator = &return_authenticator; + + status = dcerpc_netr_LogonSamLogon_r(net_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "netr_LogonSamLogon failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "netr_LogonSamLogon failed: %s\n", + nt_errstr(r.out.result)); + goto done; + } + + if ((r.out.return_authenticator == NULL) || + (!netlogon_creds_client_check(creds_state, + &r.out.return_authenticator->cred))) { + torture_comment(tctx, "Credentials check failed!\n"); + goto done; + } + + netlogon_creds_client_authenticator(creds_state, &netr_auth); + + pinfo.identity_info = ninfo.identity_info; + ZERO_STRUCT(pinfo.lmpassword.hash); + E_md4hash(cli_credentials_get_password(user_creds), + pinfo.ntpassword.hash); + + logon.password = &pinfo; + + /* + * We don't use this here: + * + * netlogon_creds_encrypt_samlogon_logon(creds_state, + * NetlogonInteractiveInformation, + * &logon); + * + * in order to detect bugs + */ + netlogon_creds_aes_encrypt(creds_state, pinfo.ntpassword.hash, 16); + + r.in.logon_level = NetlogonInteractiveInformation; + r.in.logon = &logon; + r.out.return_authenticator = &return_authenticator; + + status = dcerpc_netr_LogonSamLogon_r(net_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "netr_LogonSamLogon failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "netr_LogonSamLogon failed: %s\n", + nt_errstr(r.out.result)); + goto done; + } + + if ((r.out.return_authenticator == NULL) || + (!netlogon_creds_client_check(creds_state, + &r.out.return_authenticator->cred))) { + torture_comment(tctx, "Credentials check failed!\n"); + goto done; + } + } + + { + struct netr_ServerPasswordSet s; + char *password = generate_random_password(wks_creds, 8, 255); + struct netlogon_creds_CredentialState *creds_state; + struct netr_Authenticator credential, return_authenticator; + struct samr_Password new_password; + + s.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + s.in.computer_name = cli_credentials_get_workstation(wks_creds); + s.in.account_name = talloc_asprintf( + mem_ctx, "%s$", s.in.computer_name); + s.in.secure_channel_type = SEC_CHAN_WKSTA; + s.in.credential = &credential; + s.in.new_password = &new_password; + s.out.return_authenticator = &return_authenticator; + + E_md4hash(password, new_password.hash); + + creds_state = cli_credentials_get_netlogon_creds(wks_creds); + netlogon_creds_des_encrypt(creds_state, &new_password); + netlogon_creds_client_authenticator(creds_state, &credential); + + status = dcerpc_netr_ServerPasswordSet_r(net_handle, mem_ctx, &s); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "ServerPasswordSet - %s\n", nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_comment(tctx, "ServerPasswordSet - %s\n", nt_errstr(s.out.result)); + goto done; + } + + if (!netlogon_creds_client_check(creds_state, + &s.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + cli_credentials_set_password(wks_creds, password, + CRED_SPECIFIED); + } + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +/* + * Delete the wks account again + */ + +static bool leave(struct torture_context *tctx, + struct smbcli_state *cli, + struct cli_credentials *admin_creds, + struct cli_credentials *wks_creds) +{ + char *wks_name = talloc_asprintf( + NULL, "%s$", cli_credentials_get_workstation(wks_creds)); + bool ret; + + ret = delete_user(tctx, cli, admin_creds, wks_name); + talloc_free(wks_name); + return ret; +} + +/* + * Test the Samba3 DC code a bit. Join, do some schan netlogon ops, leave + */ + +static bool torture_netlogon_samba3(struct torture_context *torture) +{ + NTSTATUS status; + struct smbcli_state *cli; + struct cli_credentials *wks_creds; + const char *wks_name; + int i; + struct smbcli_options options; + struct smbcli_session_options session_options; + + wks_name = torture_setting_string(torture, "wksname", NULL); + torture_assert(torture, wks_name != NULL, "wksname not set"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(torture, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok(torture, status, "smbcli_full_connection failed\n"); + + wks_creds = cli_credentials_init(torture); + if (wks_creds == NULL) { + torture_fail(torture, "cli_credentials_init failed\n"); + } + + cli_credentials_set_conf(wks_creds, torture->lp_ctx); + cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA); + cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_password(wks_creds, + generate_random_password(wks_creds, 8, 255), + CRED_SPECIFIED); + + torture_assert(torture, + join3(torture, cli, false, NULL, wks_creds), + "join failed"); + + cli_credentials_set_domain( + samba_cmdline_get_creds(), + cli_credentials_get_domain(wks_creds), + CRED_SPECIFIED); + + for (i=0; i<2; i++) { + + /* Do this more than once, the routine "schan" changes + * the workstation password using the netlogon + * password change routine */ + + int j; + + torture_assert(torture, + auth2(torture, cli, wks_creds), + "auth2 failed"); + + for (j=0; j<2; j++) { + torture_assert(torture, + schan(torture, cli, wks_creds, + samba_cmdline_get_creds()), + "schan failed"); + } + } + + torture_assert(torture, + leave(torture, cli, NULL, wks_creds), + "leave failed"); + + return true; +} + +/* + * Do a simple join, testjoin and leave using specified smb and samr + * credentials + */ + +static bool test_join3(struct torture_context *tctx, + bool use_level25, + struct cli_credentials *smb_creds, + struct cli_credentials *samr_creds, + const char *wks_name) +{ + NTSTATUS status; + struct smbcli_state *cli; + struct cli_credentials *wks_creds; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + status = smbcli_full_connection(tctx, &cli, + torture_setting_string(tctx, "host", NULL), + lpcfg_smb_ports(tctx->lp_ctx), + "IPC$", NULL, lpcfg_socket_options(tctx->lp_ctx), + smb_creds, lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, + "smbcli_full_connection failed"); + + wks_creds = cli_credentials_init(cli); + torture_assert(tctx, wks_creds, "cli_credentials_init failed"); + + cli_credentials_set_conf(wks_creds, tctx->lp_ctx); + cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA); + cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED); + cli_credentials_set_password(wks_creds, + generate_random_password(wks_creds, 8, 255), + CRED_SPECIFIED); + + torture_assert(tctx, + join3(tctx, cli, use_level25, samr_creds, wks_creds), + "join failed"); + + cli_credentials_set_domain( + samba_cmdline_get_creds(), + cli_credentials_get_domain(wks_creds), + CRED_SPECIFIED); + + torture_assert(tctx, + auth2(tctx, cli, wks_creds), + "auth2 failed"); + + torture_assert(tctx, + leave(tctx, cli, samr_creds, wks_creds), + "leave failed"); + + talloc_free(cli); + + return true; +} + +/* + * Test the different session key variants. Do it by joining, this uses the + * session key in the setpassword routine. Test the join by doing the auth2. + */ + +static bool torture_samba3_sessionkey(struct torture_context *torture) +{ + struct cli_credentials *anon_creds; + const char *wks_name; + + + wks_name = torture_setting_string(torture, "wksname", NULL); + torture_assert(torture, wks_name != NULL, "wksname not set"); + + if (!(anon_creds = cli_credentials_init_anon(torture))) { + torture_fail(torture, "create_anon_creds failed\n"); + } + + cli_credentials_set_workstation(anon_creds, wks_name, CRED_SPECIFIED); + + + if (!torture_setting_bool(torture, "samba3", false)) { + + /* Samba3 in the build farm right now does this happily. Need + * to fix :-) */ + + if (test_join3(torture, false, anon_creds, NULL, wks_name)) { + torture_fail(torture, "join using anonymous bind on an anonymous smb " + "connection succeeded -- HUH??\n"); + } + } + + torture_assert(torture, + test_join3(torture, false, samba_cmdline_get_creds(), + NULL, wks_name), + "join using anonymous bind on an authenticated smb connection failed"); + + /* + * The following two are tests for setuserinfolevel 25 + */ + + torture_assert(torture, + test_join3(torture, true, samba_cmdline_get_creds(), + NULL, wks_name), + "join using anonymous bind on an authenticated smb connection failed"); + + return true; +} + +/* + * Sane wrapper around lsa_LookupNames + */ + +static struct dom_sid *name2sid(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct dcerpc_pipe *p, + const char *name, + const char *domain) +{ + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + struct lsa_Close c; + NTSTATUS status; + struct policy_handle handle; + struct lsa_LookupNames l; + struct lsa_TransSidArray sids; + struct lsa_RefDomainList *domains = NULL; + struct lsa_String lsa_name; + uint32_t count = 0; + struct dom_sid *result; + TALLOC_CTX *tmp_ctx; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NULL; + } + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + status = dcerpc_lsa_OpenPolicy2_r(b, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "OpenPolicy2 failed - %s\n", nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "OpenPolicy2 failed - %s\n", nt_errstr(r.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + sids.count = 0; + sids.sids = NULL; + + lsa_name.string = talloc_asprintf(tmp_ctx, "%s\\%s", domain, name); + + l.in.handle = &handle; + l.in.num_names = 1; + l.in.names = &lsa_name; + l.in.sids = &sids; + l.in.level = 1; + l.in.count = &count; + l.out.count = &count; + l.out.sids = &sids; + l.out.domains = &domains; + + status = dcerpc_lsa_LookupNames_r(b, tmp_ctx, &l); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "LookupNames of %s failed - %s\n", lsa_name.string, + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + if (!NT_STATUS_IS_OK(l.out.result)) { + torture_comment(tctx, "LookupNames of %s failed - %s\n", lsa_name.string, + nt_errstr(l.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + result = dom_sid_add_rid(mem_ctx, domains->domains[0].sid, + l.out.sids->sids[0].rid); + + c.in.handle = &handle; + c.out.handle = &handle; + + status = dcerpc_lsa_Close_r(b, tmp_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_lsa_Close failed - %s\n", nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + if (!NT_STATUS_IS_OK(c.out.result)) { + torture_comment(tctx, "dcerpc_lsa_Close failed - %s\n", nt_errstr(c.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + talloc_free(tmp_ctx); + return result; +} + +/* + * Find out the user SID on this connection + */ + +static struct dom_sid *whoami(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_tree *tree) +{ + struct dcerpc_pipe *lsa; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_GetUserName r; + NTSTATUS status; + struct lsa_String *authority_name_p = NULL; + struct lsa_String *account_name_p = NULL; + struct dom_sid *result; + + status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\lsarpc", + &ndr_table_lsarpc, &lsa); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "Could not bind to LSA: %s\n", + nt_errstr(status)); + return NULL; + } + lsa_handle = lsa->binding_handle; + + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "GetUserName failed - %s\n", + nt_errstr(status)); + talloc_free(lsa); + return NULL; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_warning(tctx, "GetUserName failed - %s\n", + nt_errstr(r.out.result)); + talloc_free(lsa); + return NULL; + } + + result = name2sid(tctx, mem_ctx, lsa, account_name_p->string, + authority_name_p->string); + + talloc_free(lsa); + return result; +} + +static int destroy_tree(struct smbcli_tree *tree) +{ + smb_tree_disconnect(tree); + return 0; +} + +/* + * Do a tcon, given a session + */ + +static NTSTATUS secondary_tcon(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_session *session, + const char *sharename, + struct smbcli_tree **res) +{ + struct smbcli_tree *result; + TALLOC_CTX *tmp_ctx; + union smb_tcon tcon; + NTSTATUS status; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + return NT_STATUS_NO_MEMORY; + } + + if (!(result = smbcli_tree_init(session, mem_ctx, false))) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = "?????"; + + status = smb_raw_tcon(result, tmp_ctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smb_raw_tcon failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return status; + } + + result->tid = tcon.tconx.out.tid; + + if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) { + smb1cli_session_protect_session_key(result->session->smbXcli); + } + + result = talloc_steal(mem_ctx, result); + talloc_set_destructor(result, destroy_tree); + talloc_free(tmp_ctx); + *res = result; + return NT_STATUS_OK; +} + +/* + * Test the getusername behaviour + */ + +static bool torture_samba3_rpc_getusername(struct torture_context *torture) +{ + NTSTATUS status; + struct smbcli_state *cli; + bool ret = true; + struct dom_sid *user_sid; + struct dom_sid *created_sid; + struct cli_credentials *anon_creds; + struct cli_credentials *user_creds; + char *domain_name; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + if (!(anon_creds = cli_credentials_init_anon(torture))) { + torture_fail(torture, "create_anon_creds failed\n"); + } + + status = smbcli_full_connection( + torture, &cli, torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), anon_creds, + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok(torture, status, "anon smbcli_full_connection failed\n"); + + if (!(user_sid = whoami(torture, torture, cli->tree))) { + torture_fail(torture, "whoami on anon connection failed\n"); + } + + torture_assert_sid_equal(torture, user_sid, dom_sid_parse_talloc(torture, "s-1-5-7"), + "Anon lsa_GetUserName returned unexpected SID"); + + talloc_free(cli); + + status = smbcli_full_connection( + torture, &cli, torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), torture->ev, &options, + &session_options, lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok(torture, status, "smbcli_full_connection failed\n"); + + if (!(user_sid = whoami(torture, torture, cli->tree))) { + torture_fail(torture, "whoami on auth'ed connection failed\n"); + } + + if (!(user_creds = cli_credentials_init(torture))) { + torture_fail(torture, "cli_credentials_init failed\n"); + } + + cli_credentials_set_conf(user_creds, torture->lp_ctx); + cli_credentials_set_username(user_creds, "torture_username", + CRED_SPECIFIED); + cli_credentials_set_password(user_creds, + generate_random_password(user_creds, 8, 255), + CRED_SPECIFIED); + + if (!create_user(torture, torture, cli, NULL, + cli_credentials_get_username(user_creds), + cli_credentials_get_password(user_creds), + &domain_name, &created_sid)) { + torture_fail(torture, "create_user failed\n"); + } + + cli_credentials_set_domain(user_creds, domain_name, + CRED_SPECIFIED); + + { + struct smbcli_session *session2; + struct smb_composite_sesssetup setup; + struct smbcli_tree *tree; + + session2 = smbcli_session_init(cli->transport, torture, false, session_options); + if (session2 == NULL) { + torture_fail(torture, "smbcli_session_init failed\n"); + } + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.workgroup = ""; + setup.in.credentials = user_creds; + setup.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(session2, &setup); + torture_assert_ntstatus_ok(torture, status, "session setup with new user failed"); + + session2->vuid = setup.out.vuid; + + if (!NT_STATUS_IS_OK(secondary_tcon(torture, torture, session2, + "IPC$", &tree))) { + torture_fail(torture, "secondary_tcon failed\n"); + } + + if (!(user_sid = whoami(torture, torture, tree))) { + torture_fail_goto(torture, del, "whoami on user connection failed\n"); + ret = false; + goto del; + } + + talloc_free(tree); + } + + torture_comment(torture, "Created %s, found %s\n", + dom_sid_string(torture, created_sid), + dom_sid_string(torture, user_sid)); + + if (!dom_sid_equal(created_sid, user_sid)) { + ret = false; + } + + del: + if (!delete_user(torture, cli, + NULL, + cli_credentials_get_username(user_creds))) { + torture_fail(torture, "delete_user failed\n"); + } + + return ret; +} + +static bool test_NetShareGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *sharename) +{ + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + union srvsvc_NetShareInfo info; + uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007, 1501 }; + int i; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + r.out.info = &info; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ictr.ctr0; + if (ctr->count > 0) { + *one_sharename = ctr->array[0].name; + } + } + } + + return ret; +} + +static bool torture_samba3_rpc_srvsvc(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + const char *sharename = NULL; + bool ret = true; + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_srvsvc), + "failed to open srvsvc"); + + ret &= test_NetShareEnum(torture, p, &sharename); + if (sharename == NULL) { + torture_comment(torture, "did not get sharename\n"); + } else { + ret &= test_NetShareGetInfo(torture, p, sharename); + } + + return ret; +} + +/* + * Do a ReqChallenge/Auth2 with a random wks name, make sure it returns + * NT_STATUS_NO_SAM_ACCOUNT + */ + +static bool torture_samba3_rpc_randomauth2(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + struct dcerpc_pipe *net_pipe; + struct dcerpc_binding_handle *net_handle; + char *wksname; + bool result = false; + NTSTATUS status; + struct netr_ServerReqChallenge r; + struct netr_Credential netr_cli_creds; + struct netr_Credential netr_srv_creds; + uint32_t negotiate_flags; + struct netr_ServerAuthenticate2 a; + struct netlogon_creds_CredentialState *creds_state; + struct netr_Credential netr_cred; + struct samr_Password mach_pw; + struct smbcli_state *cli; + + if (!(mem_ctx = talloc_new(torture))) { + torture_comment(torture, "talloc_new failed\n"); + return false; + } + + if (!(wksname = generate_random_str_list( + mem_ctx, 14, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) { + torture_comment(torture, "generate_random_str_list failed\n"); + goto done; + } + + if (!(torture_open_connection_share( + mem_ctx, &cli, + torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + torture_comment(torture, "IPC$ connection failed\n"); + goto done; + } + + status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\netlogon", + &ndr_table_netlogon, &net_pipe); + torture_assert_ntstatus_ok_goto(torture, status, result, done, + "pipe_bind_smb failed"); + net_handle = net_pipe->binding_handle; + + r.in.computer_name = wksname; + r.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + if (r.in.server_name == NULL) { + torture_comment(torture, "talloc_asprintf failed\n"); + goto done; + } + generate_random_buffer(netr_cli_creds.data, + sizeof(netr_cli_creds.data)); + r.in.credentials = &netr_cli_creds; + r.out.return_credentials = &netr_srv_creds; + + status = dcerpc_netr_ServerReqChallenge_r(net_handle, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "netr_ServerReqChallenge failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(torture, "netr_ServerReqChallenge failed: %s\n", + nt_errstr(r.out.result)); + goto done; + } + + negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS; + E_md4hash("foobar", mach_pw.hash); + + a.in.server_name = talloc_asprintf( + mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe)); + a.in.account_name = talloc_asprintf( + mem_ctx, "%s$", wksname); + a.in.computer_name = wksname; + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.negotiate_flags = &negotiate_flags; + a.out.negotiate_flags = &negotiate_flags; + a.in.credentials = &netr_cred; + a.out.return_credentials = &netr_cred; + + creds_state = netlogon_creds_client_init(mem_ctx, + a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + r.in.credentials, + r.out.return_credentials, &mach_pw, + &netr_cred, negotiate_flags); + torture_assert(torture, (creds_state != NULL), "memory allocation failed"); + + status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_NO_TRUST_SAM_ACCOUNT)) { + torture_comment(torture, "dcerpc_netr_ServerAuthenticate2 returned %s, " + "expected NT_STATUS_NO_TRUST_SAM_ACCOUNT\n", + nt_errstr(a.out.result)); + goto done; + } + + result = true; + done: + talloc_free(mem_ctx); + return result; +} + +static struct security_descriptor *get_sharesec(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_session *sess, + const char *sharename) +{ + struct smbcli_tree *tree; + TALLOC_CTX *tmp_ctx; + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + union srvsvc_NetShareInfo info; + struct security_descriptor *result; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + torture_comment(tctx, "talloc_new failed\n"); + return NULL; + } + + if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) { + torture_comment(tctx, "secondary_tcon failed\n"); + talloc_free(tmp_ctx); + return NULL; + } + + status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\srvsvc", + &ndr_table_srvsvc, &p); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "could not bind to srvsvc pipe: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + b = p->binding_handle; + +#if 0 + p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + + r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + r.in.level = 502; + r.out.info = &info; + + status = dcerpc_srvsvc_NetShareGetInfo_r(b, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "srvsvc_NetShareGetInfo failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NULL; + } + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "srvsvc_NetShareGetInfo failed: %s\n", + win_errstr(r.out.result)); + talloc_free(tmp_ctx); + return NULL; + } + + result = talloc_steal(mem_ctx, info.info502->sd_buf.sd); + talloc_free(tmp_ctx); + return result; +} + +static NTSTATUS set_sharesec(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_session *sess, + const char *sharename, + struct security_descriptor *sd) +{ + struct smbcli_tree *tree; + TALLOC_CTX *tmp_ctx; + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + NTSTATUS status; + struct sec_desc_buf i; + struct srvsvc_NetShareSetInfo r; + union srvsvc_NetShareInfo info; + uint32_t error = 0; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + torture_comment(tctx, "talloc_new failed\n"); + return NT_STATUS_NO_MEMORY; + } + + if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) { + torture_comment(tctx, "secondary_tcon failed\n"); + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\srvsvc", + &ndr_table_srvsvc, &p); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "could not bind to srvsvc pipe: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + b = p->binding_handle; + +#if 0 + p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT; +#endif + + r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.share_name = sharename; + r.in.level = 1501; + i.sd = sd; + info.info1501 = &i; + r.in.info = &info; + r.in.parm_error = &error; + + status = dcerpc_srvsvc_NetShareSetInfo_r(b, tmp_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "srvsvc_NetShareSetInfo failed: %s\n", + nt_errstr(status)); + } + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, "srvsvc_NetShareSetInfo failed: %s\n", + win_errstr(r.out.result)); + status = werror_to_ntstatus(r.out.result); + } + talloc_free(tmp_ctx); + return status; +} + +bool try_tcon(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct security_descriptor *orig_sd, + struct smbcli_session *session, + const char *sharename, const struct dom_sid *user_sid, + unsigned int access_mask, NTSTATUS expected_tcon, + NTSTATUS expected_mkdir) +{ + TALLOC_CTX *tmp_ctx; + struct smbcli_tree *rmdir_tree, *tree; + struct dom_sid *domain_sid; + uint32_t rid; + struct security_descriptor *sd; + NTSTATUS status; + bool ret = true; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + torture_comment(tctx, "talloc_new failed\n"); + return false; + } + + status = secondary_tcon(tctx, tmp_ctx, session, sharename, &rmdir_tree); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "first tcon to delete dir failed\n"); + talloc_free(tmp_ctx); + return false; + } + + smbcli_rmdir(rmdir_tree, "sharesec_testdir"); + + if (!NT_STATUS_IS_OK(dom_sid_split_rid(tmp_ctx, user_sid, + &domain_sid, &rid))) { + torture_comment(tctx, "dom_sid_split_rid failed\n"); + talloc_free(tmp_ctx); + return false; + } + + sd = security_descriptor_dacl_create( + tmp_ctx, 0, "S-1-5-32-544", + dom_sid_string(mem_ctx, dom_sid_add_rid(mem_ctx, domain_sid, + DOMAIN_RID_USERS)), + dom_sid_string(mem_ctx, user_sid), + SEC_ACE_TYPE_ACCESS_ALLOWED, access_mask, 0, NULL); + if (sd == NULL) { + torture_comment(tctx, "security_descriptor_dacl_create failed\n"); + talloc_free(tmp_ctx); + return false; + } + + status = set_sharesec(tctx, mem_ctx, session, sharename, sd); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "custom set_sharesec failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return false; + } + + status = secondary_tcon(tctx, tmp_ctx, session, sharename, &tree); + if (!NT_STATUS_EQUAL(status, expected_tcon)) { + torture_comment(tctx, "Expected %s, got %s\n", nt_errstr(expected_tcon), + nt_errstr(status)); + ret = false; + goto done; + } + + if (!NT_STATUS_IS_OK(status)) { + /* An expected non-access, no point in trying to write */ + goto done; + } + + status = smbcli_mkdir(tree, "sharesec_testdir"); + if (!NT_STATUS_EQUAL(status, expected_mkdir)) { + torture_warning(tctx, "Expected %s, got %s\n", + nt_errstr(expected_mkdir), nt_errstr(status)); + ret = false; + } + + done: + smbcli_rmdir(rmdir_tree, "sharesec_testdir"); + + status = set_sharesec(tctx, mem_ctx, session, sharename, orig_sd); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "custom set_sharesec failed: %s\n", + nt_errstr(status)); + talloc_free(tmp_ctx); + return false; + } + + talloc_free(tmp_ctx); + return ret; +} + +static bool torture_samba3_rpc_sharesec(struct torture_context *torture) +{ + struct smbcli_state *cli = NULL; + struct security_descriptor *sd = NULL; + struct dom_sid *user_sid = NULL; + const char *testuser_passwd = NULL; + struct cli_credentials *test_credentials = NULL; + struct smbcli_options options; + struct smbcli_session_options session_options; + NTSTATUS status; + struct test_join *tj = NULL; + struct dcerpc_pipe *lsa_pipe = NULL; + const char *priv_array[1]; + + /* Create a new user. The normal user has SeBackup and SeRestore + privs so we can't lock them out with a share security descriptor. */ + tj = torture_create_testuser(torture, + "sharesec_user", + torture_setting_string(torture, "workgroup", NULL), + ACB_NORMAL, + &testuser_passwd); + if (!tj) { + torture_fail(torture, "Creating sharesec_user failed\n"); + } + + /* Give them SeDiskOperatorPrivilege but no other privs. */ + status = torture_rpc_connection(torture, &lsa_pipe, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "Error connecting to LSA pipe"); + } + + priv_array[0] = "SeDiskOperatorPrivilege"; + if (!torture_setup_privs(torture, + lsa_pipe, + 1, + priv_array, + torture_join_user_sid(tj))) { + talloc_free(lsa_pipe); + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "Failed to setup privs\n"); + } + talloc_free(lsa_pipe); + + test_credentials = cli_credentials_init(torture); + cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, lpcfg_workgroup(torture->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, "sharesec_user", CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED); + + ZERO_STRUCT(options); + ZERO_STRUCT(session_options); + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(torture, + &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + NULL, + lpcfg_socket_options(torture->lp_ctx), + test_credentials, + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, + &options, + &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(cli); + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "Failed to open connection\n"); + } + + if (!(user_sid = whoami(torture, torture, cli->tree))) { + talloc_free(cli); + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "whoami failed\n"); + } + + sd = get_sharesec(torture, torture, cli->session, + torture_setting_string(torture, "share", NULL)); + + if (!try_tcon(torture, torture, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, 0, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK)) { + talloc_free(cli); + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "failed to test tcon with 0 access_mask"); + } + + if (!try_tcon(torture, torture, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, SEC_FILE_READ_DATA, NT_STATUS_OK, + NT_STATUS_MEDIA_WRITE_PROTECTED)) { + talloc_free(cli); + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + torture_fail(torture, "failed to test tcon with SEC_FILE_READ_DATA access_mask"); + } + + /* sharesec_user doesn't have any rights on the underlying file system. + Go back to the normal user. */ + + talloc_free(cli); + cli = NULL; + torture_delete_testuser(torture, tj, "sharesec_user"); + talloc_free(tj); + tj = NULL; + + if (!(torture_open_connection_share( + torture, &cli, torture, torture_setting_string(torture, "host", NULL), + "IPC$", torture->ev))) { + torture_fail(torture, "IPC$ connection failed\n"); + } + + if (!(user_sid = whoami(torture, torture, cli->tree))) { + torture_fail(torture, "whoami failed\n"); + } + torture_assert(torture, try_tcon( + torture, torture, sd, cli->session, + torture_setting_string(torture, "share", NULL), + user_sid, SEC_FILE_ALL, NT_STATUS_OK, NT_STATUS_OK), + "failed to test tcon with SEC_FILE_ALL access_mask"); + + return true; +} + +static bool torture_samba3_rpc_lsa(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + struct policy_handle lsa_handle; + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_lsarpc), + "failed to setup lsarpc"); + + b = p->binding_handle; + + { + struct lsa_ObjectAttribute attr; + struct lsa_OpenPolicy2 o; + o.in.system_name = talloc_asprintf( + torture, "\\\\%s", dcerpc_server_name(p)); + ZERO_STRUCT(attr); + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &lsa_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_lsa_OpenPolicy2_r(b, torture, &o), + "dcerpc_lsa_OpenPolicy2 failed"); + torture_assert_ntstatus_ok(torture, o.out.result, + "dcerpc_lsa_OpenPolicy2 failed"); + } + + { + int i; + int levels[] = { 2,3,5,6 }; + + for (i=0; iev), + "IPC$ connection failed"); + + torture_assert_ntstatus_ok(tctx, + get_servername(tctx, cli->tree, servername), + "get_servername failed"); + + talloc_free(cli); + + return true; +} + +static bool find_printers(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char ***printers, + size_t *num_printers) +{ + struct srvsvc_NetShareEnum r; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr1 c1_in; + struct srvsvc_NetShareCtr1 *c1; + uint32_t totalentries = 0; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(c1_in); + info_ctr.level = 1; + info_ctr.ctr.ctr1 = &c1_in; + + r.in.server_unc = talloc_asprintf( + tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + torture_assert_ntstatus_ok(tctx, + dcerpc_srvsvc_NetShareEnum_r(b, tctx, &r), + "NetShareEnum level 1 failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetShareEnum level 1 failed"); + + *printers = NULL; + *num_printers = 0; + c1 = r.out.info_ctr->ctr.ctr1; + for (i=0; icount; i++) { + if (c1->array[i].type != STYPE_PRINTQ) { + continue; + } + if (!add_string_to_array(tctx, c1->array[i].name, + printers, num_printers)) { + return false; + } + } + + return true; +} + +static bool enumprinters(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, int level, int *num_printers) +{ + struct spoolss_EnumPrinters r; + DATA_BLOB blob; + uint32_t needed; + uint32_t count; + union spoolss_PrinterInfo *info; + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = talloc_asprintf(tctx, "\\\\%s", servername); + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "dcerpc_spoolss_EnumPrinters failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "EnumPrinters unexpected return code should be WERR_INSUFFICIENT_BUFFER"); + + blob = data_blob_talloc_zero(tctx, needed); + if (blob.data == NULL) { + return false; + } + + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "dcerpc_spoolss_EnumPrinters failed"); + torture_assert_werr_ok(tctx, r.out.result, + "dcerpc_spoolss_EnumPrinters failed"); + + *num_printers = count; + + return true; +} + +static bool getprinterinfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, int level, + union spoolss_PrinterInfo **res) +{ + struct spoolss_GetPrinter r; + DATA_BLOB blob; + uint32_t needed; + + r.in.handle = handle; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinter_r(b, tctx, &r), + "dcerpc_spoolss_GetPrinter failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "GetPrinter unexpected return code should be WERR_INSUFFICIENT_BUFFER"); + + r.in.handle = handle; + r.in.level = level; + blob = data_blob_talloc_zero(tctx, needed); + if (blob.data == NULL) { + return false; + } + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinter_r(b, tctx, &r), + "dcerpc_spoolss_GetPrinter failed"); + torture_assert_werr_ok(tctx, r.out.result, + "dcerpc_spoolss_GetPrinter failed"); + + if (res != NULL) { + *res = talloc_steal(tctx, r.out.info); + } + + return true; +} + +static bool torture_samba3_rpc_spoolss(struct torture_context *torture) +{ + struct dcerpc_pipe *p, *p2; + struct dcerpc_binding_handle *b; + struct policy_handle server_handle, printer_handle; + const char **printers; + size_t num_printers; + struct spoolss_UserLevel1 userlevel1; + char *servername; + + torture_assert(torture, + rap_get_servername(torture, &servername), + "failed to rap servername"); + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p2, &ndr_table_srvsvc), + "failed to setup srvsvc"); + + torture_assert(torture, + find_printers(torture, p2, &printers, &num_printers), + "failed to find printers via srvsvc"); + + talloc_free(p2); + + if (num_printers == 0) { + torture_skip(torture, "Did not find printers\n"); + return true; + } + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_spoolss), + "failed to setup spoolss"); + + b = p->binding_handle; + + ZERO_STRUCT(userlevel1); + userlevel1.client = talloc_asprintf( + torture, "\\\\%s", lpcfg_netbios_name(torture->lp_ctx)); + userlevel1.user = cli_credentials_get_username( + samba_cmdline_get_creds()); + userlevel1.build = 2600; + userlevel1.major = 3; + userlevel1.minor = 0; + userlevel1.processor = 0; + + { + struct spoolss_OpenPrinterEx r; + + ZERO_STRUCT(r); + r.in.printername = talloc_asprintf(torture, "\\\\%s", + servername); + r.in.datatype = NULL; + r.in.access_mask = 0; + r.in.userlevel_ctr.level = 1; + r.in.userlevel_ctr.user_info.level1 = &userlevel1; + r.out.handle = &server_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_spoolss_OpenPrinterEx_r(b, torture, &r), + "dcerpc_spoolss_OpenPrinterEx failed"); + torture_assert_werr_ok(torture, r.out.result, + "dcerpc_spoolss_OpenPrinterEx failed"); + } + + { + struct spoolss_ClosePrinter r; + + r.in.handle = &server_handle; + r.out.handle = &server_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_spoolss_ClosePrinter_r(b, torture, &r), + "dcerpc_spoolss_ClosePrinter failed"); + torture_assert_werr_ok(torture, r.out.result, + "dcerpc_spoolss_ClosePrinter failed"); + } + + { + struct spoolss_OpenPrinterEx r; + + ZERO_STRUCT(r); + r.in.printername = talloc_asprintf( + torture, "\\\\%s\\%s", servername, printers[0]); + r.in.datatype = NULL; + r.in.access_mask = 0; + r.in.userlevel_ctr.level = 1; + r.in.userlevel_ctr.user_info.level1 = &userlevel1; + r.out.handle = &printer_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_spoolss_OpenPrinterEx_r(b, torture, &r), + "dcerpc_spoolss_OpenPrinterEx failed"); + torture_assert_werr_ok(torture, r.out.result, + "dcerpc_spoolss_OpenPrinterEx failed"); + } + + { + int i; + + for (i=0; i<8; i++) { + torture_assert(torture, + getprinterinfo(torture, b, &printer_handle, i, NULL), + talloc_asprintf(torture, "getprinterinfo %d failed", i)); + } + } + + { + struct spoolss_ClosePrinter r; + + r.in.handle = &printer_handle; + r.out.handle = &printer_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_spoolss_ClosePrinter_r(b, torture, &r), + "dcerpc_spoolss_ClosePrinter failed"); + torture_assert_werr_ok(torture, r.out.result, + "dcerpc_spoolss_ClosePrinter failed"); + } + + { + int num_enumerated; + + torture_assert(torture, + enumprinters(torture, b, servername, 1, &num_enumerated), + "enumprinters failed"); + + torture_assert_int_equal(torture, num_printers, num_enumerated, + "netshareenum / enumprinters lvl 1 numprinter mismatch"); + } + + { + int num_enumerated; + + torture_assert(torture, + enumprinters(torture, b, servername, 2, &num_enumerated), + "enumprinters failed"); + + torture_assert_int_equal(torture, num_printers, num_enumerated, + "netshareenum / enumprinters lvl 2 numprinter mismatch"); + } + + return true; +} + +static bool torture_samba3_rpc_wkssvc(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + char *servername; + + torture_assert(torture, + rap_get_servername(torture, &servername), + "failed to rap servername"); + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_wkssvc), + "failed to setup wkssvc"); + + b = p->binding_handle; + + { + struct wkssvc_NetWkstaInfo100 wks100; + union wkssvc_NetWkstaInfo info; + struct wkssvc_NetWkstaGetInfo r; + + r.in.server_name = "\\foo"; + r.in.level = 100; + info.info100 = &wks100; + r.out.info = &info; + + torture_assert_ntstatus_ok(torture, + dcerpc_wkssvc_NetWkstaGetInfo_r(b, torture, &r), + "dcerpc_wkssvc_NetWksGetInfo failed"); + torture_assert_werr_ok(torture, r.out.result, + "dcerpc_wkssvc_NetWksGetInfo failed"); + + torture_assert_str_equal(torture, servername, r.out.info->info100->server_name, + "servername RAP / DCERPC inconsistency"); + } + + return true; +} + +static bool winreg_close(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct winreg_CloseKey c; + + c.in.handle = c.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_CloseKey_r(b, tctx, &c), + "winreg_CloseKey failed"); + torture_assert_werr_ok(tctx, c.out.result, + "winreg_CloseKey failed"); + + return true; +} + +static bool enumvalues(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + uint32_t enum_index = 0; + + while (1) { + struct winreg_EnumValue r; + struct winreg_ValNameBuf name; + enum winreg_Type type = 0; + uint8_t buf8[1024]; + NTSTATUS status; + uint32_t size, length; + + ZERO_STRUCT(buf8); + r.in.handle = handle; + r.in.enum_index = enum_index; + name.name = ""; + name.size = 1024; + r.in.name = r.out.name = &name; + size = 1024; + length = 5; + r.in.type = &type; + r.in.value = buf8; + r.in.size = &size; + r.in.length = &length; + + status = dcerpc_winreg_EnumValue_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + return true; + } + enum_index += 1; + } +} + +static bool enumkeys(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + int depth) +{ + struct winreg_EnumKey r; + struct winreg_StringBuf kclass, name; + NTSTATUS status; + NTTIME t = 0; + + if (depth <= 0) { + return true; + } + + kclass.name = ""; + kclass.size = 1024; + + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.in.keyclass = &kclass; + r.out.name = &name; + r.in.last_changed_time = &t; + + do { + struct winreg_OpenKey o; + struct policy_handle key_handle; + int i; + + name.name = NULL; + name.size = 1024; + + status = dcerpc_winreg_EnumKey_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) { + /* We're done enumerating */ + return true; + } + + for (i=0; i<10-depth; i++) { + torture_comment(tctx, " "); + } + torture_comment(tctx, "%s\n", r.out.name->name); + + o.in.parent_handle = handle; + o.in.keyname.name = r.out.name->name; + o.in.options = 0; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &key_handle; + + status = dcerpc_winreg_OpenKey_r(b, tctx, &o); + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(o.out.result)) { + enumkeys(tctx, b, &key_handle, depth-1); + enumvalues(tctx, b, &key_handle); + torture_assert(tctx, winreg_close(tctx, b, &key_handle), ""); + } + + r.in.enum_index += 1; + } while(true); + + return true; +} + +typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_binding_handle *, TALLOC_CTX *, void *); + +static bool test_Open3(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *name, winreg_open_fn open_fn) +{ + struct policy_handle handle; + struct winreg_OpenHKLM r; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, + open_fn(b, tctx, &r), + talloc_asprintf(tctx, "%s failed", name)); + torture_assert_werr_ok(tctx, r.out.result, + talloc_asprintf(tctx, "%s failed", name)); + + enumkeys(tctx, b, &handle, 4); + + torture_assert(tctx, + winreg_close(tctx, b, &handle), + "dcerpc_CloseKey failed"); + + return true; +} + +static bool torture_samba3_rpc_winreg(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + bool ret = true; + struct { + const char *name; + winreg_open_fn fn; + } open_fns[] = { + {"OpenHKLM", (winreg_open_fn)dcerpc_winreg_OpenHKLM_r }, + {"OpenHKU", (winreg_open_fn)dcerpc_winreg_OpenHKU_r }, + {"OpenHKPD", (winreg_open_fn)dcerpc_winreg_OpenHKPD_r }, + {"OpenHKPT", (winreg_open_fn)dcerpc_winreg_OpenHKPT_r }, + {"OpenHKCR", (winreg_open_fn)dcerpc_winreg_OpenHKCR_r }}; +#if 0 + int i; +#endif + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_winreg), + "failed to setup winreg"); + + b = p->binding_handle; + +#if 1 + ret = test_Open3(torture, b, open_fns[0].name, open_fns[0].fn); +#else + for (i = 0; i < ARRAY_SIZE(open_fns); i++) { + if (!test_Open3(torture, b, open_fns[i].name, open_fns[i].fn)) + ret = false; + } +#endif + return ret; +} + +static bool get_shareinfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, + const char *share, + struct srvsvc_NetShareInfo502 **info502) +{ + struct srvsvc_NetShareGetInfo r; + union srvsvc_NetShareInfo info; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", servername); + r.in.share_name = share; + r.in.level = 502; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_srvsvc_NetShareGetInfo_r(b, tctx, &r), + "srvsvc_NetShareGetInfo failed"); + torture_assert_werr_ok(tctx, r.out.result, + "srvsvc_NetShareGetInfo failed"); + + *info502 = talloc_move(tctx, &info.info502); + + return true; +} + +/* + * Get us a handle on HKLM\ + */ + +static bool get_hklm_handle(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct winreg_OpenHKLM r; + struct policy_handle result; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &result; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenHKLM_r(b, tctx, &r), + "OpenHKLM failed"); + torture_assert_werr_ok(tctx, r.out.result, + "OpenHKLM failed"); + + *handle = result; + + return true; +} + +static bool torture_samba3_createshare(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *sharename) +{ + struct policy_handle hklm; + struct policy_handle new_handle; + struct winreg_CreateKey c; + struct winreg_CloseKey cl; + enum winreg_CreateAction action_taken = REG_ACTION_NONE; + + ZERO_STRUCT(c); + ZERO_STRUCT(cl); + ZERO_STRUCT(hklm); + ZERO_STRUCT(new_handle); + + c.in.handle = &hklm; + c.in.name.name = talloc_asprintf( + tctx, "software\\samba\\smbconf\\%s", sharename); + torture_assert(tctx, c.in.name.name, "talloc_asprintf failed"); + + c.in.keyclass.name = ""; + c.in.options = 0; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.in.secdesc = NULL; + c.in.action_taken = &action_taken; + c.out.new_handle = &new_handle; + c.out.action_taken = &action_taken; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_CreateKey_r(b, tctx, &c), + "OpenKey failed"); + torture_assert_werr_ok(tctx, c.out.result, + "OpenKey failed"); + + cl.in.handle = &new_handle; + cl.out.handle = &new_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_CloseKey_r(b, tctx, &cl), + "CloseKey failed"); + torture_assert_werr_ok(tctx, cl.out.result, + "CloseKey failed"); + + return true; +} + +static bool torture_samba3_deleteshare(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *sharename) +{ + struct policy_handle hklm; + struct winreg_DeleteKey d; + + torture_assert(tctx, + get_hklm_handle(tctx, b, &hklm), + "get_hklm_handle failed"); + + d.in.handle = &hklm; + d.in.key.name = talloc_asprintf( + tctx, "software\\samba\\smbconf\\%s", sharename); + torture_assert(tctx, d.in.key.name, "talloc_asprintf failed"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_DeleteKey_r(b, tctx, &d), + "DeleteKey failed"); + torture_assert_werr_ok(tctx, d.out.result, + "DeleteKey failed"); + + return true; +} + +static bool torture_samba3_setconfig(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *sharename, + const char *parameter, + const char *value) +{ + struct policy_handle hklm, key_handle; + struct winreg_OpenKey o; + struct winreg_SetValue s; + uint32_t type; + DATA_BLOB val; + + torture_assert(tctx, + get_hklm_handle(tctx, b, &hklm), + "get_hklm_handle failed"); + + o.in.parent_handle = &hklm; + o.in.keyname.name = talloc_asprintf( + tctx, "software\\samba\\smbconf\\%s", sharename); + torture_assert(tctx, o.in.keyname.name, "talloc_asprintf failed"); + + o.in.options = 0; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &key_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenKey_r(b, tctx, &o), + "OpenKey failed"); + torture_assert_werr_ok(tctx, o.out.result, + "OpenKey failed"); + + torture_assert(tctx, + reg_string_to_val(tctx, "REG_SZ", value, &type, &val), + "reg_string_to_val failed"); + + s.in.handle = &key_handle; + s.in.name.name = parameter; + s.in.type = type; + s.in.data = val.data; + s.in.size = val.length; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_SetValue_r(b, tctx, &s), + "SetValue failed"); + torture_assert_werr_ok(tctx, s.out.result, + "SetValue failed"); + + return true; +} + +static bool torture_samba3_regconfig(struct torture_context *torture) +{ + struct srvsvc_NetShareInfo502 *i = NULL; + const char *comment = "Dummer Kommentar"; + struct dcerpc_pipe *srvsvc_pipe, *winreg_pipe; + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &srvsvc_pipe, &ndr_table_srvsvc), + "failed to setup srvsvc"); + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &winreg_pipe, &ndr_table_winreg), + "failed to setup winreg"); + + torture_assert(torture, + torture_samba3_createshare(torture, winreg_pipe->binding_handle, "blubber"), + "torture_samba3_createshare failed"); + + torture_assert(torture, + torture_samba3_setconfig(torture, winreg_pipe->binding_handle, "blubber", "comment", comment), + "torture_samba3_setconfig failed"); + + torture_assert(torture, + get_shareinfo(torture, srvsvc_pipe->binding_handle, dcerpc_server_name(srvsvc_pipe), "blubber", &i), + "get_shareinfo failed"); + + torture_assert_str_equal(torture, comment, i->comment, + "got unexpected comment"); + + torture_assert(torture, + torture_samba3_deleteshare(torture, winreg_pipe->binding_handle, "blubber"), + "torture_samba3_deleteshare failed"); + + return true; +} + +/* + * Test that even with a result of 0 rids the array is returned as a + * non-NULL pointer. Yes, XP does notice. + */ + +bool torture_samba3_getaliasmembership_0(struct torture_context *torture) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + struct samr_Connect2 c; + struct samr_OpenDomain o; + struct dom_sid sid; + struct lsa_SidPtr ptr; + struct lsa_SidArray sids; + struct samr_GetAliasMembership g; + struct samr_Ids rids; + struct policy_handle samr, domain; + + torture_assert_ntstatus_ok(torture, + torture_rpc_connection(torture, &p, &ndr_table_samr), + "failed to setup samr"); + + b = p->binding_handle; + + c.in.system_name = NULL; + c.in.access_mask = SAMR_ACCESS_LOOKUP_DOMAIN; + c.out.connect_handle = &samr; + torture_assert_ntstatus_ok(torture, + dcerpc_samr_Connect2_r(b, torture, &c), + ""); + torture_assert_ntstatus_ok(torture, c.out.result, + ""); + dom_sid_parse("S-1-5-32", &sid); + o.in.connect_handle = &samr; + o.in.access_mask = SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS; + o.in.sid = &sid; + o.out.domain_handle = &domain; + torture_assert_ntstatus_ok(torture, + dcerpc_samr_OpenDomain_r(b, torture, &o), + ""); + torture_assert_ntstatus_ok(torture, o.out.result, + ""); + dom_sid_parse("S-1-2-3-4-5", &sid); + ptr.sid = &sid; + sids.num_sids = 1; + sids.sids = &ptr; + g.in.domain_handle = &domain; + g.in.sids = &sids; + g.out.rids = &rids; + torture_assert_ntstatus_ok(torture, + dcerpc_samr_GetAliasMembership_r(b, torture, &g), + ""); + torture_assert_ntstatus_ok(torture, g.out.result, + ""); + if (rids.ids == NULL) { + /* This is the piece to test here */ + torture_fail(torture, + "torture_samba3_getaliasmembership_0: " + "Server returns NULL rids array\n"); + } + + return true; +} + +/** + * Test smb reauthentication while rpc pipe is in use. + */ +static bool torture_rpc_smb_reauth1(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct smbcli_options options; + struct smbcli_session_options session_options; + + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_GetUserName r; + struct lsa_String *authority_name_p = NULL; + char *authority_name_saved = NULL; + struct lsa_String *account_name_p = NULL; + char *account_name_saved = NULL; + struct cli_credentials *anon_creds = NULL; + struct smb_composite_sesssetup io; + + mem_ctx = talloc_init("torture_samba3_reauth"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smbcli_full_connection failed"); + + status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc", + &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb failed"); + lsa_handle = lsa_pipe->binding_handle; + + /* lsa getusername */ + + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n", + authority_name_p->string, + account_name_p->string); + + account_name_saved = talloc_strdup(mem_ctx, account_name_p->string); + torture_assert_goto(torture, (account_name_saved != NULL), ret, done, + "talloc failed"); + authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string); + torture_assert_goto(torture, (authority_name_saved != NULL), ret, done, + "talloc failed"); + + /* smb re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + + ZERO_STRUCT(io); + io.in.sesskey = cli->transport->negotiate.sesskey; + io.in.capabilities = cli->transport->negotiate.capabilities; + io.in.credentials = anon_creds; + io.in.workgroup = lpcfg_workgroup(torture->lp_ctx); + io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(cli->session, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername after reauth */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + /* smb re-auth again to the original user */ + + ZERO_STRUCT(io); + io.in.sesskey = cli->transport->negotiate.sesskey; + io.in.capabilities = cli->transport->negotiate.capabilities; + io.in.credentials = samba_cmdline_get_creds(); + io.in.workgroup = lpcfg_workgroup(torture->lp_ctx); + io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(cli->session, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test smb reauthentication while rpc pipe is in use. + * Open a second lsa bind after reauth to anon. + * Do lsa getusername on that second bind. + */ +static bool torture_rpc_smb_reauth2(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct smbcli_options options; + struct smbcli_session_options session_options; + + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_GetUserName r; + struct lsa_String *authority_name_p = NULL; + char *authority_name_saved = NULL; + struct lsa_String *account_name_p = NULL; + char *account_name_saved = NULL; + struct cli_credentials *anon_creds = NULL; + struct smb_composite_sesssetup io; + + mem_ctx = talloc_init("torture_samba3_reauth"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smbcli_full_connection failed"); + + /* smb re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + + ZERO_STRUCT(io); + io.in.sesskey = cli->transport->negotiate.sesskey; + io.in.capabilities = cli->transport->negotiate.capabilities; + io.in.credentials = anon_creds; + io.in.workgroup = lpcfg_workgroup(torture->lp_ctx); + io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(cli->session, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* open the lsa pipe */ + + status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc", + &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb failed"); + lsa_handle = lsa_pipe->binding_handle; + + /* lsa getusername */ + + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n", + authority_name_p->string, + account_name_p->string); + + account_name_saved = talloc_strdup(mem_ctx, account_name_p->string); + torture_assert_goto(torture, (account_name_saved != NULL), ret, done, + "talloc failed"); + authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string); + torture_assert_goto(torture, (authority_name_saved != NULL), ret, done, + "talloc failed"); + + /* smb re-auth again to the original user */ + + ZERO_STRUCT(io); + io.in.sesskey = cli->transport->negotiate.sesskey; + io.in.capabilities = cli->transport->negotiate.capabilities; + io.in.credentials = samba_cmdline_get_creds(); + io.in.workgroup = lpcfg_workgroup(torture->lp_ctx); + io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx); + + status = smb_composite_sesssetup(cli->session, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername after reauth */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test smb2 reauthentication while rpc pipe is in use. + */ +static bool torture_rpc_smb2_reauth1(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_GetUserName r; + struct lsa_String *authority_name_p = NULL; + char *authority_name_saved = NULL; + struct lsa_String *account_name_p = NULL; + char *account_name_saved = NULL; + struct cli_credentials *anon_creds = NULL; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + + mem_ctx = talloc_init("torture_samba3_reauth"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + status = pipe_bind_smb2(torture, mem_ctx, tree, "lsarpc", + &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb2 failed"); + lsa_handle = lsa_pipe->binding_handle; + + /* lsa getusername */ + + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n", + authority_name_p->string, + account_name_p->string); + + account_name_saved = talloc_strdup(mem_ctx, account_name_p->string); + torture_assert_goto(torture, (account_name_saved != NULL), ret, done, + "talloc failed"); + authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string); + torture_assert_goto(torture, (authority_name_saved != NULL), ret, done, + "talloc failed"); + + /* smb re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername after reauth */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + /* smb re-auth again to the original user */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test smb2 reauthentication while rpc pipe is in use. + * Open a second lsa bind after reauth to anon. + * Do lsa getusername on that second bind. + */ +static bool torture_rpc_smb2_reauth2(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + + struct dcerpc_pipe *lsa_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_GetUserName r; + struct lsa_String *authority_name_p = NULL; + char *authority_name_saved = NULL; + struct lsa_String *account_name_p = NULL; + char *account_name_saved = NULL; + struct cli_credentials *anon_creds = NULL; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + + mem_ctx = talloc_init("torture_samba3_reauth"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + /* smb re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* open the lsa pipe */ + + status = pipe_bind_smb2(torture, mem_ctx, tree, "lsarpc", + &ndr_table_lsarpc, &lsa_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb2 failed"); + lsa_handle = lsa_pipe->binding_handle; + + /* lsa getusername */ + + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n", + authority_name_p->string, + account_name_p->string); + + account_name_saved = talloc_strdup(mem_ctx, account_name_p->string); + torture_assert_goto(torture, (account_name_saved != NULL), ret, done, + "talloc failed"); + authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string); + torture_assert_goto(torture, (authority_name_saved != NULL), ret, done, + "talloc failed"); + + /* smb re-auth again to the original user */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "session reauth to anon failed"); + + /* re-do lsa getusername */ + + TALLOC_FREE(authority_name_p); + TALLOC_FREE(account_name_p); + ZERO_STRUCT(r); + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r); + + authority_name_p = *r.out.authority_name; + + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "GetUserName failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done, + "GetUserName failed"); + + torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0), + ret, done, "authority_name not equal after reauth to anon"); + torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0), + ret, done, "account_name not equal after reauth to anon"); + + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool torture_rpc_smb1_pipe_name(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_state *cli; + struct smbcli_options options; + struct smbcli_session_options session_options; + union smb_open io; + union smb_close cl; + uint16_t fnum; + + mem_ctx = talloc_init("torture_samba3_smb1_pipe_name"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + lpcfg_smbcli_session_options(torture->lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, &cli, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(torture->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(torture->lp_ctx), + torture->ev, &options, &session_options, + lpcfg_gensec_settings(torture, torture->lp_ctx)); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smbcli_full_connection failed"); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.access_mask = DESIRED_ACCESS_PIPE; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.ntcreatex.in.security_flags = 0; + + io.ntcreatex.in.fname = "__none__"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for '__none__'"); + + io.ntcreatex.in.fname = "pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for 'pipe\\srvsvc'"); + + io.ntcreatex.in.fname = "\\pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for '\\pipe\\srvsvc'"); + + io.ntcreatex.in.fname = "srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'srvsvc'"); + fnum = io.ntcreatex.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.ntcreatex.in.fname = "\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\srvsvc'"); + fnum = io.ntcreatex.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.ntcreatex.in.fname = "\\\\\\\\\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\\\\\\\\\srvsvc'"); + fnum = io.ntcreatex.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_NTTRANS_CREATE; + io.nttrans.in.access_mask = DESIRED_ACCESS_PIPE; + io.nttrans.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.nttrans.in.open_disposition = NTCREATEX_DISP_OPEN; + io.nttrans.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.nttrans.in.security_flags = 0; + + io.nttrans.in.fname = "__none__"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for '__none__'"); + + io.nttrans.in.fname = "pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for 'pipe\\srvsvc'"); + + io.nttrans.in.fname = "\\pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for '\\pipe\\srvsvc'"); + + io.nttrans.in.fname = "srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'srvsvc'"); + fnum = io.nttrans.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.nttrans.in.fname = "\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\srvsvc'"); + fnum = io.nttrans.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.nttrans.in.fname = "\\\\\\\\\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\\\\\\\\\srvsvc'"); + fnum = io.nttrans.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + ZERO_STRUCT(io); + io.generic.level = RAW_OPEN_OPENX; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + + io.openx.in.fname = "__none__"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, + ret, done, + "smb_raw_open for '__none__'"); + + io.openx.in.fname = "srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, + ret, done, + "smb_raw_open for 'srvsvc'"); + + io.openx.in.fname = "\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, + ret, done, + "smb_raw_open for '\\srvsvc'"); + + io.openx.in.fname = "\\pipesrvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, + ret, done, + "smb_raw_open for '\\pipesrvsvc'"); + + io.openx.in.fname = "pipe\\__none__"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for 'pipe\\__none__'"); + + io.openx.in.fname = "\\pipe\\__none__"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb_raw_open for '\\pipe\\__none__'"); + + io.openx.in.fname = "pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'pipe\\srvsvc'"); + fnum = io.openx.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.openx.in.fname = "\\pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\pipe\\srvsvc'"); + fnum = io.openx.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.openx.in.fname = "\\\\\\\\\\pipe\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\\\\\\\\\pipe\\srvsvc'"); + fnum = io.openx.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + + io.openx.in.fname = "\\\\\\\\\\pipe\\\\\\\\\\srvsvc"; + status = smb_raw_open(cli->tree, torture, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for '\\\\\\\\\\pipe\\\\\\\\\\srvsvc'"); + fnum = io.openx.out.file.fnum; + ZERO_STRUCT(cl); + cl.generic.level = RAW_CLOSE_CLOSE; + cl.close.in.file.fnum = fnum; + status = smb_raw_close(cli->tree, &cl); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb_raw_close failed"); + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool torture_rpc_smb2_pipe_name(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + struct smb2_handle h; + struct smb2_create io; + + mem_ctx = talloc_init("torture_samba3_smb2_pipe_name"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = DESIRED_ACCESS_PIPE; + io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + + io.in.fname = "__none__"; + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_create for '__none__'"); + + io.in.fname = "\\srvsvc"; + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_create for '\\srvsvc'"); + + io.in.fname = "\\pipe\\srvsvc"; + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_create for '\\pipe\\srvsvc'"); + + io.in.fname = "pipe\\srvsvc"; + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_create for 'pipe\\srvsvc'"); + + io.in.fname = "srvsvc"; + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'srvsvc'"); + + h = io.out.file.handle; + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_util_close failed"); + + ret = true; +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test behaviour of a waiting read call on a pipe when + * the pipe handle is closed: + * - open a pipe via smb2 + * - trigger a read which hangs since there is nothing to read + * - close the pipe file handle + * - wait for the read to return and check the status + * (STATUS_PIPE_BROKEN) + */ +static bool torture_rpc_smb2_pipe_read_close(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + struct smb2_handle h; + struct smb2_request *smb2req; + struct smb2_create io; + struct smb2_read rd; + + mem_ctx = talloc_init("torture_samba3_pipe_read_close"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = DESIRED_ACCESS_PIPE; + io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = "lsarpc"; + + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'lsarpc'"); + + h = io.out.file.handle; + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 1024; + rd.in.offset = 0; + rd.in.min_count = 0; + + smb2req = smb2_read_send(tree, &rd); + torture_assert_goto(torture, (smb2req != NULL), ret, done, + "smb2_read_send failed"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_util_close failed"); + + status = smb2_read_recv(smb2req, mem_ctx, &rd); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done, + "smb2_read_recv: unexpected return code"); + + ret = true; +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test behaviour of a waiting read call on a pipe when + * the tree is disconnected. + * - open a pipe via smb2 + * - trigger a read which hangs since there is nothing to read + * - do a tree disconnect + * - wait for the read to return and check the status + * (STATUS_PIPE_BROKEN) + */ +static bool torture_rpc_smb2_pipe_read_tdis(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + struct smb2_handle h; + struct smb2_request *smb2req; + struct smb2_create io; + struct smb2_read rd; + + mem_ctx = talloc_init("torture_samba3_pipe_read_tdis"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = DESIRED_ACCESS_PIPE; + io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = "lsarpc"; + + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'lsarpc'"); + + h = io.out.file.handle; + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 1024; + rd.in.offset = 0; + rd.in.min_count = 0; + + smb2req = smb2_read_send(tree, &rd); + torture_assert_goto(torture, (smb2req != NULL), ret, done, + "smb2_read_send failed"); + + status = smb2_tdis(tree); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_tdis failed"); + + status = smb2_read_recv(smb2req, mem_ctx, &rd); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done, + "smb2_read_recv: unexpected return code"); + + ret = true; +done: + talloc_free(mem_ctx); + return ret; +} + +/** + * Test behaviour of a waiting read call on a pipe when + * the user logs off + * - open a pipe via smb2 + * - trigger a read which hangs since there is nothing to read + * - do a logoff + * - wait for the read to return and check the status + * (STATUS_PIPE_BROKEN) + */ +static bool torture_rpc_smb2_pipe_read_logoff(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(torture, "host", NULL); + struct smb2_tree *tree; + struct smb2_handle h; + struct smb2_request *smb2req; + struct smb2_create io; + struct smb2_read rd; + + mem_ctx = talloc_init("torture_samba3_pipe_read_tdis"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = DESIRED_ACCESS_PIPE; + io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = "lsarpc"; + + status = smb2_create(tree, tree, &io); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed for 'lsarpc'"); + + h = io.out.file.handle; + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 1024; + rd.in.offset = 0; + rd.in.min_count = 0; + + smb2req = smb2_read_send(tree, &rd); + torture_assert_goto(torture, (smb2req != NULL), ret, done, + "smb2_read_send failed"); + + status = smb2_logoff(tree->session); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_logoff failed"); + + status = smb2_read_recv(smb2req, mem_ctx, &rd); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done, + "smb2_read_recv: unexpected return code"); + + ret = true; +done: + talloc_free(mem_ctx); + return ret; +} + +static bool torture_rpc_lsa_over_netlogon(struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + struct smb2_tree *tree; + struct dcerpc_pipe *netlogon_pipe; + struct dcerpc_binding_handle *lsa_handle; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 o; + struct policy_handle handle; + + torture_comment(torture, "Testing if we can access LSA server over " + "\\\\pipe\\netlogon rather than \\\\pipe\\lsarpc\n"); + + mem_ctx = talloc_init("torture_samba3_rpc_lsa_over_netlogon"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + status = pipe_bind_smb2(torture, mem_ctx, tree, "netlogon", + &ndr_table_lsarpc, &netlogon_pipe); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "pipe_bind_smb2 failed"); + lsa_handle = netlogon_pipe->binding_handle; + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + o.in.system_name = "\\"; + o.in.attr = &attr; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.handle = &handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_lsa_OpenPolicy2_r(lsa_handle, torture, &o), + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok(torture, + o.out.result, + "OpenPolicy2 failed"); + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +static bool torture_rpc_pipes_supported_interfaces( + struct torture_context *torture) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + struct smb2_tree *tree; + struct dcerpc_pipe *pipe1; + struct dcerpc_pipe *pipe2; + struct dcerpc_pipe *pipe3; + + torture_comment(torture, "Testing only appropriate interfaces are " + "available in smb pipes\n"); + + mem_ctx = talloc_init("torture_samba3_rpc_pipes_supported_interfaces"); + torture_assert(torture, (mem_ctx != NULL), "talloc_init failed"); + + lpcfg_smbcli_options(torture->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + torture_setting_string(torture, "host", NULL), + lpcfg_smb_ports(torture->lp_ctx), + "IPC$", + lpcfg_resolve_context(torture->lp_ctx), + samba_cmdline_get_creds(), + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_connect failed"); + + /* Test embedded services pipes. The svcctl interface is + * not available if we open the winreg pipe. */ + status = pipe_bind_smb2(torture, mem_ctx, tree, "winreg", + &ndr_table_svcctl, &pipe1); + torture_assert_ntstatus_equal(torture, + status, + NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX, + "svcctl interface not supported in winreg pipe"); + + /* Test it is not possible to bind to S4 server provided services */ + status = pipe_bind_smb2(torture, mem_ctx, tree, "srvsvc", + &ndr_table_samr, &pipe2); + torture_assert_ntstatus_equal(torture, + status, + NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX, + "samr interface not supported in srvsvc pipe"); + + /* Test pipes in forked daemons like lsassd. The lsarpc interface is + * not available if we open the SAMR pipe. */ + status = pipe_bind_smb2(torture, mem_ctx, tree, "samr", + &ndr_table_lsarpc, &pipe3); + torture_assert_ntstatus_equal(torture, + status, + NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX, + "lsarpc interface not supported in samr pipe"); + + ret = true; + done: + talloc_free(mem_ctx); + return ret; +} + +struct torture_suite *torture_rpc_samba3(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samba3"); + + torture_suite_add_simple_test(suite, "bind", torture_bind_samba3); + torture_suite_add_simple_test(suite, "netlogon", torture_netlogon_samba3); + torture_suite_add_simple_test(suite, "sessionkey", torture_samba3_sessionkey); + torture_suite_add_simple_test(suite, "srvsvc", torture_samba3_rpc_srvsvc); + torture_suite_add_simple_test(suite, "sharesec", torture_samba3_rpc_sharesec); + torture_suite_add_simple_test(suite, "getusername", torture_samba3_rpc_getusername); + torture_suite_add_simple_test(suite, "randomauth2", torture_samba3_rpc_randomauth2); + torture_suite_add_simple_test(suite, "lsa", torture_samba3_rpc_lsa); + torture_suite_add_simple_test(suite, "spoolss", torture_samba3_rpc_spoolss); + torture_suite_add_simple_test(suite, "wkssvc", torture_samba3_rpc_wkssvc); + torture_suite_add_simple_test(suite, "winreg", torture_samba3_rpc_winreg); + torture_suite_add_simple_test(suite, "getaliasmembership-0", torture_samba3_getaliasmembership_0); + torture_suite_add_simple_test(suite, "regconfig", torture_samba3_regconfig); + torture_suite_add_simple_test(suite, "smb-reauth1", torture_rpc_smb_reauth1); + torture_suite_add_simple_test(suite, "smb-reauth2", torture_rpc_smb_reauth2); + torture_suite_add_simple_test(suite, "smb2-reauth1", torture_rpc_smb2_reauth1); + torture_suite_add_simple_test(suite, "smb2-reauth2", torture_rpc_smb2_reauth2); + torture_suite_add_simple_test(suite, "smb1-pipe-name", torture_rpc_smb1_pipe_name); + torture_suite_add_simple_test(suite, "smb2-pipe-name", torture_rpc_smb2_pipe_name); + torture_suite_add_simple_test(suite, "smb2-pipe-read-close", torture_rpc_smb2_pipe_read_close); + torture_suite_add_simple_test(suite, "smb2-pipe-read-tdis", torture_rpc_smb2_pipe_read_tdis); + torture_suite_add_simple_test(suite, "smb2-pipe-read-logoff", torture_rpc_smb2_pipe_read_logoff); + torture_suite_add_simple_test(suite, + "lsa_over_netlogon", + torture_rpc_lsa_over_netlogon); + torture_suite_add_simple_test(suite, + "pipes_supported_interfaces", + torture_rpc_pipes_supported_interfaces); + + suite->description = talloc_strdup(suite, "samba3 DCERPC interface tests"); + + return suite; +} diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c new file mode 100644 index 0000000..f16db64 --- /dev/null +++ b/source4/torture/rpc/samlogon.c @@ -0,0 +1,2121 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon SamLogon operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2003-2004 + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "lib/cmdline/cmdline.h" +#include "torture/rpc/torture_rpc.h" +#include "auth/gensec/gensec.h" +#include "libcli/auth/libcli_auth.h" +#include "param/param.h" + +#include +#include + +#define TEST_MACHINE_NAME "samlogontest" +#define TEST_USER_NAME "samlogontestuser" +#define TEST_USER_NAME_WRONG_WKS "samlogontest2" +#define TEST_USER_NAME_WRONG_TIME "samlogontest3" + +enum ntlm_break { + BREAK_BOTH, + BREAK_NONE, + BREAK_LM, + BREAK_NT, + NO_LM, + NO_NT +}; + +struct samlogon_state { + TALLOC_CTX *mem_ctx; + struct torture_context *tctx; + const char *comment; + const char *account_name; + const char *account_domain; + const char *netbios_name; + const char *password; + const char *workgroup; + struct dcerpc_pipe *p; + int function_level; + uint32_t parameter_control; + struct netr_LogonSamLogon r; + struct netr_LogonSamLogonEx r_ex; + struct netr_LogonSamLogonWithFlags r_flags; + struct netr_Authenticator auth, auth2; + struct netlogon_creds_CredentialState *creds; + NTSTATUS expected_error; + bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */ + DATA_BLOB chall; +}; + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ +static NTSTATUS check_samlogon(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + uint32_t parameter_control, + DATA_BLOB *chall, + DATA_BLOB *lm_response, + DATA_BLOB *nt_response, + uint8_t lm_key[8], + uint8_t user_session_key[16], + char **error_string) +{ + NTSTATUS status; + struct netr_LogonSamLogon *r = &samlogon_state->r; + struct netr_LogonSamLogonEx *r_ex = &samlogon_state->r_ex; + struct netr_LogonSamLogonWithFlags *r_flags = &samlogon_state->r_flags; + struct netr_NetworkInfo ninfo; + struct netr_SamBaseInfo *base = NULL; + uint16_t validation_level = 0; + + samlogon_state->r.in.logon->network = &ninfo; + samlogon_state->r_ex.in.logon->network = &ninfo; + samlogon_state->r_flags.in.logon->network = &ninfo; + + ninfo.identity_info.domain_name.string = samlogon_state->account_domain; + ninfo.identity_info.parameter_control = parameter_control; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.account_name.string = samlogon_state->account_name; + ninfo.identity_info.workstation.string = TEST_MACHINE_NAME; + + memcpy(ninfo.challenge, chall->data, 8); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + if (lm_response && lm_response->data) { + lm_response->data[0]++; + } + break; + case BREAK_NT: + if (nt_response && nt_response->data) { + nt_response->data[0]++; + } + break; + case BREAK_BOTH: + if (lm_response && lm_response->data) { + lm_response->data[0]++; + } + if (nt_response && nt_response->data) { + nt_response->data[0]++; + } + break; + case NO_LM: + data_blob_free(lm_response); + break; + case NO_NT: + data_blob_free(nt_response); + break; + } + + if (nt_response) { + ninfo.nt.data = nt_response->data; + ninfo.nt.length = nt_response->length; + } else { + ninfo.nt.data = NULL; + ninfo.nt.length = 0; + } + + if (lm_response) { + ninfo.lm.data = lm_response->data; + ninfo.lm.length = lm_response->length; + } else { + ninfo.lm.data = NULL; + ninfo.lm.length = 0; + } + + switch (samlogon_state->function_level) { + case NDR_NETR_LOGONSAMLOGON: + ZERO_STRUCT(samlogon_state->auth2); + netlogon_creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth); + + r->out.return_authenticator = NULL; + status = dcerpc_netr_LogonSamLogon_r(samlogon_state->p->binding_handle, + samlogon_state->mem_ctx, r); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + if (!r->out.return_authenticator || + !netlogon_creds_client_check(samlogon_state->creds, &r->out.return_authenticator->cred)) { + torture_comment(samlogon_state->tctx, "Credential chaining failed\n"); + } + if (!NT_STATUS_IS_OK(r->out.result)) { + if (error_string) { + *error_string = strdup(nt_errstr(r->out.result)); + } + return r->out.result; + } + + validation_level = r->in.validation_level; + + status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds, + validation_level, + r->out.validation); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + switch (validation_level) { + case 2: + base = &r->out.validation->sam2->base; + break; + case 3: + base = &r->out.validation->sam3->base; + break; + case 6: + base = &r->out.validation->sam6->base; + break; + } + break; + case NDR_NETR_LOGONSAMLOGONEX: + status = dcerpc_netr_LogonSamLogonEx_r(samlogon_state->p->binding_handle, + samlogon_state->mem_ctx, r_ex); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + if (!NT_STATUS_IS_OK(r_ex->out.result)) { + if (error_string) { + *error_string = strdup(nt_errstr(r_ex->out.result)); + } + return r_ex->out.result; + } + + validation_level = r_ex->in.validation_level; + + status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds, + validation_level, + r_ex->out.validation); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + switch (validation_level) { + case 2: + base = &r_ex->out.validation->sam2->base; + break; + case 3: + base = &r_ex->out.validation->sam3->base; + break; + case 6: + base = &r_ex->out.validation->sam6->base; + break; + } + break; + case NDR_NETR_LOGONSAMLOGONWITHFLAGS: + ZERO_STRUCT(samlogon_state->auth2); + netlogon_creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth); + + r_flags->out.return_authenticator = NULL; + status = dcerpc_netr_LogonSamLogonWithFlags_r(samlogon_state->p->binding_handle, + samlogon_state->mem_ctx, r_flags); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + if (!r_flags->out.return_authenticator || + !netlogon_creds_client_check(samlogon_state->creds, &r_flags->out.return_authenticator->cred)) { + torture_comment(samlogon_state->tctx, "Credential chaining failed\n"); + } + if (!NT_STATUS_IS_OK(r_flags->out.result)) { + if (error_string) { + *error_string = strdup(nt_errstr(r_flags->out.result)); + } + return r_flags->out.result; + } + + validation_level = r_flags->in.validation_level; + + status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds, + validation_level, + r_flags->out.validation); + if (!NT_STATUS_IS_OK(status)) { + if (error_string) { + *error_string = strdup(nt_errstr(status)); + } + return status; + } + + switch (validation_level) { + case 2: + base = &r_flags->out.validation->sam2->base; + break; + case 3: + base = &r_flags->out.validation->sam3->base; + break; + case 6: + base = &r_flags->out.validation->sam6->base; + break; + } + break; + default: + /* can't happen */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (!base) { + torture_comment(samlogon_state->tctx, "No user info returned from 'successful' SamLogon*() call!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (user_session_key) { + memcpy(user_session_key, base->key.key, 16); + } + if (lm_key) { + memcpy(lm_key, base->LMSessKey.key, 8); + } + + return status; +} + + +/* + * Test the normal 'LM and NTLM' combination + */ + +static bool test_lm_ntlm_broken(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string) +{ + bool pass = true; + bool lm_good; + NTSTATUS nt_status; + DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t user_session_key[16]; + uint8_t lm_hash[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + lm_good = SMBencrypt(samlogon_state->password, samlogon_state->chall.data, lm_response.data); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } else { + E_deshash(samlogon_state->password, lm_hash); + } + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data); + + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + data_blob_free(&lm_response); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'long' passwords, the LM password is invalid */ + if (break_which == NO_NT && !lm_good) { + return true; + } + /* for modern servers, the LM password is invalid */ + if (break_which == NO_NT + && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) { + return true; + } + + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (break_which == NO_NT && !lm_good) { + *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!"); + return false; + } + + /* for modern servers, the LM password is invalid */ + if (break_which == NO_NT + && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) { + *error_string = strdup("LM password is OK but should have failed against a modern server"); + return false; + } + + if (!all_zero(lm_key, sizeof(lm_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + torture_comment(samlogon_state->tctx, "expected (all zeros):\n"); + pass = false; + } + + switch (break_which) { + case NO_NT: + { + uint8_t lm_key_expected[16]; + memcpy(lm_key_expected, session_key.data, 8); + memset(lm_key_expected+8, '\0', 8); + if (memcmp(lm_key_expected, user_session_key, + 16) != 0) { + *error_string = strdup("NT Session Key does not match expectations (should be first-8 session key)!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lm_key_expected, sizeof(lm_key_expected)); + pass = false; + } + break; + } + default: + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + *error_string = strdup("NT Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, session_key.data, session_key.length); + pass = false; + } + } + return pass; +} + +/* + * Test LM authentication, no NT response supplied + */ + +static bool test_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + + return test_lm_ntlm_broken(samlogon_state, NO_NT, error_string); +} + +/* + * Test the NTLM response only, no LM. + */ + +static bool test_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, NO_LM, error_string); +} + +/* + * Test the NTLM response only, but in the LM field. + */ + +static bool test_ntlm_in_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + bool lm_good; + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t lm_hash[16]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + nt_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + &nt_response, + NULL, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (torture_setting_bool(samlogon_state->tctx, "samba4", false)) { + if (!all_zero(lm_key, sizeof(lm_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + torture_comment(samlogon_state->tctx, "expected (all zeros):\n"); + pass = false; + } + + + if (!all_zero(user_session_key, sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "NT Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + torture_comment(samlogon_state->tctx, "expected (all zeros):\n"); + pass = false; + } + } else { + if (lm_good) { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lm_hash, 8); + pass = false; + } +#if 0 + } else { + if (memcmp(session_key.data, lm_key, + sizeof(lm_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM Key does not match expectations (first 8 session key)!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, session_key.data, 8); + pass = false; + } +#endif + } + if (lm_good && memcmp(lm_hash, user_session_key, 8) != 0) { + uint8_t lm_key_expected[16]; + memcpy(lm_key_expected, lm_hash, 8); + memset(lm_key_expected+8, '\0', 8); + if (memcmp(lm_key_expected, user_session_key, + 16) != 0) { + torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations (should be first-8 LM hash)!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lm_key_expected, sizeof(lm_key_expected)); + pass = false; + } + } + } + return pass; +} + +/* + * Test the NTLM response only, but in the both the NT and LM fields. + */ + +static bool test_ntlm_in_both(struct samlogon_state *samlogon_state, char **error_string) +{ + bool pass = true; + bool lm_good; + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + uint8_t lm_key[8]; + uint8_t lm_hash[16]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + nt_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + NULL, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (!all_zero(lm_key, + sizeof(lm_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + torture_comment(samlogon_state->tctx, "expected (all zero)\n"); + pass = false; + } + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, session_key.data, session_key.length); + pass = false; + } + + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +enum ntlmv2_domain { + UPPER_DOMAIN, + NO_DOMAIN +}; + +static bool test_lmv2_ntlmv2_broken(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + enum ntlmv2_domain ntlmv2_domain, + char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_session_key = data_blob(NULL, 0); + DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, TEST_MACHINE_NAME, samlogon_state->workgroup); + + uint8_t lm_session_key[8]; + uint8_t user_session_key[16]; + + ZERO_STRUCT(lm_session_key); + ZERO_STRUCT(user_session_key); + + switch (ntlmv2_domain) { + case UPPER_DOMAIN: + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, samlogon_state->account_domain, + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + case NO_DOMAIN: + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, "", + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + } + data_blob_free(&names_blob); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lmv2_response, + &ntlmv2_response, + lm_session_key, + user_session_key, + error_string); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return break_which == BREAK_BOTH; + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + + switch (break_which) { + case NO_NT: + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "USER (LMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM (LMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lmv2_session_key.data, 8); + pass = false; + } + break; + default: + if (memcmp(ntlmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) == 0) { + torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key expected, got LMv2 session key instead:\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + + } else { + torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + } + if (memcmp(ntlmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) == 0) { + torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key expected, got LMv2 session key instead:\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, lm_session_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlmv2_session_key.data, 8); + pass = false; + } else { + torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlmv2_session_key.data, 8); + pass = false; + } + } + } + + return pass; +} + +/* + * Test the NTLM and LMv2 responses + */ + +static bool test_lmv2_ntlm_broken(struct samlogon_state *samlogon_state, + enum ntlm_break break_which, + enum ntlmv2_domain ntlmv2_domain, + char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_session_key = data_blob(NULL, 0); + DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, samlogon_state->netbios_name, samlogon_state->workgroup); + + DATA_BLOB ntlm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB ntlm_session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16); + + bool lm_good; + uint8_t lm_hash[16]; + uint8_t lm_session_key[8]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, + ntlm_response.data); + E_md4hash(samlogon_state->password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, + ntlm_session_key.data); + + lm_good = E_deshash(samlogon_state->password, lm_hash); + if (!lm_good) { + ZERO_STRUCT(lm_hash); + } + + ZERO_STRUCT(lm_session_key); + ZERO_STRUCT(user_session_key); + + switch (ntlmv2_domain) { + case UPPER_DOMAIN: + /* TODO - test with various domain cases, and without domain */ + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, samlogon_state->account_domain, + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + case NO_DOMAIN: + /* TODO - test with various domain cases, and without domain */ + if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx, + samlogon_state->account_name, "", + samlogon_state->password, &samlogon_state->chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &lmv2_session_key, &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return false; + } + break; + } + + data_blob_free(&names_blob); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lmv2_response, + &ntlm_response, + lm_session_key, + user_session_key, + error_string); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + switch (break_which) { + case NO_NT: + if (memcmp(lmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "USER (LMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length); + pass = false; + } + if (memcmp(lmv2_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM (LMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, lmv2_session_key.data, 8); + pass = false; + } + break; + case BREAK_LM: + if (memcmp(ntlm_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlm_session_key.data, ntlm_session_key.length); + pass = false; + } + if (!all_zero(lm_session_key, + sizeof(lm_session_key))) { + torture_comment(samlogon_state->tctx, "LM Session Key does not match expectations (zeros)!\n"); + torture_comment(samlogon_state->tctx, "lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + pass = false; + } + break; + default: + if (memcmp(ntlm_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, 16); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlm_session_key.data, ntlm_session_key.length); + pass = false; + } + if (memcmp(ntlm_session_key.data, lm_session_key, + sizeof(lm_session_key)) != 0) { + torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key does not match expectations!\n"); + torture_comment(samlogon_state->tctx, "lm_session_key:\n"); + dump_data(1, lm_session_key, 8); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, ntlm_session_key.data, 8); + pass = false; + } + } + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static bool test_lmv2_ntlmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, UPPER_DOMAIN, error_string); +} + +#if 0 +static bool test_lmv2_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, NO_DOMAIN, error_string); +} +#endif + +/* + * Test the LMv2 response only + */ + +static bool test_lmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, NO_DOMAIN, error_string); +} + +/* + * Test the NTLMv2 response only + */ + +static bool test_ntlmv2(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, NO_DOMAIN, error_string); +} + +static bool test_lm_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_NONE, error_string); +} + +static bool test_ntlm_lm_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_LM, error_string); +} + +static bool test_ntlm_ntlm_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_NT, error_string); +} + +static bool test_lm_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lm_ntlm_broken(samlogon_state, BREAK_BOTH, error_string); +} +static bool test_ntlmv2_lmv2_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_lmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string); +} + +static bool test_ntlmv2_ntlmv2_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string); +} + +#if 0 +static bool test_ntlmv2_ntlmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string); +} +#endif + +static bool test_ntlmv2_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string); +} + +static bool test_ntlmv2_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_ntlm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_ntlm_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_lm(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string); +} + +static bool test_lmv2_ntlm_break_lm_no_dom(struct samlogon_state *samlogon_state, char **error_string) +{ + return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string); +} + +/* + * Test the NTLM2 response (extra challenge in LM field) + * + * This test is the same as the 'break LM' test, but checks that the + * server implements NTLM2 session security in the right place + * (NETLOGON is the wrong place). + */ + +static bool test_ntlm2(struct samlogon_state *samlogon_state, char **error_string) +{ + bool pass = true; + NTSTATUS nt_status; + DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24); + + uint8_t lm_key[8]; + uint8_t nt_hash[16]; + uint8_t lm_hash[16]; + uint8_t nt_key[16]; + uint8_t user_session_key[16]; + uint8_t expected_user_session_key[16]; + uint8_t session_nonce_hash[16]; + uint8_t client_chall[8]; + + gnutls_hmac_hd_t hmac_hnd; + gnutls_hash_hd_t hash_hnd; + + ZERO_STRUCT(user_session_key); + ZERO_STRUCT(lm_key); + generate_random_buffer(client_chall, 8); + + gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5); + gnutls_hash(hash_hnd, samlogon_state->chall.data, 8); + gnutls_hash(hash_hnd, client_chall, 8); + gnutls_hash_deinit(hash_hnd, session_nonce_hash); + + E_md4hash(samlogon_state->password, (uint8_t *)nt_hash); + E_deshash(samlogon_state->password, (uint8_t *)lm_hash); + SMBsesskeygen_ntv1((const uint8_t *)nt_hash, + nt_key); + + SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data); + + memcpy(lm_response.data, session_nonce_hash, 8); + memset(lm_response.data + 8, 0, 16); + + gnutls_hmac_init(&hmac_hnd, + GNUTLS_MAC_MD5, + nt_key, + 16); + gnutls_hmac(hmac_hnd, samlogon_state->chall.data, 8); + gnutls_hmac(hmac_hnd, client_chall, 8); + gnutls_hmac_deinit(hmac_hnd, expected_user_session_key); + + nt_status = check_samlogon(samlogon_state, + BREAK_NONE, + samlogon_state->parameter_control, + &samlogon_state->chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + return false; + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (!all_zero(lm_key, sizeof(lm_key))) { + torture_comment(samlogon_state->tctx, "LM Session Key does not match expectations (zeros)!\n"); + torture_comment(samlogon_state->tctx, "lm_key:\n"); + dump_data(1, lm_key, 8); + pass = false; + } + if (memcmp(nt_key, user_session_key, 16) != 0) { + torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations (should be NT Key)!\n"); + torture_comment(samlogon_state->tctx, "user_session_key:\n"); + dump_data(1, user_session_key, sizeof(user_session_key)); + torture_comment(samlogon_state->tctx, "expected:\n"); + dump_data(1, nt_key, sizeof(nt_key)); + pass = false; + } + return pass; +} + +static bool test_plaintext(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string) +{ + NTSTATUS nt_status; + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB lm_response = data_blob(NULL, 0); + char *password; + char *dospw; + smb_ucs2_t *unicodepw; + size_t converted_size = 0; + uint8_t user_session_key[16]; + uint8_t lm_key[16]; + uint8_t lm_hash[16]; + DATA_BLOB chall = data_blob_talloc_zero(samlogon_state->mem_ctx, 8); + bool lm_good = E_deshash(samlogon_state->password, lm_hash); + + ZERO_STRUCT(user_session_key); + + if (!push_ucs2_talloc(samlogon_state->mem_ctx, + &unicodepw, samlogon_state->password, &converted_size)) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + exit(1); + } + + nt_response = data_blob_talloc(samlogon_state->mem_ctx, unicodepw, strlen_m(samlogon_state->password)*2); + + password = strupper_talloc(samlogon_state->mem_ctx, samlogon_state->password); + + if (!convert_string_talloc(samlogon_state->mem_ctx, + CH_UNIX, CH_DOS, + password, strlen(password)+1, + (void**)&dospw, &converted_size)) { + DEBUG(0, ("convert_string_talloc failed!\n")); + exit(1); + } + + lm_response = data_blob_talloc(samlogon_state->mem_ctx, dospw, strlen(dospw)); + + nt_status = check_samlogon(samlogon_state, + break_which, + samlogon_state->parameter_control | MSV1_0_CLEARTEXT_PASSWORD_ALLOWED, + &chall, + &lm_response, + &nt_response, + lm_key, + user_session_key, + error_string); + + if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) { + /* for 'old' passwords, we allow the server to be OK or wrong password */ + if (samlogon_state->old_password) { + return true; + } + /* for 'long' passwords, the LM password is invalid */ + if (break_which == NO_NT && !lm_good) { + return true; + } + /* for modern servers, the LM password is invalid */ + if (break_which == NO_NT + && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) { + return true; + } + + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH)); + } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) { + return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT)); + } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) { + int ret; + + SAFE_FREE(*error_string); + ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status)); + if (ret == -1) { + *error_string = NULL; + } + return false; + } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) { + return true; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return false; + } + + if (break_which == NO_NT && !lm_good) { + *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!"); + return false; + } + + /* for modern servers, the LM password is invalid */ + if (break_which == NO_NT + && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) { + *error_string = strdup("LM password is OK but should have failed against a modern server"); + return false; + } + + return true; +} + +static bool test_plaintext_none_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_NONE, error_string); +} + +static bool test_plaintext_lm_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_LM, error_string); +} + +static bool test_plaintext_nt_broken(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, BREAK_NT, error_string); +} + +static bool test_plaintext_nt_only(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, NO_LM, error_string); +} + +static bool test_plaintext_lm_only(struct samlogon_state *samlogon_state, + char **error_string) { + return test_plaintext(samlogon_state, NO_NT, error_string); +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - NT in LM field + - NT in both fields + - NTLMv2 + - NTLMv2 and LMv2 + - LMv2 + - plaintext tests (in challenge-response fields) + + check we get the correct session key in each case + check what values we get for the LM session key + +*/ + +static const struct ntlm_tests { + bool (*fn)(struct samlogon_state *, char **); + const char *name; + bool expect_fail; +} test_table[] = { + {test_lmv2_ntlmv2, "NTLMv2 and LMv2", false}, +#if 0 + {test_lmv2_ntlmv2_no_dom, "NTLMv2 and LMv2 (no domain)", false}, +#endif + {test_lm, "LM", false}, + {test_lm_ntlm, "LM and NTLM", false}, + {test_lm_ntlm_both_broken, "LM and NTLM, both broken", false}, + {test_ntlm, "NTLM", false}, + {test_ntlm_in_lm, "NTLM in LM", false}, + {test_ntlm_in_both, "NTLM in both", false}, + {test_ntlmv2, "NTLMv2", false}, + {test_ntlmv2_no_dom, "NTLMv2 (no domain)", false}, + {test_lmv2, "LMv2", false}, + {test_lmv2_no_dom, "LMv2 (no domain)", false}, + {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken", false}, + {test_ntlmv2_lmv2_broken_no_dom, "NTLMv2 and LMv2, LMv2 broken (no domain)", false}, + {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken", false}, +#if 0 + {test_ntlmv2_ntlmv2_broken_no_dom, "NTLMv2 and LMv2, NTLMv2 broken (no domain)", false}, +#endif + {test_ntlmv2_both_broken, "NTLMv2 and LMv2, both broken", false}, + {test_ntlmv2_both_broken_no_dom, "NTLMv2 and LMv2, both broken (no domain)", false}, + {test_ntlm_lm_broken, "NTLM and LM, LM broken", false}, + {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken", false}, + {test_ntlm2, "NTLM2 (NTLMv2 session security)", false}, + {test_lmv2_ntlm_both_broken, "LMv2 and NTLM, both broken", false}, + {test_lmv2_ntlm_both_broken_no_dom, "LMv2 and NTLM, both broken (no domain)", false}, + {test_lmv2_ntlm_break_ntlm, "LMv2 and NTLM, NTLM broken", false}, + {test_lmv2_ntlm_break_ntlm_no_dom, "LMv2 and NTLM, NTLM broken (no domain)", false}, + {test_lmv2_ntlm_break_lm, "LMv2 and NTLM, LMv2 broken", false}, + {test_lmv2_ntlm_break_lm_no_dom, "LMv2 and NTLM, LMv2 broken (no domain)", false}, + {test_plaintext_none_broken, "Plaintext", false}, + {test_plaintext_lm_broken, "Plaintext LM broken", false}, + {test_plaintext_nt_broken, "Plaintext NT broken", false}, + {test_plaintext_nt_only, "Plaintext NT only", false}, + {test_plaintext_lm_only, "Plaintext LM only", false}, + { .name = NULL, } +}; + +/* + try a netlogon SamLogon +*/ +static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + struct netlogon_creds_CredentialState *creds, + const char *comment, + const char *account_domain, const char *account_name, + const char *plain_pass, uint32_t parameter_control, + NTSTATUS expected_error, bool old_password, + int n_subtests) +{ + TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_SamLogon function-level context"); + int i, v, l, f; + bool ret = true; + int validation_levels[] = {2,3,6}; + int logon_levels[] = { NetlogonNetworkInformation, NetlogonNetworkTransitiveInformation }; + int function_levels[] = { + NDR_NETR_LOGONSAMLOGON, + NDR_NETR_LOGONSAMLOGONEX, + NDR_NETR_LOGONSAMLOGONWITHFLAGS }; + struct samlogon_state samlogon_state; + + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative = 1; + uint32_t flags = 0; + + ZERO_STRUCT(logon); + + torture_comment(tctx, "Testing netr_LogonSamLogon and netr_LogonSamLogonWithFlags\n"); + + samlogon_state.comment = comment; + samlogon_state.account_name = account_name; + samlogon_state.account_domain = account_domain; + samlogon_state.password = plain_pass; + samlogon_state.workgroup = lpcfg_workgroup(tctx->lp_ctx); + samlogon_state.netbios_name = lpcfg_netbios_name(tctx->lp_ctx); + samlogon_state.p = p; + samlogon_state.creds = creds; + samlogon_state.expected_error = expected_error; + samlogon_state.chall = data_blob_talloc(fn_ctx, NULL, 8); + samlogon_state.parameter_control = parameter_control; + samlogon_state.old_password = old_password; + samlogon_state.tctx = tctx; + + generate_random_buffer(samlogon_state.chall.data, 8); + samlogon_state.r_flags.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r_flags.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r_flags.in.credential = &samlogon_state.auth; + samlogon_state.r_flags.in.return_authenticator = &samlogon_state.auth2; + samlogon_state.r_flags.in.flags = &flags; + samlogon_state.r_flags.in.logon = &logon; + samlogon_state.r_flags.out.validation = &validation; + samlogon_state.r_flags.out.authoritative = &authoritative; + samlogon_state.r_flags.out.flags = &flags; + + samlogon_state.r_ex.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r_ex.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r_ex.in.flags = &flags; + samlogon_state.r_ex.in.logon = &logon; + samlogon_state.r_ex.out.validation = &validation; + samlogon_state.r_ex.out.authoritative = &authoritative; + samlogon_state.r_ex.out.flags = &flags; + + samlogon_state.r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + samlogon_state.r.in.computer_name = TEST_MACHINE_NAME; + samlogon_state.r.in.credential = &samlogon_state.auth; + samlogon_state.r.in.return_authenticator = &samlogon_state.auth2; + samlogon_state.r.in.logon = &logon; + samlogon_state.r.out.validation = &validation; + samlogon_state.r.out.authoritative = &authoritative; + + + for (f=0;f n_subtests)) { + continue; + } + for (v=0;vbinding_handle; + + ZERO_STRUCT(a); + ZERO_STRUCT(r); + ZERO_STRUCT(ra); + + ZERO_STRUCT(logon); + ZERO_STRUCT(validation); + + netlogon_creds_client_authenticator(creds, &a); + + logon.password = &pinfo; + + r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &a; + r.in.return_authenticator = &ra; + r.in.logon_level = NetlogonInteractiveTransitiveInformation; + r.in.logon = &logon; + r.in.validation_level = 6; + r.in.flags = &flags; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + r.out.flags = &flags; + + pinfo.identity_info.domain_name.string = account_domain; + pinfo.identity_info.parameter_control = parameter_control; + pinfo.identity_info.logon_id = 0; + pinfo.identity_info.account_name.string = account_name; + pinfo.identity_info.workstation.string = workstation_name; + + if (!E_deshash(plain_pass, pinfo.lmpassword.hash)) { + ZERO_STRUCT(pinfo.lmpassword.hash); + } + E_md4hash(plain_pass, pinfo.ntpassword.hash); + + if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + netlogon_creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16); + netlogon_creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16); + } else { + netlogon_creds_des_encrypt(creds, &pinfo.lmpassword); + netlogon_creds_des_encrypt(creds, &pinfo.ntpassword); + } + + torture_comment(tctx, "Testing netr_LogonSamLogonWithFlags '%s' (Interactive Logon)\n", comment); + + status = dcerpc_netr_LogonSamLogonWithFlags_r(b, fn_ctx, &r); + torture_assert_ntstatus_ok_goto(tctx, + status, + ret, failed, + talloc_asprintf(tctx, "%s: netr_LogonSamLogonWithFlags - %s\n", + __location__, nt_errstr(status))); + + if (!r.out.return_authenticator) { + talloc_free(fn_ctx); + torture_fail(tctx, "no authenticator returned"); + } + + torture_assert_goto(tctx, + netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), + ret, failed, + "Credential chaining failed\n"); + + torture_assert_ntstatus_equal(tctx, r.out.result, expected_error, + talloc_asprintf(tctx, "[%s]\\[%s] netr_LogonSamLogonWithFlags - expected %s got %s\n", + account_domain, account_name, nt_errstr(expected_error), nt_errstr(r.out.result))); + + ret = true; + failed: + talloc_free(fn_ctx); + + return ret; +} + +/* This sets and resets the "minPwdAge" (in order to allow immediate user + * password changes). The behaviour is controlled by the "set" boolean. */ +static bool handle_minPwdAge(struct torture_context *torture, + TALLOC_CTX *mem_ctx, bool set) +{ + struct dcerpc_pipe *p; + struct policy_handle connect_handle, domain_handle; + struct samr_Connect c_r; + struct samr_LookupDomain ld_r; + struct samr_OpenDomain od_r; + struct samr_QueryDomainInfo qdi_r; + struct samr_SetDomainInfo sdi_r; + struct samr_Close cl_r; + struct lsa_String domName; + struct dom_sid *domSid = NULL; + union samr_DomainInfo *domInfo = NULL; + static int64_t old_minPwdAge = 0; + NTSTATUS status; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + c_r.in.system_name = 0; + c_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c_r.out.connect_handle = &connect_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_Connect_r(p->binding_handle, mem_ctx, &c_r), + "Connect failed"); + torture_assert_ntstatus_ok(torture, c_r.out.result, "Connect failed"); + + ld_r.in.connect_handle = &connect_handle; + ld_r.in.domain_name = &domName; + ld_r.in.domain_name->string = lpcfg_workgroup(torture->lp_ctx); + ld_r.out.sid = &domSid; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_LookupDomain_r(p->binding_handle, mem_ctx, &ld_r), + "LookupDomain failed"); + torture_assert_ntstatus_ok(torture, ld_r.out.result, + "LookupDomain failed"); + + od_r.in.connect_handle = &connect_handle; + od_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od_r.in.sid = *ld_r.out.sid; + od_r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_OpenDomain_r(p->binding_handle, mem_ctx, &od_r), + "OpenDomain failed"); + torture_assert_ntstatus_ok(torture, od_r.out.result, + "OpenDomain failed"); + + qdi_r.in.domain_handle = &domain_handle; + qdi_r.in.level = DomainPasswordInformation; + qdi_r.out.info = &domInfo; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_QueryDomainInfo_r(p->binding_handle, mem_ctx, &qdi_r), + "QueryDomainInfo failed"); + torture_assert_ntstatus_ok(torture, qdi_r.out.result, + "QueryDomainInfo failed"); + + if (set) { + old_minPwdAge = domInfo->info1.min_password_age; + domInfo->info1.min_password_age = 0; + } else { + domInfo->info1.min_password_age = old_minPwdAge; + } + + sdi_r.in.domain_handle = &domain_handle; + sdi_r.in.level = DomainPasswordInformation; + sdi_r.in.info = domInfo; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_SetDomainInfo_r(p->binding_handle, mem_ctx, &sdi_r), + "SetDomainInfo failed"); + torture_assert_ntstatus_ok(torture, sdi_r.out.result, + "SetDomainInfo failed"); + + cl_r.in.handle = &connect_handle; + cl_r.out.handle = &connect_handle; + + torture_assert_ntstatus_ok(torture, + dcerpc_samr_Close_r(p->binding_handle, mem_ctx, &cl_r), + "Close failed"); + torture_assert_ntstatus_ok(torture, cl_r.out.result, "Close failed"); + + return true; +} + +bool torture_rpc_samlogon(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + struct dcerpc_binding *b; + struct cli_credentials *machine_credentials; + TALLOC_CTX *mem_ctx = talloc_init("torture_rpc_netlogon"); + bool ret = true; + struct test_join *join_ctx = NULL; + struct test_join *user_ctx = NULL, *user_ctx_wrong_wks = NULL, *user_ctx_wrong_time = NULL; + const char *old_user_password, *user_password_wrong_wks, *user_password_wrong_time; + char *user_password; + const char *userdomain; + struct samr_SetUserInfo s; + union samr_UserInfo u; + int i; + int ci; + + unsigned int credential_flags[] = { + NETLOGON_NEG_AUTH2_FLAGS, + NETLOGON_NEG_ARCFOUR, + NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT, + NETLOGON_NEG_AUTH2_ADS_FLAGS, + 0 /* yes, this is a valid flag, causes the use of DES */ + }; + + struct netlogon_creds_CredentialState *creds; + struct dcerpc_pipe *tmp_p = NULL; + + torture_assert(torture, handle_minPwdAge(torture, mem_ctx, true), + "handle_minPwdAge error!"); + + /* We only need to join as a workstation here, and in future, + * if we wish to test against trusted domains, we must be a + * workstation here */ + join_ctx = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_WSTRUST, + &machine_credentials); + torture_assert(torture, join_ctx, "Failed to join as Workstation\n"); + + userdomain = torture_setting_string(torture, "userdomain", lpcfg_workgroup(torture->lp_ctx)); + + user_ctx = torture_create_testuser(torture, + TEST_USER_NAME, + userdomain, + ACB_NORMAL, + &old_user_password); + torture_assert(torture, user_ctx, "Failed to create a test user\n"); + + user_password = talloc_strdup(torture, old_user_password); + torture_assert(torture, user_password != NULL, "Failed to copy old_user_password\n"); + + tmp_p = torture_join_samr_pipe(user_ctx); + torture_assert(torture, tmp_p, "torture_join_samr_pipe failed\n"); + test_ChangePasswordUser3(tmp_p, torture, + TEST_USER_NAME, 16 /* > 14 */, &user_password, + NULL, 0, false); + + user_ctx_wrong_wks = torture_create_testuser(torture, + TEST_USER_NAME_WRONG_WKS, + userdomain, + ACB_NORMAL, + &user_password_wrong_wks); + torture_assert(torture, user_ctx_wrong_wks, + "Failed to create a test user (wrong workstation test)\n"); + + ZERO_STRUCT(u); + s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_wks); + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_WORKSTATIONS; + u.info21.workstations.string = "not" TEST_MACHINE_NAME; + + tmp_p = torture_join_samr_pipe(user_ctx_wrong_wks); + status = dcerpc_samr_SetUserInfo_r(tmp_p->binding_handle, mem_ctx, &s); + torture_assert_ntstatus_ok_goto(torture, status, ret, failed, + talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(status))); + torture_assert_ntstatus_ok_goto(torture, s.out.result, ret, failed, + talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(s.out.result))); + + user_ctx_wrong_time + = torture_create_testuser(torture, TEST_USER_NAME_WRONG_TIME, + userdomain, + ACB_NORMAL, + &user_password_wrong_time); + torture_assert(torture, user_ctx_wrong_time, + "Failed to create a test user (wrong workstation test)\n"); + + ZERO_STRUCT(u); + s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_time); + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_WORKSTATIONS | SAMR_FIELD_LOGON_HOURS; + u.info21.workstations.string = TEST_MACHINE_NAME; + u.info21.logon_hours.units_per_week = 168; + u.info21.logon_hours.bits = talloc_zero_array(mem_ctx, uint8_t, 168); + + tmp_p = torture_join_samr_pipe(user_ctx_wrong_time); + status = dcerpc_samr_SetUserInfo_r(tmp_p->binding_handle, mem_ctx, &s); + torture_assert_ntstatus_ok_goto(torture, status, ret, failed, + talloc_asprintf(torture, "SetUserInfo (logon times and list of workstations) failed - %s\n", nt_errstr(status))); + torture_assert_ntstatus_ok_goto(torture, s.out.result, ret, failed, + talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(s.out.result))); + + + torture_assert_ntstatus_ok_goto(torture, + torture_rpc_binding(torture, &b), + ret, + failed, + "Obtaining binding"); + + /* We have to use schannel, otherwise the SamLogonEx fails + * with INTERNAL_ERROR */ + + status = dcerpc_binding_set_flags(b, + DCERPC_SCHANNEL | + DCERPC_SIGN | DCERPC_SEAL | + DCERPC_SCHANNEL_128, + DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(torture, status, "set flags"); + + status = dcerpc_pipe_connect_b(mem_ctx, &p, b, + &ndr_table_netlogon, + machine_credentials, torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok_goto(torture, status, ret, failed, + talloc_asprintf(torture, "RPC pipe connect as domain member failed: %s\n", nt_errstr(status))); + + torture_assert_not_null_goto(torture, + creds = cli_credentials_get_netlogon_creds(machine_credentials), + ret, + failed, + "obtaining credentials"); + + { + + struct { + const char *comment; + const char *domain; + const char *username; + const char *password; + bool network_login; + NTSTATUS expected_interactive_error; + NTSTATUS expected_network_error; + uint32_t parameter_control; + bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */ + } usercreds[] = { + { + .comment = "domain\\user", + .domain = cli_credentials_get_domain( + samba_cmdline_get_creds()), + .username = cli_credentials_get_username( + samba_cmdline_get_creds()), + .password = cli_credentials_get_password( + samba_cmdline_get_creds()), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "realm\\user", + .domain = cli_credentials_get_realm( + samba_cmdline_get_creds()), + .username = cli_credentials_get_username( + samba_cmdline_get_creds()), + .password = cli_credentials_get_password( + samba_cmdline_get_creds()), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username( + samba_cmdline_get_creds()), + cli_credentials_get_domain( + samba_cmdline_get_creds()) + ), + .password = cli_credentials_get_password( + samba_cmdline_get_creds()), + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username( + samba_cmdline_get_creds()), + cli_credentials_get_realm( + samba_cmdline_get_creds()) + ), + .password = cli_credentials_get_password( + samba_cmdline_get_creds()), + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "machine domain\\user", + .domain = cli_credentials_get_domain(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine domain\\user", + .domain = cli_credentials_get_domain(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .expected_network_error = NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, + .parameter_control = 0, + }, + { + .comment = "machine realm\\user", + .domain = cli_credentials_get_realm(machine_credentials), + .username = cli_credentials_get_username(machine_credentials), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(machine_credentials), + cli_credentials_get_domain(machine_credentials) + ), + .password = cli_credentials_get_password(machine_credentials), + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "machine user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + cli_credentials_get_username(machine_credentials), + cli_credentials_get_realm(machine_credentials) + ), + .password = cli_credentials_get_password(machine_credentials), + .network_login = true, + .expected_interactive_error = NT_STATUS_NO_SUCH_USER, + .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT + }, + { + .comment = "test user (long pw): domain\\user", + .domain = userdomain, + .username = TEST_USER_NAME, + .password = user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "test user (long pw): user@realm", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + TEST_USER_NAME, + lpcfg_realm(torture->lp_ctx)), + .password = user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + { + .comment = "test user (long pw): user@domain", + .domain = NULL, + .username = talloc_asprintf(mem_ctx, + "%s@%s", + TEST_USER_NAME, + userdomain), + .password = user_password, + .network_login = false, /* works for some things, but not NTLMv2. Odd */ + .expected_interactive_error = NT_STATUS_OK, + .expected_network_error = NT_STATUS_OK, + .parameter_control = 0, + }, + /* Oddball, can we use the old password ? */ + { + .comment = "test user: user\\domain OLD PASSWORD", + .domain = userdomain, + .username = TEST_USER_NAME, + .password = old_user_password, + .network_login = true, + .expected_interactive_error = NT_STATUS_WRONG_PASSWORD, + .expected_network_error = NT_STATUS_OK, + .old_password = true + }, + { + .comment = "test user (wrong workstation): domain\\user", + .domain = userdomain, + .username = TEST_USER_NAME_WRONG_WKS, + .password = user_password_wrong_wks, + .network_login = true, + .expected_interactive_error = NT_STATUS_INVALID_WORKSTATION, + .expected_network_error = NT_STATUS_INVALID_WORKSTATION, + .parameter_control = 0, + } + }; + + /* Try all the tests for different username forms */ + for (ci = 0; ci < ARRAY_SIZE(usercreds); ci++) { + + torture_assert_goto(torture, + test_InteractiveLogon(p, mem_ctx, torture, creds, + usercreds[ci].comment, + TEST_MACHINE_NAME, + usercreds[ci].domain, + usercreds[ci].username, + usercreds[ci].password, + usercreds[ci].parameter_control, + usercreds[ci].expected_interactive_error), + ret, + failed, + talloc_asprintf(mem_ctx, "InteractiveLogon: %s", + usercreds[ci].comment)); + + if (usercreds[ci].network_login) { + torture_assert_goto(torture, + test_SamLogon(p, mem_ctx, torture, creds, + usercreds[ci].comment, + usercreds[ci].domain, + usercreds[ci].username, + usercreds[ci].password, + usercreds[ci].parameter_control, + usercreds[ci].expected_network_error, + usercreds[ci].old_password, + 0), + ret, + failed, + talloc_asprintf(mem_ctx, "SamLogon: %s", + usercreds[ci].comment)); + } + } + + /* Using the first username form, try the different + * credentials flag setups, on only one of the tests (checks + * session key encryption) */ + + for (i=0; i < ARRAY_SIZE(credential_flags); i++) { + /* TODO: Somehow we lost setting up the different credential flags here! */ + + torture_comment(torture, + "Testing with flags: 0x%08x\n", + credential_flags[i]); + + torture_assert_goto(torture, + test_InteractiveLogon(p, mem_ctx, torture, creds, + usercreds[0].comment, + TEST_MACHINE_NAME, + usercreds[0].domain, + usercreds[0].username, + usercreds[0].password, + usercreds[0].parameter_control, + usercreds[0].expected_interactive_error), + ret, + failed, + talloc_asprintf(mem_ctx, + "Testing InteractiveLogon with flags: 0x%08x\n", + credential_flags[i])); + + if (usercreds[0].network_login) { + torture_assert_goto(torture, + test_SamLogon(p, mem_ctx, torture, creds, + usercreds[0].comment, + usercreds[0].domain, + usercreds[0].username, + usercreds[0].password, + usercreds[0].parameter_control, + usercreds[0].expected_network_error, + usercreds[0].old_password, + 1), + ret, + failed, + talloc_asprintf(mem_ctx, + "Testing SamLogon with flags: 0x%08x\n", + credential_flags[i])); + } + } + + } +failed: + torture_assert(torture, handle_minPwdAge(torture, mem_ctx, false), + "handle_minPwdAge error!"); + + talloc_free(mem_ctx); + + torture_leave_domain(torture, join_ctx); + torture_leave_domain(torture, user_ctx); + torture_leave_domain(torture, user_ctx_wrong_wks); + torture_leave_domain(torture, user_ctx_wrong_time); + return ret; +} diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c new file mode 100644 index 0000000..22e0606 --- /dev/null +++ b/source4/torture/rpc/samr.c @@ -0,0 +1,9466 @@ +/* + Unix SMB/CIFS implementation. + test suite for samr rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2003 + Copyright (C) Jelmer Vernooij 2005-2007 + Copyright (C) Guenther Deschner 2008-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include +#include "system/time.h" +#include "system/network.h" +#include "librpc/gen_ndr/lsa.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_proto.h" +#include "../libcli/auth/schannel.h" +#include "torture/util.h" +#include "source4/librpc/rpc/dcerpc.h" +#include "librpc/rpc/dcerpc_samr.h" +#include "source3/rpc_client/init_samr.h" +#include "lib/crypto/gnutls_helpers.h" + +#undef strcasecmp + +#define TEST_ACCOUNT_NAME "samrtorturetest" +#define TEST_ACCOUNT_NAME_PWD "samrpwdlastset" +#define TEST_ALIASNAME "samrtorturetestalias" +#define TEST_GROUPNAME "samrtorturetestgroup" +#define TEST_MACHINENAME "samrtestmach$" +#define TEST_DOMAINNAME "samrtestdom$" + +#include +#include + +enum torture_samr_choice { + TORTURE_SAMR_PASSWORDS, + TORTURE_SAMR_PASSWORDS_PWDLASTSET, + TORTURE_SAMR_PASSWORDS_BADPWDCOUNT, + TORTURE_SAMR_PASSWORDS_LOCKOUT, + TORTURE_SAMR_USER_ATTRIBUTES, + TORTURE_SAMR_USER_PRIVILEGES, + TORTURE_SAMR_OTHER, + TORTURE_SAMR_MANY_ACCOUNTS, + TORTURE_SAMR_MANY_GROUPS, + TORTURE_SAMR_MANY_ALIASES +}; + +struct torture_samr_context { + struct policy_handle handle; + struct cli_credentials *machine_credentials; + enum torture_samr_choice choice; + uint32_t num_objects_large_dc; +}; + +static bool test_QueryUserInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle); + +static bool test_QueryUserInfo2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle); + +static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle); + +static bool test_ChangePassword(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *acct_name, + struct policy_handle *domain_handle, char **password); + +static void init_lsa_String(struct lsa_String *string, const char *s) +{ + string->string = s; +} + +static void init_lsa_StringLarge(struct lsa_StringLarge *string, const char *s) +{ + string->string = s; +} + +static void init_lsa_BinaryString(struct lsa_BinaryString *string, const char *s, uint32_t length) +{ + string->length = length; + string->size = length; + string->array = (uint16_t *)discard_const(s); +} + +bool test_samr_handle_Close(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_Close r; + + r.in.handle = handle; + r.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Close_r(b, tctx, &r), + "Close failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "Close failed"); + + return true; +} + +static bool test_Shutdown(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_Shutdown r; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_skip(tctx, "samr_Shutdown disabled - enable dangerous tests to use\n"); + return true; + } + + r.in.connect_handle = handle; + + torture_comment(tctx, "Testing samr_Shutdown\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Shutdown_r(b, tctx, &r), + "Shutdown failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "Shutdown failed"); + + return true; +} + +static bool test_SetDsrmPassword(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_SetDsrmPassword r; + struct lsa_String string; + struct samr_Password hash; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_skip(tctx, "samr_SetDsrmPassword disabled - enable dangerous tests to use"); + } + + E_md4hash("TeSTDSRM123", hash.hash); + + init_lsa_String(&string, "Administrator"); + + r.in.name = &string; + r.in.unknown = 0; + r.in.hash = &hash; + + torture_comment(tctx, "Testing samr_SetDsrmPassword\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDsrmPassword_r(b, tctx, &r), + "SetDsrmPassword failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_SUPPORTED, "SetDsrmPassword failed"); + + return true; +} + + +static bool test_QuerySecurity(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QuerySecurity r; + struct samr_SetSecurity s; + struct sec_desc_buf *sdbuf = NULL; + + r.in.handle = handle; + r.in.sec_info = 7; + r.out.sdbuf = &sdbuf; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r), + "QuerySecurity failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed"); + + torture_assert(tctx, sdbuf != NULL, "sdbuf is NULL"); + + s.in.handle = handle; + s.in.sec_info = 7; + s.in.sdbuf = sdbuf; + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "skipping SetSecurity test against Samba4\n"); + } + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &s), + "SetSecurity failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "SetSecurity failed"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r), + "QuerySecurity failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed"); + + return true; +} + + +static bool test_SetUserInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx, + struct policy_handle *handle, uint32_t base_acct_flags, + const char *base_account_name) +{ + struct samr_SetUserInfo s; + struct samr_SetUserInfo2 s2; + struct samr_QueryUserInfo q; + struct samr_QueryUserInfo q0; + union samr_UserInfo u; + union samr_UserInfo *info; + bool ret = true; + const char *test_account_name; + + uint32_t user_extra_flags = 0; + + if (!torture_setting_bool(tctx, "samba3", false)) { + if (base_acct_flags == ACB_NORMAL) { + /* When created, accounts are expired by default */ + user_extra_flags = ACB_PW_EXPIRED; + } + } + + s.in.user_handle = handle; + s.in.info = &u; + + s2.in.user_handle = handle; + s2.in.info = &u; + + q.in.user_handle = handle; + q.out.info = &info; + q0 = q; + +#define TESTCALL(call, r) \ + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ ##call## _r(b, tctx, &r),\ + #call " failed"); \ + if (!NT_STATUS_IS_OK(r.out.result)) { \ + torture_result(tctx, TORTURE_FAIL, #call " level %u failed - %s (%s)\n", \ + r.in.level, nt_errstr(r.out.result), __location__); \ + ret = false; \ + break; \ + } + +#define STRING_EQUAL(s1, s2, field) \ + torture_assert_str_equal(tctx, s1, s2, "Failed to set " #field) + +#define MEM_EQUAL(s1, s2, length, field) \ + torture_assert_mem_equal(tctx, s1, s2, length, "Failed to set " #field) + +#define INT_EQUAL(i1, i2, field) \ + torture_assert_int_equal(tctx, i1, i2, "Failed to set " #field) + +#define TEST_USERINFO_STRING(lvl1, field1, lvl2, field2, value, fpval) do { \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *info; \ + if (lvl1 == 21) { \ + ZERO_STRUCT(u.info21); \ + u.info21.fields_present = fpval; \ + } \ + init_lsa_String(&u.info ## lvl1.field1, value); \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + init_lsa_String(&u.info ## lvl1.field1, ""); \ + TESTCALL(QueryUserInfo, q); \ + u = *info; \ + STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *info; \ + STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \ + } while (0) + +#define TEST_USERINFO_BINARYSTRING(lvl1, field1, lvl2, field2, value, fpval) do { \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *info; \ + if (lvl1 == 21) { \ + ZERO_STRUCT(u.info21); \ + u.info21.fields_present = fpval; \ + } \ + init_lsa_BinaryString(&u.info ## lvl1.field1, value, strlen(value)); \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + init_lsa_BinaryString(&u.info ## lvl1.field1, "", 1); \ + TESTCALL(QueryUserInfo, q); \ + u = *info; \ + MEM_EQUAL(u.info ## lvl1.field1.array, value, strlen(value), field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *info; \ + MEM_EQUAL(u.info ## lvl2.field2.array, value, strlen(value), field2); \ + } while (0) + +#define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *info; \ + if (lvl1 == 21) { \ + uint8_t *bits = u.info21.logon_hours.bits; \ + ZERO_STRUCT(u.info21); \ + if (fpval == SAMR_FIELD_LOGON_HOURS) { \ + u.info21.logon_hours.units_per_week = 168; \ + u.info21.logon_hours.bits = bits; \ + } \ + u.info21.fields_present = fpval; \ + } \ + u.info ## lvl1.field1 = value; \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + u.info ## lvl1.field1 = 0; \ + TESTCALL(QueryUserInfo, q); \ + u = *info; \ + INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *info; \ + INT_EQUAL(u.info ## lvl2.field2, exp_value, field1); \ + } while (0) + +#define TEST_USERINFO_INT(lvl1, field1, lvl2, field2, value, fpval) do { \ + TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, value, fpval); \ + } while (0) + + q0.in.level = 12; + do { TESTCALL(QueryUserInfo, q0) } while (0); + + TEST_USERINFO_STRING(2, comment, 1, comment, "xx2-1 comment", 0); + TEST_USERINFO_STRING(2, comment, 21, comment, "xx2-21 comment", 0); + TEST_USERINFO_STRING(21, comment, 21, comment, "xx21-21 comment", + SAMR_FIELD_COMMENT); + + test_account_name = talloc_asprintf(tctx, "%sxx7-1", base_account_name); + TEST_USERINFO_STRING(7, account_name, 1, account_name, test_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name); + TEST_USERINFO_STRING(7, account_name, 3, account_name, test_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name); + TEST_USERINFO_STRING(7, account_name, 5, account_name, test_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name); + TEST_USERINFO_STRING(7, account_name, 6, account_name, test_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name); + TEST_USERINFO_STRING(7, account_name, 7, account_name, test_account_name, 0); + test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name); + TEST_USERINFO_STRING(7, account_name, 21, account_name, test_account_name, 0); + test_account_name = base_account_name; + TEST_USERINFO_STRING(21, account_name, 21, account_name, test_account_name, + SAMR_FIELD_ACCOUNT_NAME); + + TEST_USERINFO_STRING(6, full_name, 1, full_name, "xx6-1 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 3, full_name, "xx6-3 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 5, full_name, "xx6-5 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 6, full_name, "xx6-6 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 8, full_name, "xx6-8 full_name", 0); + TEST_USERINFO_STRING(6, full_name, 21, full_name, "xx6-21 full_name", 0); + TEST_USERINFO_STRING(8, full_name, 21, full_name, "xx8-21 full_name", 0); + TEST_USERINFO_STRING(21, full_name, 21, full_name, "xx21-21 full_name", + SAMR_FIELD_FULL_NAME); + + TEST_USERINFO_STRING(6, full_name, 1, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 3, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 5, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 6, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 8, full_name, "", 0); + TEST_USERINFO_STRING(6, full_name, 21, full_name, "", 0); + TEST_USERINFO_STRING(8, full_name, 21, full_name, "", 0); + TEST_USERINFO_STRING(21, full_name, 21, full_name, "", + SAMR_FIELD_FULL_NAME); + + TEST_USERINFO_STRING(11, logon_script, 3, logon_script, "xx11-3 logon_script", 0); + TEST_USERINFO_STRING(11, logon_script, 5, logon_script, "xx11-5 logon_script", 0); + TEST_USERINFO_STRING(11, logon_script, 21, logon_script, "xx11-21 logon_script", 0); + TEST_USERINFO_STRING(21, logon_script, 21, logon_script, "xx21-21 logon_script", + SAMR_FIELD_LOGON_SCRIPT); + + TEST_USERINFO_STRING(12, profile_path, 3, profile_path, "xx12-3 profile_path", 0); + TEST_USERINFO_STRING(12, profile_path, 5, profile_path, "xx12-5 profile_path", 0); + TEST_USERINFO_STRING(12, profile_path, 21, profile_path, "xx12-21 profile_path", 0); + TEST_USERINFO_STRING(21, profile_path, 21, profile_path, "xx21-21 profile_path", + SAMR_FIELD_PROFILE_PATH); + + TEST_USERINFO_STRING(10, home_directory, 3, home_directory, "xx10-3 home_directory", 0); + TEST_USERINFO_STRING(10, home_directory, 5, home_directory, "xx10-5 home_directory", 0); + TEST_USERINFO_STRING(10, home_directory, 21, home_directory, "xx10-21 home_directory", 0); + TEST_USERINFO_STRING(21, home_directory, 21, home_directory, "xx21-21 home_directory", + SAMR_FIELD_HOME_DIRECTORY); + TEST_USERINFO_STRING(21, home_directory, 10, home_directory, "xx21-10 home_directory", + SAMR_FIELD_HOME_DIRECTORY); + + TEST_USERINFO_STRING(10, home_drive, 3, home_drive, "xx10-3 home_drive", 0); + TEST_USERINFO_STRING(10, home_drive, 5, home_drive, "xx10-5 home_drive", 0); + TEST_USERINFO_STRING(10, home_drive, 21, home_drive, "xx10-21 home_drive", 0); + TEST_USERINFO_STRING(21, home_drive, 21, home_drive, "xx21-21 home_drive", + SAMR_FIELD_HOME_DRIVE); + TEST_USERINFO_STRING(21, home_drive, 10, home_drive, "xx21-10 home_drive", + SAMR_FIELD_HOME_DRIVE); + + TEST_USERINFO_STRING(13, description, 1, description, "xx13-1 description", 0); + TEST_USERINFO_STRING(13, description, 5, description, "xx13-5 description", 0); + TEST_USERINFO_STRING(13, description, 21, description, "xx13-21 description", 0); + TEST_USERINFO_STRING(21, description, 21, description, "xx21-21 description", + SAMR_FIELD_DESCRIPTION); + + TEST_USERINFO_STRING(14, workstations, 3, workstations, "14workstation3", 0); + TEST_USERINFO_STRING(14, workstations, 5, workstations, "14workstation4", 0); + TEST_USERINFO_STRING(14, workstations, 21, workstations, "14workstation21", 0); + TEST_USERINFO_STRING(21, workstations, 21, workstations, "21workstation21", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 3, workstations, "21workstation3", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 5, workstations, "21workstation5", + SAMR_FIELD_WORKSTATIONS); + TEST_USERINFO_STRING(21, workstations, 14, workstations, "21workstation14", + SAMR_FIELD_WORKSTATIONS); + + TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "xx20-21 parameters", 0); + TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "xx21-21 parameters", + SAMR_FIELD_PARAMETERS); + TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "xx21-20 parameters", + SAMR_FIELD_PARAMETERS); + /* also empty user parameters are allowed */ + TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "", 0); + TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "", + SAMR_FIELD_PARAMETERS); + TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "", + SAMR_FIELD_PARAMETERS); + + /* Samba 3 cannot store country_code and code_page atm. - gd */ + if (!torture_setting_bool(tctx, "samba3", false)) { + TEST_USERINFO_INT(2, country_code, 2, country_code, __LINE__, 0); + TEST_USERINFO_INT(2, country_code, 21, country_code, __LINE__, 0); + TEST_USERINFO_INT(21, country_code, 21, country_code, __LINE__, + SAMR_FIELD_COUNTRY_CODE); + TEST_USERINFO_INT(21, country_code, 2, country_code, __LINE__, + SAMR_FIELD_COUNTRY_CODE); + + TEST_USERINFO_INT(2, code_page, 21, code_page, __LINE__, 0); + TEST_USERINFO_INT(21, code_page, 21, code_page, __LINE__, + SAMR_FIELD_CODE_PAGE); + TEST_USERINFO_INT(21, code_page, 2, code_page, __LINE__, + SAMR_FIELD_CODE_PAGE); + } + + if (!torture_setting_bool(tctx, "samba3", false)) { + TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, __LINE__, 0); + TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, __LINE__, 0); + TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, __LINE__, + SAMR_FIELD_ACCT_EXPIRY); + } else { + /* Samba 3 can only store seconds / time_t in passdb - gd */ + NTTIME nt; + unix_to_nt_time(&nt, time(NULL) + __LINE__); + TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, nt, 0); + unix_to_nt_time(&nt, time(NULL) + __LINE__); + TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, nt, 0); + unix_to_nt_time(&nt, time(NULL) + __LINE__); + TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); + unix_to_nt_time(&nt, time(NULL) + __LINE__); + TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); + unix_to_nt_time(&nt, time(NULL) + __LINE__); + TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY); + } + + TEST_USERINFO_INT(4, logon_hours.bits[3], 3, logon_hours.bits[3], 1, 0); + TEST_USERINFO_INT(4, logon_hours.bits[3], 5, logon_hours.bits[3], 2, 0); + TEST_USERINFO_INT(4, logon_hours.bits[3], 21, logon_hours.bits[3], 3, 0); + TEST_USERINFO_INT(21, logon_hours.bits[3], 21, logon_hours.bits[3], 4, + SAMR_FIELD_LOGON_HOURS); + + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), + 0); + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); + + /* Setting PWNOEXP clears the magic ACB_PW_EXPIRED flag */ + TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), + (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP), + 0); + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ), + (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags), + 0); + + + /* The 'autolock' flag doesn't stick - check this */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_AUTOLOCK), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); +#if 0 + /* Removing the 'disabled' flag doesn't stick - check this */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + 0); +#endif + + /* Samba3 cannot store these atm */ + if (!torture_setting_bool(tctx, "samba3", false)) { + /* The 'store plaintext' flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED), + (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED | user_extra_flags), + 0); + /* The 'use DES' flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY), + (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY | user_extra_flags), + 0); + /* The 'don't require kerberos pre-authentication flag does stick */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH), + (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH | user_extra_flags), + 0); + /* The 'no kerberos PAC required' flag sticks */ + TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD), + (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD | user_extra_flags), + 0); + } + TEST_USERINFO_INT_EXP(21, acct_flags, 21, acct_flags, + (base_acct_flags | ACB_DISABLED), + (base_acct_flags | ACB_DISABLED | user_extra_flags), + SAMR_FIELD_ACCT_FLAGS); + +#if 0 + /* these fail with win2003 - it appears you can't set the primary gid? + the set succeeds, but the gid isn't changed. Very weird! */ + TEST_USERINFO_INT(9, primary_gid, 1, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 3, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 5, primary_gid, 513); + TEST_USERINFO_INT(9, primary_gid, 21, primary_gid, 513); +#endif + + return ret; +} + +/* + generate a random password for password change tests +*/ +static char *samr_rand_pass_silent(TALLOC_CTX *mem_ctx, int min_len) +{ + size_t len = MAX(8, min_len); + char *s = generate_random_password(mem_ctx, len, len+6); + return s; +} + +static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) +{ + char *s = samr_rand_pass_silent(mem_ctx, min_len); + printf("Generated password '%s'\n", s); + return s; + +} + +/* + generate a random password for password change tests +*/ +static DATA_BLOB samr_very_rand_pass(TALLOC_CTX *mem_ctx, int len) +{ + int i; + DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */); + generate_random_buffer(password.data, password.length); + + for (i=0; i < len; i++) { + if (((uint16_t *)password.data)[i] == 0) { + ((uint16_t *)password.data)[i] = 1; + } + } + + return password; +} + +/* + generate a random password for password change tests (fixed length) +*/ +static char *samr_rand_pass_fixed_len(TALLOC_CTX *mem_ctx, int len) +{ + char *s = generate_random_password(mem_ctx, len, len); + printf("Generated password '%s'\n", s); + return s; +} + +static bool test_SetUserPass(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 24; + + u.info24.password_expired = 0; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + status = init_samr_CryptPassword(newpass, + &session_key, + &u.info24.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + torture_comment(tctx, "Testing SetUserInfo level 24 (set password)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_SetUserPass_23(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + struct dcerpc_binding_handle *b = p->binding_handle; + char *newpass; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 23; + + ZERO_STRUCT(u); + + u.info23.info.fields_present = fields_present; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + status = init_samr_CryptPassword(newpass, + &session_key, + &u.info23.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + torture_comment(tctx, "Testing SetUserInfo level 23 (set password)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + /* This should break the key nicely */ + session_key.data[0]++; + + status = init_samr_CryptPassword(newpass, + &session_key, + &u.info23.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + /* Reset the session key */ + session_key.data[0]--; + + torture_comment(tctx, "Testing SetUserInfo level 23 (set password) with wrong password\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } + + return ret; +} + +static bool test_SetUserPass_32(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + DATA_BLOB session_key; + uint8_t salt_data[16]; + DATA_BLOB salt = { + .data = salt_data, + .length = sizeof(salt_data), + }; + char *newpass = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + bool ret = true; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 32; + + ZERO_STRUCT(u); + + u.info32.info.fields_present = fields_present; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, + TORTURE_FAIL, + "SetUserInfo level %u - no session key - %s\n", + s.in.level, + nt_errstr(status)); + return false; + } + + generate_nonce_buffer(salt.data, salt.length); + + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info32.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordAES failed"); + + torture_comment(tctx, + "Testing SetUserInfo level 32 (set password aes)\n"); + + status = dcerpc_samr_SetUserInfo_r(b, tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, + __FUNCTION__, + newpass, + nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, + TORTURE_FAIL, + "SetUserInfo level %u failed - %s\n", + s.in.level, + nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + session_key.data[0]++; + + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info32.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + /* Reset the key */ + session_key.data[0]--; + + torture_comment(tctx, + "Testing SetUserInfo level 32 (set password aes) with " + "wrong session key\n"); + + status = dcerpc_samr_SetUserInfo_r(b, tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed"); + torture_comment(tctx, + "(%s:%s) new_password[%s] status[%s]\n", + __location__, + __FUNCTION__, + newpass, + nt_errstr(s.out.result)); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, + TORTURE_FAIL, + "SetUserInfo level %u should have failed with " + "WRONG_PASSWORD- %s\n", + s.in.level, + nt_errstr(s.out.result)); + ret = false; + } + + return ret; +} + + +static bool test_SetUserPass_31(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, bool makeshort, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + uint8_t salt_data[16]; + DATA_BLOB salt = { + .data = salt_data, + .length = sizeof(salt_data), + }; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + if (makeshort && policy_min_pw_len) { + newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1); + } else { + newpass = samr_rand_pass(tctx, policy_min_pw_len); + } + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 31; + + ZERO_STRUCT(u); + + u.info31.password_expired = 0; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_nonce_buffer(salt.data, salt.length); + + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info31.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + session_key.data[0]++; + + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info31.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + /* Reset the key */ + session_key.data[0]--; + + torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes) with wrong session key\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_SetUserPassEx(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, bool makeshort, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + if (makeshort && policy_min_pw_len) { + newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1); + } else { + newpass = samr_rand_pass(tctx, policy_min_pw_len); + } + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 26; + + u.info26.password_expired = 0; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info26.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + session_key.data[0]++; + + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info26.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + /* Reset the key */ + session_key.data[0]--; + + torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex) with wrong session key\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + +static bool test_SetUserPass_25(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 25; + + ZERO_STRUCT(u); + + u.info25.info.fields_present = fields_present; + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info25.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + /* This should break the key nicely */ + session_key.data[0]++; + + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info25.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + + /* Reset the key */ + session_key.data[0]--; + + torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with wrong session key\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } + + return ret; +} + +static bool test_SetUserPass_18(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + uint8_t lm_hash[16], nt_hash[16]; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 18; + + ZERO_STRUCT(u); + + u.info18.nt_pwd_active = true; + u.info18.lm_pwd_active = true; + + E_md4hash(newpass, nt_hash); + E_deshash(newpass, lm_hash); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + { + DATA_BLOB in,out; + in = data_blob_const(nt_hash, 16); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + memcpy(u.info18.nt_pwd.hash, out.data, out.length); + } + { + DATA_BLOB in,out; + in = data_blob_const(lm_hash, 16); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + memcpy(u.info18.lm_pwd.hash, out.data, out.length); + } + + torture_comment(tctx, "Testing SetUserInfo level 18 (set password hash)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + +static bool test_SetUserPass_21(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, uint32_t fields_present, + char **password) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + uint8_t lm_hash[16], nt_hash[16]; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 21; + + E_md4hash(newpass, nt_hash); + E_deshash(newpass, lm_hash); + + ZERO_STRUCT(u); + + u.info21.fields_present = fields_present; + + if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { + u.info21.lm_owf_password.length = 16; + u.info21.lm_owf_password.size = 16; + u.info21.lm_owf_password.array = (uint16_t *)lm_hash; + u.info21.lm_password_set = true; + } + + if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { + u.info21.nt_owf_password.length = 16; + u.info21.nt_owf_password.size = 16; + u.info21.nt_owf_password.array = (uint16_t *)nt_hash; + u.info21.nt_password_set = true; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { + DATA_BLOB in,out; + in = data_blob_const(u.info21.lm_owf_password.array, + u.info21.lm_owf_password.length); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + u.info21.lm_owf_password.array = (uint16_t *)out.data; + } + + if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { + DATA_BLOB in,out; + in = data_blob_const(u.info21.nt_owf_password.array, + u.info21.nt_owf_password.length); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + u.info21.nt_owf_password.array = (uint16_t *)out.data; + } + + torture_comment(tctx, "Testing SetUserInfo level 21 (set password hash)\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } else { + *password = newpass; + } + + /* try invalid length */ + if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { + + u.info21.nt_owf_password.length++; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } + } + + if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { + + u.info21.lm_owf_password.length++; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } + } + + return ret; +} + +static bool test_SetUserPass_level_ex(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint16_t level, + uint32_t fields_present, + char **password, uint8_t password_expired, + bool use_setinfo2, + bool *matched_expected_error) +{ + NTSTATUS status; + NTSTATUS expected_error = NT_STATUS_OK; + struct samr_SetUserInfo s; + struct samr_SetUserInfo2 s2; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + uint8_t salt_data[16]; + DATA_BLOB salt = { + .data = salt_data, + .length = sizeof(salt_data), + }; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + const char *comment = NULL; + uint8_t lm_hash[16], nt_hash[16]; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass_silent(tctx, policy_min_pw_len); + + if (use_setinfo2) { + s2.in.user_handle = handle; + s2.in.info = &u; + s2.in.level = level; + } else { + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = level; + } + + if (fields_present & SAMR_FIELD_COMMENT) { + comment = talloc_asprintf(tctx, "comment: %ld\n", (long int) time(NULL)); + } + + ZERO_STRUCT(u); + + switch (level) { + case 18: + E_md4hash(newpass, nt_hash); + E_deshash(newpass, lm_hash); + + u.info18.nt_pwd_active = true; + u.info18.lm_pwd_active = true; + u.info18.password_expired = password_expired; + + memcpy(u.info18.lm_pwd.hash, lm_hash, 16); + memcpy(u.info18.nt_pwd.hash, nt_hash, 16); + + break; + case 21: + E_md4hash(newpass, nt_hash); + E_deshash(newpass, lm_hash); + + u.info21.fields_present = fields_present; + u.info21.password_expired = password_expired; + u.info21.comment.string = comment; + + if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { + u.info21.lm_owf_password.length = 16; + u.info21.lm_owf_password.size = 16; + u.info21.lm_owf_password.array = (uint16_t *)lm_hash; + u.info21.lm_password_set = true; + } + + if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { + u.info21.nt_owf_password.length = 16; + u.info21.nt_owf_password.size = 16; + u.info21.nt_owf_password.array = (uint16_t *)nt_hash; + u.info21.nt_password_set = true; + } + + break; + case 23: + u.info23.info.fields_present = fields_present; + u.info23.info.password_expired = password_expired; + u.info23.info.comment.string = comment; + + break; + case 24: + u.info24.password_expired = password_expired; + + break; + case 25: + u.info25.info.fields_present = fields_present; + u.info25.info.password_expired = password_expired; + u.info25.info.comment.string = comment; + + break; + case 26: + u.info26.password_expired = password_expired; + + break; + case 31: + u.info31.password_expired = password_expired; + + break; + case 28: + u.info25.info.fields_present = fields_present; + u.info25.info.password_expired = password_expired; + u.info25.info.comment.string = comment; + + break; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_nonce_buffer(salt.data, salt.length); + + switch (level) { + case 18: + { + DATA_BLOB in,out; + in = data_blob_const(u.info18.nt_pwd.hash, 16); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + memcpy(u.info18.nt_pwd.hash, out.data, out.length); + } + { + DATA_BLOB in,out; + in = data_blob_const(u.info18.lm_pwd.hash, 16); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + memcpy(u.info18.lm_pwd.hash, out.data, out.length); + } + + break; + case 21: + if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) { + DATA_BLOB in,out; + in = data_blob_const(u.info21.lm_owf_password.array, + u.info21.lm_owf_password.length); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + u.info21.lm_owf_password.array = (uint16_t *)out.data; + } + if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) { + DATA_BLOB in,out; + in = data_blob_const(u.info21.nt_owf_password.array, + u.info21.nt_owf_password.length); + out = data_blob_talloc_zero(tctx, 16); + sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT); + u.info21.nt_owf_password.array = (uint16_t *)out.data; + } + break; + case 23: + status = init_samr_CryptPassword(newpass, + &session_key, + &u.info23.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + break; + case 24: + status = init_samr_CryptPassword(newpass, + &session_key, + &u.info24.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + break; + case 25: + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info25.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + break; + case 26: + status = init_samr_CryptPasswordEx(newpass, + &session_key, + &u.info26.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPasswordEx failed"); + break; + case 31: + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info31.password); + + break; + case 32: + status = init_samr_CryptPasswordAES(tctx, + newpass, + &salt, + &session_key, + &u.info32.password); + + break; + } + + if (use_setinfo2) { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo2_r(b, tctx, &s2), + "SetUserInfo2 failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s2.out.result)); + status = s2.out.result; + } else { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + newpass, nt_errstr(s.out.result)); + status = s.out.result; + } + + if (!NT_STATUS_IS_OK(status)) { + if (fields_present == 0) { + expected_error = NT_STATUS_INVALID_PARAMETER; + } + if (fields_present & SAMR_FIELD_LAST_PWD_CHANGE) { + expected_error = NT_STATUS_ACCESS_DENIED; + } + } + + if (!NT_STATUS_IS_OK(expected_error)) { + if (use_setinfo2) { + torture_assert_ntstatus_equal(tctx, + s2.out.result, + expected_error, "SetUserInfo2 failed"); + } else { + torture_assert_ntstatus_equal(tctx, + s.out.result, + expected_error, "SetUserInfo failed"); + } + *matched_expected_error = true; + return true; + } + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo%s level %u failed - %s\n", + use_setinfo2 ? "2":"", level, nt_errstr(status)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + +static bool test_SetAliasInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_SetAliasInfo r; + struct samr_QueryAliasInfo q; + union samr_AliasInfo *info; + uint16_t levels[] = {2, 3}; + int i; + bool ret = true; + + /* Ignoring switch level 1, as that includes the number of members for the alias + * and setting this to a wrong value might have negative consequences + */ + + for (i=0;iname,TEST_ALIASNAME); break; + case ALIASINFODESCRIPTION: init_lsa_String(&r.in.info->description, + "Test Description, should test I18N as well"); break; + case ALIASINFOALL: torture_comment(tctx, "ALIASINFOALL ignored\n"); break; + } + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetAliasInfo_r(b, tctx, &r), + "SetAliasInfo failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetAliasInfo level %u failed - %s\n", + levels[i], nt_errstr(r.out.result)); + ret = false; + } + + q.in.alias_handle = handle; + q.in.level = levels[i]; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryAliasInfo_r(b, tctx, &q), + "QueryAliasInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryAliasInfo level %u failed - %s\n", + levels[i], nt_errstr(q.out.result)); + ret = false; + } + } + + return ret; +} + +static bool test_GetGroupsForUser(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *user_handle) +{ + struct samr_GetGroupsForUser r; + struct samr_RidWithAttributeArray *rids = NULL; + + torture_comment(tctx, "Testing GetGroupsForUser\n"); + + r.in.user_handle = user_handle; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r), + "GetGroupsForUser failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetGroupsForUser failed"); + + return true; + +} + +static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + struct lsa_String *domain_name) +{ + struct samr_GetDomPwInfo r; + struct samr_PwInfo info; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.domain_name = domain_name; + r.out.info = &info; + + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), + "GetDomPwInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); + + r.in.domain_name->string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), + "GetDomPwInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); + + r.in.domain_name->string = "\\\\__NONAME__"; + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), + "GetDomPwInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); + + r.in.domain_name->string = "\\\\Builtin"; + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), + "GetDomPwInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed"); + + return true; +} + +static bool test_GetUserPwInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_GetUserPwInfo r; + struct samr_PwInfo info; + + torture_comment(tctx, "Testing GetUserPwInfo\n"); + + r.in.user_handle = handle; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &r), + "GetUserPwInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetUserPwInfo"); + + return true; +} + +static NTSTATUS test_LookupName(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, const char *name, + uint32_t *rid) +{ + NTSTATUS status; + struct samr_LookupNames n; + struct lsa_String sname[2]; + struct samr_Ids rids, types; + + init_lsa_String(&sname[0], name); + + n.in.domain_handle = domain_handle; + n.in.num_names = 1; + n.in.names = sname; + n.out.rids = &rids; + n.out.types = &types; + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (NT_STATUS_IS_OK(n.out.result)) { + *rid = n.out.rids->ids[0]; + } else { + return n.out.result; + } + + init_lsa_String(&sname[1], "xxNONAMExx"); + n.in.num_names = 2; + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_EQUAL(n.out.result, STATUS_SOME_UNMAPPED)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames[2] failed - %s\n", nt_errstr(n.out.result)); + if (NT_STATUS_IS_OK(n.out.result)) { + return NT_STATUS_UNSUCCESSFUL; + } + return n.out.result; + } + + n.in.num_names = 0; + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(n.out.result)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames[0] failed - %s\n", nt_errstr(status)); + return n.out.result; + } + + init_lsa_String(&sname[0], "xxNONAMExx"); + n.in.num_names = 1; + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames[1 bad name] failed - %s\n", nt_errstr(n.out.result)); + if (NT_STATUS_IS_OK(n.out.result)) { + return NT_STATUS_UNSUCCESSFUL; + } + return n.out.result; + } + + init_lsa_String(&sname[0], "xxNONAMExx"); + init_lsa_String(&sname[1], "xxNONAME2xx"); + n.in.num_names = 2; + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames[2 bad names] failed - %s\n", nt_errstr(n.out.result)); + if (NT_STATUS_IS_OK(n.out.result)) { + return NT_STATUS_UNSUCCESSFUL; + } + return n.out.result; + } + + return NT_STATUS_OK; +} + +static NTSTATUS test_OpenUser_byname(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *name, struct policy_handle *user_handle) +{ + NTSTATUS status; + struct samr_OpenUser r; + uint32_t rid; + + status = test_LookupName(b, tctx, domain_handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = user_handle; + status = dcerpc_samr_OpenUser_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(r.out.result)); + } + + return r.out.result; +} + +#if 0 +static bool test_ChangePasswordNT3(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct samr_ChangePasswordUser r; + bool ret = true; + struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; + struct policy_handle user_handle; + char *oldpass = "test"; + char *newpass = "test2"; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + + status = test_OpenUser_byname(p, tctx, handle, "testuser", &user_handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + torture_comment(tctx, "Testing ChangePasswordUser for user 'testuser'\n"); + + torture_comment(tctx, "old password: %s\n", oldpass); + torture_comment(tctx, "new password: %s\n", newpass); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } + + if (!test_samr_handle_Close(p, tctx, &user_handle)) { + ret = false; + } + + return ret; +} +#endif + +static bool test_ChangePasswordUser(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + const char *acct_name, + struct policy_handle *handle, char **password) +{ + NTSTATUS status; + struct samr_ChangePasswordUser r; + bool ret = true; + struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6; + struct policy_handle user_handle; + char *oldpass; + uint8_t old_nt_hash[16], new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + bool changed = true; + + char *newpass; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + + status = test_OpenUser_byname(b, tctx, handle, acct_name, &user_handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + pwp.in.user_handle = &user_handle; + pwp.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp), + "GetUserPwInfo failed"); + if (NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + torture_comment(tctx, "Testing ChangePasswordUser\n"); + + torture_assert(tctx, *password != NULL, + "Failing ChangePasswordUser as old password was NULL. Previous test failed?"); + + oldpass = *password; + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + /* Break the NT hash */ + hash3.hash[0]++; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + /* Do not proceed if this call has been removed */ + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) { + torture_skip(tctx, "ValidatePassword not supported by server\n"); + } + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, + "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash"); + } + + /* Unbreak the NT hash */ + hash3.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + /* Break the LM hash */ + hash1.hash[0]--; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, + "expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash"); + } + + /* Unbreak the NT hash */ + hash3.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + /* Break the LM cross */ + hash6.hash[0]++; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) && + !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) + { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the LM cross-hash, got %s\n", nt_errstr(r.out.result)); + ret = false; + } + + /* Unbreak the LM cross */ + hash6.hash[0]--; + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + /* Break the NT cross */ + hash5.hash[0]++; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) && + !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) + { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the NT cross-hash, got %s\n", nt_errstr(r.out.result)); + ret = false; + } + + /* Unbreak the NT cross */ + hash5.hash[0]--; + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 0; + r.in.lm_cross = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (NT_STATUS_IS_OK(r.out.result)) { + changed = true; + *password = newpass; + } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result)); + ret = false; + } + + oldpass = newpass; + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 0; + r.in.nt_cross = NULL; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (NT_STATUS_IS_OK(r.out.result)) { + changed = true; + *password = newpass; + } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result)); + ret = false; + } + + oldpass = newpass; + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + + /* Reset the hashes to not broken values */ + E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash); + E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash); + E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash); + E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash); + E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash); + E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash); + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } else { + changed = true; + *password = newpass; + } + + r.in.user_handle = &user_handle; + r.in.lm_present = 1; + r.in.old_lm_crypted = &hash1; + r.in.new_lm_crypted = &hash2; + r.in.nt_present = 1; + r.in.old_nt_crypted = &hash3; + r.in.new_nt_crypted = &hash4; + r.in.cross1_present = 1; + r.in.nt_cross = &hash5; + r.in.cross2_present = 1; + r.in.lm_cross = &hash6; + + if (changed) { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r), + "ChangePasswordUser failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); + } else if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(r.out.result)); + ret = false; + } + } + + + if (!test_samr_handle_Close(b, tctx, &user_handle)) { + ret = false; + } + + return ret; +} + + +static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *acct_name, + struct policy_handle *handle, char **password) +{ + struct samr_OemChangePasswordUser2 r; + bool ret = true; + struct samr_Password lm_verifier; + struct samr_CryptPassword lm_pass; + struct lsa_AsciiString server, account, account_bad; + char *oldpass; + char *newpass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t old_lm_hash[16], new_lm_hash[16]; + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t session_key = { + .data = old_lm_hash, + .size = 16 + }; + + struct samr_GetDomPwInfo dom_pw_info; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + + struct lsa_String domain_name; + + domain_name.string = ""; + dom_pw_info.in.domain_name = &domain_name; + dom_pw_info.out.info = &info; + + torture_comment(tctx, "Testing OemChangePasswordUser2\n"); + + torture_assert(tctx, *password != NULL, + "Failing OemChangePasswordUser2 as old password was NULL. Previous test failed?"); + + oldpass = *password; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), + "GetDomPwInfo failed"); + if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { + policy_min_pw_len = dom_pw_info.out.info->min_password_length; + } + + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + account.string = acct_name; + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &session_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); + gnutls_cipher_deinit(cipher_hnd); + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + /* Break the verification */ + lm_verifier.hash[0]++; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_assert_ntstatus_equal(tctx, + r.out.result, + NT_STATUS_NOT_IMPLEMENTED, + "Samba4 should refuse LM password change"); + /* + * No point continuing, once we have checked this is not + * implemented + */ + return true; + } + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + /* Break the old password */ + old_lm_hash[0]++; + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &session_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); + gnutls_cipher_deinit(cipher_hnd); + /* unbreak it for the next operation */ + old_lm_hash[0]--; + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &session_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); + gnutls_cipher_deinit(cipher_hnd); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) + && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD for invalid user - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + /* This shouldn't be a valid name */ + account_bad.string = TEST_ACCOUNT_NAME "XX"; + r.in.account = &account_bad; + r.in.password = NULL; + r.in.hash = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII); + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &session_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516); + gnutls_cipher_deinit(cipher_hnd); + E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.password = &lm_pass; + r.in.hash = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r), + "OemChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_comment(tctx, "OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *acct_name, + char **password, + char *newpass, bool allow_password_restriction) +{ + struct samr_ChangePasswordUser2 r; + bool ret = true; + struct lsa_String server, account; + struct samr_CryptPassword nt_pass, lm_pass; + struct samr_Password nt_verifier, lm_verifier; + char *oldpass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + DATA_BLOB old_nt_hash_blob + = data_blob_const(old_nt_hash, sizeof(old_nt_hash)); + struct samr_GetDomPwInfo dom_pw_info; + struct samr_PwInfo info; + + struct lsa_String domain_name; + NTSTATUS status; + + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t old_lm_key = { + .data = old_lm_hash, + .size = sizeof(old_lm_hash), + }; + + domain_name.string = ""; + dom_pw_info.in.domain_name = &domain_name; + dom_pw_info.out.info = &info; + + torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name); + + torture_assert(tctx, *password != NULL, + "Failing ChangePasswordUser2 as old password was NULL. Previous test failed?"); + oldpass = *password; + + if (!newpass) { + int policy_min_pw_len = 0; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), + "GetDomPwInfo failed"); + if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { + policy_min_pw_len = dom_pw_info.out.info->min_password_length; + } + + newpass = samr_rand_pass(tctx, policy_min_pw_len); + } + + server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, acct_name); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE); + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &old_lm_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, + lm_pass.data, + 516); + gnutls_cipher_deinit(cipher_hnd); + + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r), + "ChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (allow_password_restriction && NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } else { + *password = newpass; + } + + return ret; +} + + +static bool test_ChangePasswordUser2_ntstatus(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *acct_name, + const char *password, NTSTATUS status) +{ + struct samr_ChangePasswordUser2 r; + struct lsa_String server, account; + struct samr_CryptPassword nt_pass, lm_pass; + struct samr_Password nt_verifier, lm_verifier; + const char *oldpass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + DATA_BLOB old_nt_hash_blob + = data_blob_const(old_nt_hash, sizeof(old_nt_hash)); + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t old_lm_key = { + .data = old_lm_hash, + .size = sizeof(old_lm_hash), + }; + + struct samr_GetDomPwInfo dom_pw_info; + struct samr_PwInfo info; + + struct lsa_String domain_name; + NTSTATUS crypt_status; + + char *newpass; + int policy_min_pw_len = 0; + + domain_name.string = ""; + dom_pw_info.in.domain_name = &domain_name; + dom_pw_info.out.info = &info; + + torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name); + + oldpass = password; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info), + "GetDomPwInfo failed"); + if (NT_STATUS_IS_OK(dom_pw_info.out.result)) { + policy_min_pw_len = dom_pw_info.out.info->min_password_length; + } + + newpass = samr_rand_pass(tctx, policy_min_pw_len); + + server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, acct_name); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE); + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &old_lm_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, + lm_pass.data, + 516); + gnutls_cipher_deinit(cipher_hnd); + + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + crypt_status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + crypt_status, + "init_samr_CryptPassword failed"); + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r), + "ChangePasswordUser2 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result)); + } else { + torture_assert_ntstatus_equal(tctx, r.out.result, status, "ChangePasswordUser2 returned unexpected value"); + } + + return true; +} + + +bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *account_string, + int policy_min_pw_len, + char **password, + const char *newpass, + NTTIME last_password_change, + bool handle_reject_reason) +{ + struct samr_ChangePasswordUser3 r; + bool ret = true; + struct lsa_String server, account, account_bad; + struct samr_CryptPassword nt_pass, lm_pass; + struct samr_Password nt_verifier, lm_verifier; + char *oldpass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; + uint8_t old_lm_hash[16], new_lm_hash[16]; + NTTIME t; + struct samr_DomInfo1 *dominfo = NULL; + struct userPwdChangeFailureInformation *reject = NULL; + DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, 16); + NTSTATUS status; + + torture_comment(tctx, "Testing ChangePasswordUser3\n"); + + if (newpass == NULL) { + do { + if (policy_min_pw_len == 0) { + newpass = samr_rand_pass(tctx, policy_min_pw_len); + } else { + newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len); + } + } while (check_password_quality(newpass) == false); + } else { + torture_comment(tctx, "Using password '%s'\n", newpass); + } + + torture_assert(tctx, *password != NULL, + "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?"); + + oldpass = *password; + server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, account_string); + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + /* + * The new plaintext password is encrypted using RC4 with the + * old NT password hash (directly, with no confounder). The + * password is at the end of the random padded buffer, + * offering a little protection. + * + * This is almost certainly wrong, it should be the old LM + * hash, it was switched in an unrelated commit + * 579c13da43d5b40ac6d6c1436399fbc1d8dfd054 in 2004. + */ + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &lm_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + /* + * Now we prepare a DES cross-hash of the old LM and new NT + * passwords to link the two buffers + */ + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + /* + * The new plaintext password is also encrypted using RC4 with + * the old NT password hash (directly, with no confounder). + * The password is at the end of the random padded buffer, + * offering a little protection. + */ + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + /* + * Another DES based cross-hash + */ + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + /* Break the verification */ + nt_verifier.hash[0]++; + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && + (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &lm_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + /* Break the NT Hash */ + old_nt_hash[0]++; + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + /* Unbreak it again */ + old_nt_hash[0]--; + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) && + (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + /* This shouldn't be a valid name */ + init_lsa_String(&account_bad, talloc_asprintf(tctx, "%sXX", account_string)); + + r.in.account = &account_bad; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n", + nt_errstr(r.out.result)); + ret = false; + } + + E_md4hash(oldpass, old_nt_hash); + E_md4hash(newpass, new_nt_hash); + + E_deshash(oldpass, old_lm_hash); + E_deshash(newpass, new_lm_hash); + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &lm_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash); + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword"); + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 1; + r.in.lm_password = &lm_pass; + r.in.lm_verifier = &lm_verifier; + r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; + + unix_to_nt_time(&t, time(NULL)); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + torture_comment(tctx, "(%s): dominfo[%s], reject[%s], handle_reject_reason[%s], " + "last_password_change[%s], dominfo->min_password_age[%lld]\n", + __location__, + (dominfo == NULL)? "NULL" : "present", + reject ? "true" : "false", + handle_reject_reason ? "true" : "false", + null_nttime(last_password_change) ? "null" : "not null", + dominfo ? (long long)dominfo->min_password_age : (long long)0); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) + && dominfo + && reject + && handle_reject_reason + && (!null_nttime(last_password_change) || !dominfo->min_password_age)) { + if (dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) { + + if (reject && (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR)) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", + SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); + return false; + } + } + + /* We tested the order of precedence which is as follows: + + * pwd min_age + * pwd length + * pwd complexity + * pwd history + + Guenther */ + + if ((dominfo->min_password_age < 0) && !null_nttime(last_password_change) && + (last_password_change - dominfo->min_password_age > t)) { + + if (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", + SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); + return false; + } + + } else if ((dominfo->min_password_length > 0) && + (strlen(newpass) < dominfo->min_password_length)) { + + if (reject->extendedFailureReason != SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PASSWORD_TOO_SHORT (%d), got %d\n", + SAM_PWD_CHANGE_PASSWORD_TOO_SHORT, reject->extendedFailureReason); + return false; + } + + } else if ((dominfo->password_history_length > 0) && + strequal(oldpass, newpass)) { + + if (reject->extendedFailureReason != SAM_PWD_CHANGE_PWD_IN_HISTORY) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PWD_IN_HISTORY (%d), got %d\n", + SAM_PWD_CHANGE_PWD_IN_HISTORY, reject->extendedFailureReason); + return false; + } + } else if (dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) { + + if (reject->extendedFailureReason != SAM_PWD_CHANGE_NOT_COMPLEX) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NOT_COMPLEX (%d), got %d\n", + SAM_PWD_CHANGE_NOT_COMPLEX, reject->extendedFailureReason); + return false; + } + + } + + if (reject->extendedFailureReason == SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) { + /* retry with adjusted size */ + return test_ChangePasswordUser3(p, tctx, account_string, + dominfo->min_password_length, + password, NULL, 0, false); + + } + + } else if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", + SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); + return false; + } + /* Perhaps the server has a 'min password age' set? */ + + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3"); + + *password = talloc_strdup(tctx, newpass); + } + + return ret; +} + +bool test_ChangePasswordUser4(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *account_string, + int policy_min_pw_len, + char **password, + const char *newpassword) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct samr_ChangePasswordUser4 r; + const char *oldpassword = *password; + char *srv_str = NULL; + struct lsa_String server; + struct lsa_String account; + uint8_t old_nt_key_data[16] = {0}; + gnutls_datum_t old_nt_key = { + .data = old_nt_key_data, + .size = sizeof(old_nt_key), + }; + uint8_t cek_data[16] = {0}; + DATA_BLOB cek = { + .data = cek_data, + .length = sizeof(cek_data), + }; + uint8_t pw_data[514] = {0}; + DATA_BLOB plaintext = { + .data = pw_data, + .length = sizeof(pw_data), + }; + DATA_BLOB ciphertext = data_blob_null; + struct samr_EncryptedPasswordAES pwd_buf = {.cipher_len = 0}; + DATA_BLOB iv = { + .data = pwd_buf.salt, + .length = sizeof(pwd_buf.salt), + }; + gnutls_datum_t iv_datum = { + .data = iv.data, + .size = iv.length, + }; + uint64_t pbkdf2_iterations = generate_random_u64_range(5000, 1000000); + NTSTATUS status; + bool ok; + int rc; + + torture_comment(tctx, "Testing ChangePasswordUser4\n"); + + if (newpassword == NULL) { + do { + if (policy_min_pw_len == 0) { + newpassword = + samr_rand_pass(tctx, policy_min_pw_len); + } else { + newpassword = samr_rand_pass_fixed_len( + tctx, + policy_min_pw_len); + } + } while (check_password_quality(newpassword) == false); + } else { + torture_comment(tctx, "Using password '%s'\n", newpassword); + } + + torture_assert_not_null(tctx, + *password, + "Failing ChangePasswordUser4 as old password " + "was NULL. Previous test failed?"); + + srv_str = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + torture_assert_not_null(tctx, srv_str, "srvstr is NULL"); + init_lsa_String(&server, srv_str); + + init_lsa_String(&account, account_string); + + E_md4hash(oldpassword, old_nt_key_data); + + generate_nonce_buffer(iv.data, iv.length); + + rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512, + &old_nt_key, + &iv_datum, + pbkdf2_iterations, + cek.data, + cek.length); + torture_assert_int_equal(tctx, rc, 0, "gnutls_pbkdf2 failed"); + + ok = encode_pwd_buffer514_from_str(pw_data, newpassword, STR_UNICODE); + torture_assert(tctx, ok, "encode_aes_pw_buffer failed"); + + status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt( + tctx, + &plaintext, + &cek, + &samr_aes256_enc_key_salt, + &samr_aes256_mac_key_salt, + &iv, + &ciphertext, + pwd_buf.auth_data); + torture_assert_ntstatus_ok( + tctx, + status, + "samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt failed"); + + pwd_buf.cipher_len = ciphertext.length; + pwd_buf.cipher = ciphertext.data; + pwd_buf.PBKDF2Iterations = pbkdf2_iterations; + + r.in.server = &server; + r.in.account = &account; + r.in.password = &pwd_buf; + + status = dcerpc_samr_ChangePasswordUser4_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ChangePasswordUser4 failed"); + + *password = talloc_strdup(tctx, newpassword); + return true; +} + +bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *account_string, + struct policy_handle *handle, + char **password) +{ + NTSTATUS status; + struct samr_ChangePasswordUser3 r; + struct samr_SetUserInfo s; + union samr_UserInfo u; + DATA_BLOB session_key; + + bool ret = true; + struct lsa_String server, account; + struct samr_CryptPassword nt_pass; + struct samr_Password nt_verifier; + DATA_BLOB new_random_pass; + char *newpass; + char *oldpass; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16]; + DATA_BLOB old_nt_hash_blob + = data_blob_const(old_nt_hash, + sizeof(old_nt_hash)); + NTTIME t; + struct samr_DomInfo1 *dominfo = NULL; + struct userPwdChangeFailureInformation *reject = NULL; + gnutls_cipher_hd_t cipher_hnd = NULL; + uint8_t _confounder[16] = {0}; + DATA_BLOB confounder + = data_blob_const(_confounder, + sizeof(_confounder)); + DATA_BLOB pw_data; + gnutls_datum_t old_nt_key = { + .data = old_nt_hash, + .size = sizeof(old_nt_hash), + }; + + new_random_pass = samr_very_rand_pass(tctx, 128); + + torture_assert(tctx, *password != NULL, + "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?"); + + oldpass = *password; + server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + init_lsa_String(&account, account_string); + + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = 25; + + ZERO_STRUCT(u); + + u.info25.info.fields_present = SAMR_FIELD_NT_PASSWORD_PRESENT; + + set_pw_in_buffer(u.info25.password.data, &new_random_pass); + + pw_data = data_blob_const(u.info25.password.data, 516); + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_random_buffer(_confounder, + sizeof(_confounder)); + + samba_gnutls_arcfour_confounded_md5(&confounder, + &session_key, + &pw_data, + SAMBA_GNUTLS_ENCRYPT); + + memcpy(&u.info25.password.data[516], _confounder, sizeof(_confounder)); + + torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with a password made up of only random bytes\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s), + "SetUserInfo failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, "RANDOM", nt_errstr(s.out.result)); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + ret = false; + } + + torture_comment(tctx, "Testing ChangePasswordUser3 with a password made up of only random bytes\n"); + + mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length); + + new_random_pass = samr_very_rand_pass(tctx, 128); + + mdfour(new_nt_hash, new_random_pass.data, new_random_pass.length); + + set_pw_in_buffer(nt_pass.data, &new_random_pass); + + gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &old_nt_key, + NULL); + gnutls_cipher_encrypt(cipher_hnd, + nt_pass.data, + 516); + gnutls_cipher_deinit(cipher_hnd); + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 0; + r.in.lm_password = NULL; + r.in.lm_verifier = NULL; + r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; + + unix_to_nt_time(&t, time(NULL)); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, "RANDOM", nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", + SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); + return false; + } + /* Perhaps the server has a 'min password age' set? */ + + } else if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } + + newpass = samr_rand_pass(tctx, 128); + + mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length); + + E_md4hash(newpass, new_nt_hash); + + status = init_samr_CryptPassword(newpass, + &old_nt_hash_blob, + &nt_pass); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash); + + r.in.server = &server; + r.in.account = &account; + r.in.nt_password = &nt_pass; + r.in.nt_verifier = &nt_verifier; + r.in.lm_change = 0; + r.in.lm_password = NULL; + r.in.lm_verifier = NULL; + r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; + + unix_to_nt_time(&t, time(NULL)); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r), + "ChangePasswordUser3 failed"); + torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n", + __location__, __FUNCTION__, + oldpass, newpass, nt_errstr(r.out.result)); + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) { + if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) { + torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n", + SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason); + return false; + } + /* Perhaps the server has a 'min password age' set? */ + + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3 (on second random password)"); + *password = talloc_strdup(tctx, newpass); + } + + return ret; +} + + +static bool test_GetMembersInAlias(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *alias_handle) +{ + struct samr_GetMembersInAlias r; + struct lsa_SidArray sids; + + torture_comment(tctx, "Testing GetMembersInAlias\n"); + + r.in.alias_handle = alias_handle; + r.out.sids = &sids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetMembersInAlias_r(b, tctx, &r), + "GetMembersInAlias failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GetMembersInAlias failed"); + + return true; +} + +static bool test_AddMemberToAlias(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid) +{ + struct samr_AddAliasMember r; + struct samr_DeleteAliasMember d; + struct dom_sid *sid; + + sid = dom_sid_add_rid(tctx, domain_sid, 512); + + torture_comment(tctx, "Testing AddAliasMember\n"); + r.in.alias_handle = alias_handle; + r.in.sid = sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddAliasMember_r(b, tctx, &r), + "AddAliasMember failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "AddAliasMember failed"); + + d.in.alias_handle = alias_handle; + d.in.sid = sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteAliasMember_r(b, tctx, &d), + "DeleteAliasMember failed"); + torture_assert_ntstatus_ok(tctx, d.out.result, "DelAliasMember failed"); + + return true; +} + +static bool test_AddMultipleMembersToAlias(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *alias_handle) +{ + struct samr_AddMultipleMembersToAlias a; + struct samr_RemoveMultipleMembersFromAlias r; + struct lsa_SidArray sids; + + torture_comment(tctx, "Testing AddMultipleMembersToAlias\n"); + a.in.alias_handle = alias_handle; + a.in.sids = &sids; + + sids.num_sids = 3; + sids.sids = talloc_array(tctx, struct lsa_SidPtr, 3); + + sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1"); + sids.sids[1].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-2"); + sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-3"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddMultipleMembersToAlias_r(b, tctx, &a), + "AddMultipleMembersToAlias failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, "AddMultipleMembersToAlias"); + + + torture_comment(tctx, "Testing RemoveMultipleMembersFromAlias\n"); + r.in.alias_handle = alias_handle; + r.in.sids = &sids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), + "RemoveMultipleMembersFromAlias failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed"); + + /* strange! removing twice doesn't give any error */ + torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), + "RemoveMultipleMembersFromAlias failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed"); + + /* but removing an alias that isn't there does */ + sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-4"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r), + "RemoveMultipleMembersFromAlias failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, "RemoveMultipleMembersFromAlias"); + + return true; +} + +static bool test_GetAliasMembership(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle) +{ + struct samr_GetAliasMembership r; + struct lsa_SidArray sids; + struct samr_Ids rids; + + torture_comment(tctx, "Testing GetAliasMembership\n"); + + r.in.domain_handle = domain_handle; + r.in.sids = &sids; + r.out.rids = &rids; + + sids.num_sids = 0; + sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), + "GetAliasMembership failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_GetAliasMembership failed"); + + torture_assert_int_equal(tctx, sids.num_sids, rids.count, + "protocol misbehaviour"); + + sids.num_sids = 1; + sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids); + sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), + "samr_GetAliasMembership failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_GetAliasMembership failed"); + +#if 0 + /* only true for w2k8 it seems + * win7, xp, w2k3 will return a 0 length array pointer */ + + if (rids.ids && (rids.count == 0)) { + torture_fail(tctx, "samr_GetAliasMembership returned 0 count and a rids array"); + } +#endif + if (!rids.ids && rids.count) { + torture_fail(tctx, "samr_GetAliasMembership returned non-0 count but no rids"); + } + + return true; +} + +static bool test_TestPrivateFunctionsUser(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *user_handle) +{ + struct samr_TestPrivateFunctionsUser r; + + torture_comment(tctx, "Testing TestPrivateFunctionsUser\n"); + + r.in.user_handle = user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_TestPrivateFunctionsUser_r(b, tctx, &r), + "TestPrivateFunctionsUser failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsUser"); + + return true; +} + +static bool test_QueryUserInfo_pwdlastset(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + bool use_info2, + NTTIME *pwdlastset) +{ + NTSTATUS status; + uint16_t levels[] = { /* 3, */ 5, 21 }; + int i; + /* NTTIME pwdlastset3 = 0; */ + NTTIME pwdlastset5 = 0; + NTTIME pwdlastset21 = 0; + + torture_comment(tctx, "Testing QueryUserInfo%s level 5 and 21 call ", + use_info2 ? "2":""); + + for (i=0; iinfo3.last_password_change; */ + break; + case 5: + pwdlastset5 = info->info5.last_password_change; + break; + case 21: + pwdlastset21 = info->info21.last_password_change; + break; + default: + return false; + } + } + /* torture_assert_int_equal(tctx, pwdlastset3, pwdlastset5, + "pwdlastset mixup"); */ + torture_assert_int_equal(tctx, pwdlastset5, pwdlastset21, + "pwdlastset mixup"); + + *pwdlastset = pwdlastset21; + + torture_comment(tctx, "(pwdlastset: %llu)\n", + (unsigned long long) *pwdlastset); + + return true; +} + +static bool test_SamLogon(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials, + struct cli_credentials *test_credentials, + NTSTATUS expected_result, + bool interactive) +{ + NTSTATUS status; + struct netr_LogonSamLogonEx r; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative; + struct netr_IdentityInfo identity; + struct netr_NetworkInfo ninfo; + struct netr_PasswordInfo pinfo; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int flags = CLI_CRED_NTLM_AUTH; + uint32_t samlogon_flags = 0; + struct netlogon_creds_CredentialState *creds; + struct netr_Authenticator a; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, (creds = cli_credentials_get_netlogon_creds(machine_credentials)), ""); + + if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + cli_credentials_get_ntlm_username_domain(test_credentials, tctx, + &identity.account_name.string, + &identity.domain_name.string); + + identity.parameter_control = + MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | + MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; + identity.logon_id = 0; + identity.workstation.string = cli_credentials_get_workstation(test_credentials); + + if (interactive) { + netlogon_creds_client_authenticator(creds, &a); + + if (!E_deshash(cli_credentials_get_password(test_credentials), pinfo.lmpassword.hash)) { + ZERO_STRUCT(pinfo.lmpassword.hash); + } + E_md4hash(cli_credentials_get_password(test_credentials), pinfo.ntpassword.hash); + + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, pinfo.lmpassword.hash, 16); + netlogon_creds_aes_encrypt(creds, pinfo.ntpassword.hash, 16); + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + netlogon_creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16); + netlogon_creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16); + } else { + netlogon_creds_des_encrypt(creds, &pinfo.lmpassword); + netlogon_creds_des_encrypt(creds, &pinfo.ntpassword); + } + + pinfo.identity_info = identity; + logon.password = &pinfo; + + r.in.logon_level = NetlogonInteractiveInformation; + } else { + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials), + cli_credentials_get_domain(test_credentials)); + + status = cli_credentials_get_ntlm_response(test_credentials, tctx, + &flags, + chal, + NULL, /* server_timestamp */ + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info = identity; + logon.network = &ninfo; + + r.in.logon_level = NetlogonNetworkInformation; + } + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(test_credentials); + r.in.logon = &logon; + r.in.flags = &samlogon_flags; + r.out.flags = &samlogon_flags; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + + torture_comment(tctx, "Testing LogonSamLogon with name %s\n", identity.account_name.string); + + r.in.validation_level = 6; + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "netr_LogonSamLogonEx failed"); + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) { + r.in.validation_level = 3; + torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "netr_LogonSamLogonEx failed"); + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_assert_ntstatus_equal(tctx, r.out.result, expected_result, "LogonSamLogonEx failed"); + return true; + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogonEx failed"); + } + + return true; +} + +static bool test_SamLogon_with_creds(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_creds, + const char *acct_name, + const char *password, + NTSTATUS expected_samlogon_result, + bool interactive) +{ + bool ret = true; + struct cli_credentials *test_credentials; + + test_credentials = cli_credentials_init(tctx); + + cli_credentials_set_workstation(test_credentials, + cli_credentials_get_workstation(machine_creds), CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, + cli_credentials_get_domain(machine_creds), CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, + acct_name, CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, + password, CRED_SPECIFIED); + + torture_comment(tctx, "Testing samlogon (%s) as %s password: %s\n", + interactive ? "interactive" : "network", acct_name, password); + + if (!test_SamLogon(tctx, p, machine_creds, test_credentials, + expected_samlogon_result, interactive)) { + torture_result(tctx, TORTURE_FAIL, "new password did not work\n"); + ret = false; + } + + return ret; +} + +static bool test_SetPassword_level(struct dcerpc_pipe *p, + struct dcerpc_pipe *np, + struct torture_context *tctx, + struct policy_handle *handle, + uint16_t level, + uint32_t fields_present, + uint8_t password_expired, + bool *matched_expected_error, + bool use_setinfo2, + const char *acct_name, + char **password, + struct cli_credentials *machine_creds, + bool use_queryinfo2, + NTTIME *pwdlastset, + NTSTATUS expected_samlogon_result) +{ + const char *fields = NULL; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + switch (level) { + case 21: + case 23: + case 25: + case 32: + fields = talloc_asprintf(tctx, "(fields_present: 0x%08x)", + fields_present); + break; + default: + break; + } + + torture_comment(tctx, "Testing SetUserInfo%s level %d call " + "(password_expired: %d) %s\n", + use_setinfo2 ? "2":"", level, password_expired, + fields ? fields : ""); + + if (!test_SetUserPass_level_ex(p, tctx, handle, level, + fields_present, + password, + password_expired, + use_setinfo2, + matched_expected_error)) { + ret = false; + } + + if (!test_QueryUserInfo_pwdlastset(b, tctx, handle, + use_queryinfo2, + pwdlastset)) { + ret = false; + } + + if (*matched_expected_error == true) { + return ret; + } + + if (!test_SamLogon_with_creds(tctx, np, + machine_creds, + acct_name, + *password, + expected_samlogon_result, + false)) { + ret = false; + } + + return ret; +} + +static bool setup_schannel_netlogon_pipe(struct torture_context *tctx, + struct cli_credentials *credentials, + struct dcerpc_pipe **p) +{ + struct dcerpc_binding *b; + NTSTATUS status; + + torture_assert_ntstatus_ok(tctx, torture_rpc_binding(tctx, &b), + "failed to get rpc binding"); + + /* We have to use schannel, otherwise the SamLogonEx fails + * with INTERNAL_ERROR */ + + status = dcerpc_binding_set_flags(b, + DCERPC_SCHANNEL | + DCERPC_SIGN | DCERPC_SEAL | + DCERPC_SCHANNEL_AUTO, + DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, p, b, &ndr_table_netlogon, + credentials, tctx->ev, tctx->lp_ctx), + "failed to bind to netlogon"); + + return true; +} + +static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *handle, + char **password, + struct cli_credentials *machine_credentials) +{ + int s = 0, q = 0, f = 0, l = 0, z = 0; + bool ret = true; + int delay = 50000; + bool set_levels[] = { false, true }; + bool query_levels[] = { false, true }; + uint32_t levels[] = { 18, 21, 26, 23, 24, 25, 31 }; /* Second half only used when TEST_ALL_LEVELS defined */ + uint32_t nonzeros[] = { 1, 24 }; + uint32_t fields_present[] = { + 0, + SAMR_FIELD_EXPIRED_FLAG, + SAMR_FIELD_LAST_PWD_CHANGE, + SAMR_FIELD_EXPIRED_FLAG | SAMR_FIELD_LAST_PWD_CHANGE, + SAMR_FIELD_COMMENT, + SAMR_FIELD_NT_PASSWORD_PRESENT, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE | SAMR_FIELD_EXPIRED_FLAG + }; + struct dcerpc_pipe *np = NULL; + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + delay = 999999; + torture_comment(tctx, "Samba3 has second granularity, setting delay to: %d\n", + delay); + } + + torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); + + /* set to 1 to enable testing for all possible opcode + (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2) + combinations */ +#if 0 +#define TEST_ALL_LEVELS 1 +#define TEST_SET_LEVELS 1 +#define TEST_QUERY_LEVELS 1 +#endif +#ifdef TEST_ALL_LEVELS + for (l=0; l 0) && (pwdlastset_new > 0) && + (pwdlastset_old >= pwdlastset_new)) { + torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); + ret = false; + } + break; + } + + pwdlastset_old = pwdlastset_new; + + usleep(delay); + + /* set #2 */ + + /* set a password, pwdlastset needs to get updated (increased + * value), password_expired value used here is 0 */ + + if (!test_SetPassword_level(p, np, tctx, handle, + levels[l], + fields_present[f], + 0, + &matched_expected_error, + set_levels[s], + acct_name, + password, + machine_credentials, + query_levels[q], + &pwdlastset_new, + expected_samlogon_result)) { + ret = false; + } + + /* when a password has been changed, pwdlastset must not be 0 afterwards + * and must be larger then the old value */ + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + /* SAMR_FIELD_EXPIRED_FLAG has not been set and no + * password has been changed, old and new pwdlastset + * need to be the same value */ + + if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && + !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) + { + torture_assert_int_equal(tctx, pwdlastset_old, + pwdlastset_new, "pwdlastset must be equal"); + break; + } + break; + default: + if (pwdlastset_old >= pwdlastset_new) { + torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " + "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n", + (unsigned long long) pwdlastset_old, + (unsigned long long) pwdlastset_new); + ret = false; + } + if (pwdlastset_new == 0) { + torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " + "expected non-0 pwdlastset, got: %llu\n", + (unsigned long long) pwdlastset_new); + ret = false; + } + break; + } + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && + (pwdlastset_old > 0) && (pwdlastset_new > 0) && + (pwdlastset_old >= pwdlastset_new)) { + torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); + ret = false; + } + break; + } + + pwdlastset_old = pwdlastset_new; + + usleep(delay); + + /* set #2b */ + + /* set a password, pwdlastset needs to get updated (increased + * value), password_expired value used here is 0 */ + + if (!test_SetPassword_level(p, np, tctx, handle, + levels[l], + fields_present[f], + 0, + &matched_expected_error, + set_levels[s], + acct_name, + password, + machine_credentials, + query_levels[q], + &pwdlastset_new, + expected_samlogon_result)) { + ret = false; + } + + /* when a password has been changed, pwdlastset must not be 0 afterwards + * and must be larger then the old value */ + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + /* SAMR_FIELD_EXPIRED_FLAG has not been set and no + * password has been changed, old and new pwdlastset + * need to be the same value */ + + if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && + !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) + { + torture_assert_int_equal(tctx, pwdlastset_old, + pwdlastset_new, "pwdlastset must be equal"); + break; + } + break; + default: + if (pwdlastset_old >= pwdlastset_new) { + torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " + "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n", + (unsigned long long) pwdlastset_old, + (unsigned long long) pwdlastset_new); + ret = false; + } + if (pwdlastset_new == 0) { + torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " + "expected non-0 pwdlastset, got: %llu\n", + (unsigned long long) pwdlastset_new); + ret = false; + } + break; + } + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && + (pwdlastset_old > 0) && (pwdlastset_new > 0) && + (pwdlastset_old >= pwdlastset_new)) { + torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); + ret = false; + } + break; + } + + pwdlastset_old = pwdlastset_new; + + usleep(delay); + + /* set #3 */ + + /* set a password and force password change (pwdlastset 0) by + * setting the password expired flag to a non-0 value */ + + if (!test_SetPassword_level(p, np, tctx, handle, + levels[l], + fields_present[f], + nonzeros[z], + &matched_expected_error, + set_levels[s], + acct_name, + password, + machine_credentials, + query_levels[q], + &pwdlastset_new, + expected_samlogon_result)) { + ret = false; + } + + /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25 + * set without the SAMR_FIELD_EXPIRED_FLAG */ + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + if ((pwdlastset_new != 0) && + !(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG)) { + torture_comment(tctx, "not considering a non-0 " + "pwdLastSet as a an error as the " + "SAMR_FIELD_EXPIRED_FLAG has not " + "been set\n"); + break; + } + + /* SAMR_FIELD_EXPIRED_FLAG has not been set and no + * password has been changed, old and new pwdlastset + * need to be the same value */ + + if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) && + !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) + { + torture_assert_int_equal(tctx, pwdlastset_old, + pwdlastset_new, "pwdlastset must be equal"); + break; + } + break; + default: + if (pwdlastset_new != 0) { + torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: " + "expected pwdLastSet 0, got %llu\n", + (unsigned long long) pwdlastset_old); + ret = false; + } + break; + } + + switch (levels[l]) { + case 21: + case 23: + case 25: + case 32: + if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) || + (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) && + (pwdlastset_old > 0) && (pwdlastset_new > 0) && + (pwdlastset_old >= pwdlastset_new)) { + torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n"); + ret = false; + } + break; + } + + /* if the level we are testing does not have a fields_present + * field, skip all fields present tests by setting f to to + * arraysize */ + switch (levels[l]) { + case 18: + case 24: + case 26: + case 31: + f = ARRAY_SIZE(fields_present); + break; + } + +#ifdef TEST_QUERY_LEVELS + } +#endif +#ifdef TEST_SET_LEVELS + } +#endif + } /* fields present */ + } /* nonzeros */ + } /* levels */ + +#undef TEST_SET_LEVELS +#undef TEST_QUERY_LEVELS + + talloc_free(np); + + return ret; +} + +static bool test_QueryUserInfo_badpwdcount(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *badpwdcount) +{ + union samr_UserInfo *info; + struct samr_QueryUserInfo r; + + r.in.user_handle = handle; + r.in.level = 3; + r.out.info = &info; + + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + "failed to query userinfo"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query userinfo"); + + *badpwdcount = info->info3.bad_password_count; + + torture_comment(tctx, " (bad password count: %d)\n", *badpwdcount); + + return true; +} + +static bool test_SetUserInfo_acct_flags(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *user_handle, + uint32_t acct_flags) +{ + struct samr_SetUserInfo r; + union samr_UserInfo user_info; + + torture_comment(tctx, "Testing SetUserInfo level 16\n"); + + user_info.info16.acct_flags = acct_flags; + + r.in.user_handle = user_handle; + r.in.level = 16; + r.in.info = &user_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &r), + "failed to set account flags"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to set account flags"); + + return true; +} + +static bool test_reset_badpwdcount(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *user_handle, + uint32_t acct_flags, + char **password) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + + torture_comment(tctx, "Testing SetUserInfo level 16 (enable account)\n"); + + torture_assert(tctx, + test_SetUserInfo_acct_flags(b, tctx, user_handle, + acct_flags & ~ACB_DISABLED), + "failed to enable user"); + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + + return true; +} + +static bool test_SetDomainInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + enum samr_DomainInfoClass level, + union samr_DomainInfo *info) +{ + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = level; + r.in.info = info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_SetDomainInfo_r(b, tctx, &r), + "failed to set domain info"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to set domain info"); + + return true; +} + +static bool test_SetDomainInfo_ntstatus(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + enum samr_DomainInfoClass level, + union samr_DomainInfo *info, + NTSTATUS expected) +{ + struct samr_SetDomainInfo r; + + r.in.domain_handle = domain_handle; + r.in.level = level; + r.in.info = info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &r), + "SetDomainInfo failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, expected, ""); + + return true; +} + +static bool test_QueryDomainInfo2_level(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + enum samr_DomainInfoClass level, + union samr_DomainInfo **q_info) +{ + struct samr_QueryDomainInfo2 r; + + r.in.domain_handle = domain_handle; + r.in.level = level; + r.out.info = q_info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r), + "failed to query domain info"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query domain info"); + + return true; +} + +static bool test_Password_badpwdcount(struct dcerpc_pipe *p, + struct dcerpc_pipe *np, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials, + const char *comment, + bool disable, + bool interactive, + NTSTATUS expected_success_status, + struct samr_DomInfo1 *info1, + struct samr_DomInfo12 *info12) +{ + union samr_DomainInfo info; + char **passwords; + int i; + uint32_t badpwdcount, tmp; + uint32_t password_history_length = 12; + uint32_t lockout_threshold = 15; + uint32_t lockout_seconds = 5; + uint64_t delta_time_factor = 10 * 1000 * 1000; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + lockout_seconds = 60; + } + + torture_comment(tctx, "\nTesting bad pwd count with: %s\n", comment); + + torture_assert(tctx, password_history_length < lockout_threshold, + "password history length needs to be smaller than account lockout threshold for this test"); + + + /* set policies */ + + info.info1 = *info1; + info.info1.password_history_length = password_history_length; + info.info1.min_password_age = 0; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainPasswordInformation, &info), + "failed to set password history length and min passwd age"); + + info.info12 = *info12; + info.info12.lockout_threshold = lockout_threshold; + + /* set lockout duration of 5 seconds */ + info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); + info.info12.lockout_window = ~(lockout_seconds * delta_time_factor); + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &info), + "failed to set lockout threshold"); + + /* reset bad pwd count */ + + torture_assert(tctx, + test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), ""); + + + /* enable or disable account */ + if (disable) { + torture_assert(tctx, + test_SetUserInfo_acct_flags(b, tctx, user_handle, + acct_flags | ACB_DISABLED), + "failed to disable user"); + } else { + torture_assert(tctx, + test_SetUserInfo_acct_flags(b, tctx, user_handle, + acct_flags & ~ACB_DISABLED), + "failed to enable user"); + } + + + /* setup password history */ + + passwords = talloc_array(tctx, char *, password_history_length); + + for (i=0; i < password_history_length; i++) { + + torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password), + "failed to set password"); + passwords[i] = talloc_strdup(tctx, *password); + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + expected_success_status, interactive)) { + torture_fail(tctx, "failed to auth with latest password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), ""); + + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + + /* test with wrong password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, "random_crap", + NT_STATUS_WRONG_PASSWORD, interactive)) { + torture_fail(tctx, "succeeded to authenticate with wrong password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), ""); + + torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1"); + + + /* test with latest good password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + passwords[password_history_length-1], + expected_success_status, interactive)) { + torture_fail(tctx, "succeeded to authenticate with wrong password"); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), ""); + + if (disable) { + torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1"); + } else { + /* only enabled accounts get the bad pwd count reset upon + * successful logon */ + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + tmp = badpwdcount; + + + /* test password history */ + + for (i=0; i < password_history_length; i++) { + + torture_comment(tctx, "Testing bad password count behavior with " + "password #%d of #%d\n", i, password_history_length); + + /* - network samlogon will succeed auth and not + * increase badpwdcount for 2 last entries + * - interactive samlogon only for the last one */ + + if (i == password_history_length - 1 || + (i == password_history_length - 2 && !interactive)) { + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + expected_success_status, interactive)) { + torture_fail(tctx, talloc_asprintf(tctx, "did not successfully to obtain %s for %s login with old password (#%d of #%d in history)", + nt_errstr(expected_success_status), + interactive ? "interactive" : "network", i, password_history_length)); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), ""); + + if (disable) { + /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE* for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount"); + } else { + /* torture_comment(tctx, "expecting bad pwd count to be 0 for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0"); + } + + tmp = badpwdcount; + + continue; + } + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, passwords[i], + NT_STATUS_WRONG_PASSWORD, interactive)) { + torture_fail(tctx, talloc_asprintf(tctx, "succeeded to authenticate with old password (#%d of #%d in history)", i, password_history_length)); + } + + torture_assert(tctx, + test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), ""); + + /* - network samlogon will fail auth but not increase + * badpwdcount for 3rd last entry + * - interactive samlogon for 3rd and 2nd last entry */ + + if (i == password_history_length - 3 || + (i == password_history_length - 2 && interactive)) { + /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE * by one for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount"); + } else { + /* torture_comment(tctx, "expecting bad pwd count to increase by one for pwd history entry %d\n", i); */ + torture_assert_int_equal(tctx, badpwdcount, tmp + 1, "unexpected badpwdcount"); + } + + tmp = badpwdcount; + } + + return true; +} + +static bool test_Password_badpwdcount_wrap(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials) +{ + union samr_DomainInfo *q_info, s_info; + struct samr_DomInfo1 info1, _info1; + struct samr_DomInfo12 info12, _info12; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *np; + int i; + + struct { + const char *comment; + bool disabled; + bool interactive; + NTSTATUS expected_success_status; + } creds[] = { + { + .comment = "network logon (disabled account)", + .disabled = true, + .interactive = false, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "network logon (enabled account)", + .disabled = false, + .interactive = false, + .expected_success_status= NT_STATUS_OK + }, + { + .comment = "interactive logon (disabled account)", + .disabled = true, + .interactive = true, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "interactive logon (enabled account)", + .disabled = false, + .interactive = true, + .expected_success_status= NT_STATUS_OK + }, + }; + + torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); + + /* backup old policies */ + + torture_assert(tctx, + test_QueryDomainInfo2_level(b, tctx, domain_handle, + DomainPasswordInformation, &q_info), + "failed to query domain info level 1"); + + info1 = q_info->info1; + _info1 = info1; + + torture_assert(tctx, + test_QueryDomainInfo2_level(b, tctx, domain_handle, + DomainLockoutInformation, &q_info), + "failed to query domain info level 12"); + + info12 = q_info->info12; + _info12 = info12; + + /* run tests */ + + for (i=0; i < ARRAY_SIZE(creds); i++) { + + /* skip trust tests for now */ + if (acct_flags & ACB_WSTRUST || + acct_flags & ACB_SVRTRUST || + acct_flags & ACB_DOMTRUST) { + continue; + } + + if (!test_Password_badpwdcount(p, np, tctx, acct_flags, acct_name, + domain_handle, user_handle, password, + machine_credentials, + creds[i].comment, + creds[i].disabled, + creds[i].interactive, + creds[i].expected_success_status, + &_info1, &_info12)) { + torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment); + ret = false; + } else { + torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment); + } + } + + /* restore policies */ + + s_info.info1 = info1; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainPasswordInformation, &s_info), + "failed to set password information"); + + s_info.info12 = info12; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &s_info), + "failed to set lockout information"); + + return ret; +} + +static bool test_QueryUserInfo_lockout(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *acct_name, + uint16_t raw_bad_password_count, + uint16_t effective_bad_password_count, + uint32_t effective_acb_lockout) +{ + struct policy_handle user_handle; + union samr_UserInfo *i; + struct samr_QueryUserInfo r; + + NTSTATUS status = test_OpenUser_byname(b, tctx, domain_handle, acct_name, &user_handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + r.in.user_handle = &user_handle; + r.in.level = 3; + r.out.info = &i; + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + "failed to query userinfo"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query userinfo"); + torture_comment(tctx, " (acct_flags: 0x%08x) (raw_bad_pwd_count: %u)\n", + i->info3.acct_flags, i->info3.bad_password_count); + torture_assert_int_equal(tctx, i->info3.bad_password_count, + raw_bad_password_count, + "raw badpwdcount"); + torture_assert_int_equal(tctx, i->info3.acct_flags & ACB_AUTOLOCK, + effective_acb_lockout, + "effective acb_lockout"); + TALLOC_FREE(i); + + r.in.user_handle = &user_handle; + r.in.level = 5; + r.out.info = &i; + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + "failed to query userinfo"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query userinfo"); + torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n", + i->info5.acct_flags, i->info5.bad_password_count); + torture_assert_int_equal(tctx, i->info5.bad_password_count, + effective_bad_password_count, + "effective badpwdcount"); + torture_assert_int_equal(tctx, i->info5.acct_flags & ACB_AUTOLOCK, + effective_acb_lockout, + "effective acb_lockout"); + TALLOC_FREE(i); + + r.in.user_handle = &user_handle; + r.in.level = 16; + r.out.info = &i; + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + "failed to query userinfo"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query userinfo"); + torture_comment(tctx, " (acct_flags: 0x%08x)\n", + i->info16.acct_flags); + torture_assert_int_equal(tctx, i->info16.acct_flags & ACB_AUTOLOCK, + effective_acb_lockout, + "effective acb_lockout"); + TALLOC_FREE(i); + + r.in.user_handle = &user_handle; + r.in.level = 21; + r.out.info = &i; + torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + "failed to query userinfo"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query userinfo"); + torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n", + i->info21.acct_flags, i->info21.bad_password_count); + torture_assert_int_equal(tctx, i->info21.bad_password_count, + effective_bad_password_count, + "effective badpwdcount"); + torture_assert_int_equal(tctx, i->info21.acct_flags & ACB_AUTOLOCK, + effective_acb_lockout, + "effective acb_lockout"); + TALLOC_FREE(i); + + if (!test_samr_handle_Close(b, tctx, &user_handle)) { + return false; + } + + return true; +} + +static bool test_Password_lockout(struct dcerpc_pipe *p, + struct dcerpc_pipe *np, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials, + const char *comment, + bool disable, + bool interactive, + uint32_t password_history_length, + NTSTATUS expected_success_status, + struct samr_DomInfo1 *info1, + struct samr_DomInfo12 *info12) +{ + union samr_DomainInfo info; + uint64_t lockout_threshold = 1; + uint32_t lockout_seconds = 5; + uint64_t delta_time_factor = 10 * 1000 * 1000; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + lockout_seconds = 60; + } + + torture_comment(tctx, "\nTesting account lockout: %s\n", comment); + + /* set policies */ + + info.info1 = *info1; + + torture_comment(tctx, "setting password history length to %d.\n", password_history_length); + info.info1.password_history_length = password_history_length; + + torture_comment(tctx, "setting min password again.\n"); + info.info1.min_password_age = 0; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainPasswordInformation, &info), + "failed to set password history length"); + + info.info12 = *info12; + info.info12.lockout_threshold = lockout_threshold; + + /* set lockout duration < lockout window: should fail */ + info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); + info.info12.lockout_window = ~((lockout_seconds + 1) * delta_time_factor); + + torture_assert(tctx, + test_SetDomainInfo_ntstatus(b, tctx, domain_handle, + DomainLockoutInformation, &info, + NT_STATUS_INVALID_PARAMETER), + "setting lockout duration < lockout window gave unexpected result"); + + info.info12.lockout_duration = 0; + info.info12.lockout_window = 0; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &info), + "failed to set lockout window and duration to 0"); + + + /* set lockout duration of 5 seconds */ + info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor); + info.info12.lockout_window = ~(lockout_seconds * delta_time_factor); + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &info), + "failed to set lockout window and duration to 5 seconds"); + + /* reset bad pwd count */ + + torture_assert(tctx, + test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), ""); + + + /* enable or disable account */ + + if (disable) { + torture_assert(tctx, + test_SetUserInfo_acct_flags(b, tctx, user_handle, + acct_flags | ACB_DISABLED), + "failed to disable user"); + } else { + torture_assert(tctx, + test_SetUserInfo_acct_flags(b, tctx, user_handle, + acct_flags & ~ACB_DISABLED), + "failed to enable user"); + } + + + /* test logon with right password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, *password, + expected_success_status, interactive)) { + torture_fail(tctx, "failed to auth with latest password"); + } + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 0, 0, 0), + "expected account to not be locked"); + + /* test with wrong password ==> lockout */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, "random_crap", + NT_STATUS_WRONG_PASSWORD, interactive)) { + torture_fail(tctx, "succeeded to authenticate with wrong password"); + } + + /* + * curiously, windows does _not_ return fresh values of + * effective bad_password_count and ACB_AUTOLOCK. + */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to not be locked"); + + /* test with good password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + *password, + NT_STATUS_ACCOUNT_LOCKED_OUT, interactive)) + { + torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT"); + } + + /* bad pwd count should not get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, + NT_STATUS_ACCOUNT_LOCKED_OUT), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count should not get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count should not get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + /* with bad password */ + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, + acct_name, "random_crap2", + NT_STATUS_ACCOUNT_LOCKED_OUT, interactive)) + { + torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT"); + } + + /* bad pwd count should not get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + /* let lockout duration expire ==> unlock */ + + torture_comment(tctx, "let lockout duration expire...\n"); + sleep(lockout_seconds + 1); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 0, 0), + "expected account to not be locked"); + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + *password, + expected_success_status, interactive)) + { + torture_fail(tctx, "failed to authenticate after lockout expired"); + } + + if (NT_STATUS_IS_OK(expected_success_status)) { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 0, 0, 0), + "expected account to not be locked"); + } else { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 0, 0), + "expected account to not be locked"); + } + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), + "got wrong status from ChangePasswordUser2"); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT), + "got wrong status from ChangePasswordUser2"); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT), + "got wrong status from ChangePasswordUser2"); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, ACB_AUTOLOCK), + "expected account to be locked"); + + /* let lockout duration expire ==> unlock */ + + torture_comment(tctx, "let lockout duration expire...\n"); + sleep(lockout_seconds + 1); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 0, 0), + "expected account to not be locked"); + + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + *password, + expected_success_status, interactive)) + { + torture_fail(tctx, "failed to authenticate after lockout expired"); + } + + if (NT_STATUS_IS_OK(expected_success_status)) { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 0, 0, 0), + "expected account to not be locked"); + } else { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 0, 0), + "expected account to not be locked"); + } + + /* Testing ChangePasswordUser behaviour with 3 attempts */ + info.info12.lockout_threshold = 3; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &info), + "failed to set lockout threshold to 3"); + + if (NT_STATUS_IS_OK(expected_success_status)) { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 0, 0, 0), + "expected account to not be locked"); + } else { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 0, 0), + "expected account to not be locked"); + } + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count will get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 1, 1, 0), + "expected account to not be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count will get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 2, 2, 0), + "expected account to not be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count should get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 3, 3, ACB_AUTOLOCK), + "expected account to be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT), + "got wrong status from ChangePasswordUser2"); + + /* bad pwd count should not get updated */ + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 3, 3, ACB_AUTOLOCK), + "expected account to be locked"); + + /* let lockout duration expire ==> unlock */ + + torture_comment(tctx, "let lockout duration expire...\n"); + sleep(lockout_seconds + 1); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 3, 0, 0), + "expected account to not be locked"); + + torture_assert(tctx, + test_ChangePasswordUser2(p, tctx, acct_name, password, NULL, false), + "got wrong status from ChangePasswordUser2"); + + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 3, 0, 0), + "expected account to not be locked"); + + /* Used to reset the badPwdCount for the other tests */ + if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name, + *password, + expected_success_status, interactive)) + { + torture_fail(tctx, "failed to authenticate after lockout expired"); + } + + if (NT_STATUS_IS_OK(expected_success_status)) { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 0, 0, 0), + "expected account to not be locked"); + } else { + torture_assert(tctx, + test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name, + 3, 0, 0), + "expected account to not be locked"); + } + + return true; +} + +static bool test_Password_lockout_wrap(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t acct_flags, + const char *acct_name, + struct policy_handle *domain_handle, + struct policy_handle *user_handle, + char **password, + struct cli_credentials *machine_credentials) +{ + union samr_DomainInfo *q_info, s_info; + struct samr_DomInfo1 info1, _info1; + struct samr_DomInfo12 info12, _info12; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *np; + int i; + + struct { + const char *comment; + bool disabled; + bool interactive; + uint32_t password_history_length; + NTSTATUS expected_success_status; + } creds[] = { + { + .comment = "network logon (disabled account)", + .disabled = true, + .interactive = false, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "network logon (enabled account)", + .disabled = false, + .interactive = false, + .expected_success_status= NT_STATUS_OK + }, + { + .comment = "network logon (enabled account, history len = 1)", + .disabled = false, + .interactive = false, + .expected_success_status= NT_STATUS_OK, + .password_history_length = 1 + }, + { + .comment = "interactive logon (disabled account)", + .disabled = true, + .interactive = true, + .expected_success_status= NT_STATUS_ACCOUNT_DISABLED + }, + { + .comment = "interactive logon (enabled account)", + .disabled = false, + .interactive = true, + .expected_success_status= NT_STATUS_OK + }, + { + .comment = "interactive logon (enabled account, history len = 1)", + .disabled = false, + .interactive = true, + .expected_success_status= NT_STATUS_OK, + .password_history_length = 1 + }, + }; + + torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), ""); + + /* backup old policies */ + + torture_assert(tctx, + test_QueryDomainInfo2_level(b, tctx, domain_handle, + DomainPasswordInformation, &q_info), + "failed to query domain info level 1"); + + info1 = q_info->info1; + _info1 = info1; + + torture_assert(tctx, + test_QueryDomainInfo2_level(b, tctx, domain_handle, + DomainLockoutInformation, &q_info), + "failed to query domain info level 12"); + + info12 = q_info->info12; + _info12 = info12; + + /* run tests */ + + for (i=0; i < ARRAY_SIZE(creds); i++) { + bool test_passed; + /* skip trust tests for now */ + if (acct_flags & ACB_WSTRUST || + acct_flags & ACB_SVRTRUST || + acct_flags & ACB_DOMTRUST) { + continue; + } + + test_passed = test_Password_lockout(p, np, tctx, acct_flags, acct_name, + domain_handle, user_handle, password, + machine_credentials, + creds[i].comment, + creds[i].disabled, + creds[i].interactive, + creds[i].password_history_length, + creds[i].expected_success_status, + &_info1, &_info12); + ret &= test_passed; + if (!test_passed) { + torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment); + break; + } else { + torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment); + } + } + + /* restore policies */ + + s_info.info1 = info1; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainPasswordInformation, &s_info), + "failed to set password information"); + + s_info.info12 = info12; + + torture_assert(tctx, + test_SetDomainInfo(b, tctx, domain_handle, + DomainLockoutInformation, &s_info), + "failed to set lockout information"); + + return ret; +} + +static bool test_DeleteUser_with_privs(struct dcerpc_pipe *p, + struct dcerpc_pipe *lp, + struct torture_context *tctx, + struct policy_handle *domain_handle, + struct policy_handle *lsa_handle, + struct policy_handle *user_handle, + const struct dom_sid *domain_sid, + uint32_t rid, + struct cli_credentials *machine_credentials) +{ + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_binding_handle *lb = lp->binding_handle; + + struct policy_handle lsa_acct_handle; + struct dom_sid *user_sid; + + user_sid = dom_sid_add_rid(tctx, domain_sid, rid); + + { + struct lsa_EnumAccountRights r; + struct lsa_RightSet rights; + + torture_comment(tctx, "Testing LSA EnumAccountRights\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.out.rights = &rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), + "lsa_EnumAccountRights failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, + "Expected enum rights for account to fail"); + } + + { + struct lsa_RightSet rights; + struct lsa_StringLarge names[2]; + struct lsa_AddAccountRights r; + + torture_comment(tctx, "Testing LSA AddAccountRights\n"); + + init_lsa_StringLarge(&names[0], "SeMachineAccountPrivilege"); + init_lsa_StringLarge(&names[1], NULL); + + rights.count = 1; + rights.names = names; + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.in.rights = &rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r), + "lsa_AddAccountRights failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to add privileges"); + } + + { + struct lsa_RightSet rights; + struct lsa_StringLarge names[2]; + struct lsa_AddAccountRights r; + + torture_comment(tctx, "Testing LSA AddAccountRights 1\n"); + + init_lsa_StringLarge(&names[0], "SeInteractiveLogonRight"); + init_lsa_StringLarge(&names[1], NULL); + + rights.count = 1; + rights.names = names; + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.in.rights = &rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r), + "lsa_AddAccountRights 1 failed"); + + if (torture_setting_bool(tctx, "nt4_dc", false)) { + /* + * The NT4 DC doesn't implement Rights. + */ + torture_assert_ntstatus_equal(tctx, r.out.result, + NT_STATUS_NO_SUCH_PRIVILEGE, + "Add rights failed with incorrect error"); + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to add rights"); + + } + } + + + { + struct lsa_EnumAccounts r; + uint32_t resume_handle = 0; + struct lsa_SidArray lsa_sid_array; + int i; + bool found_sid = false; + + torture_comment(tctx, "Testing LSA EnumAccounts\n"); + + r.in.handle = lsa_handle; + r.in.num_entries = 0x1000; + r.in.resume_handle = &resume_handle; + r.out.sids = &lsa_sid_array; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), + "lsa_EnumAccounts failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to enum accounts"); + + for (i=0; i < lsa_sid_array.num_sids; i++) { + if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { + found_sid = true; + } + } + + torture_assert(tctx, found_sid, + "failed to list privileged account"); + } + + { + struct lsa_EnumAccountRights r; + struct lsa_RightSet user_rights; + uint32_t expected_count = 2; + + if (torture_setting_bool(tctx, "nt4_dc", false)) { + /* + * NT4 DC doesn't store rights. + */ + expected_count = 1; + } + + torture_comment(tctx, "Testing LSA EnumAccountRights\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.out.rights = &user_rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), + "lsa_EnumAccountRights failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to enum rights for account"); + + if (user_rights.count < expected_count) { + torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights"); + return false; + } + } + + { + struct lsa_OpenAccount r; + + torture_comment(tctx, "Testing LSA OpenAccount\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &lsa_acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r), + "lsa_OpenAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to open lsa account"); + } + + { + struct lsa_GetSystemAccessAccount r; + uint32_t access_mask; + + torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n"); + + r.in.handle = &lsa_acct_handle; + r.out.access_mask = &access_mask; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r), + "lsa_GetSystemAccessAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to get lsa system access account"); + } + + { + struct lsa_Close r; + + torture_comment(tctx, "Testing LSA Close\n"); + + r.in.handle = &lsa_acct_handle; + r.out.handle = &lsa_acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(lb, tctx, &r), + "lsa_Close failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to close lsa"); + } + + { + struct samr_DeleteUser r; + + torture_comment(tctx, "Testing SAMR DeleteUser\n"); + + r.in.user_handle = user_handle; + r.out.user_handle = user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &r), + "DeleteUser failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "DeleteUser failed"); + } + + { + struct lsa_EnumAccounts r; + uint32_t resume_handle = 0; + struct lsa_SidArray lsa_sid_array; + int i; + bool found_sid = false; + + torture_comment(tctx, "Testing LSA EnumAccounts\n"); + + r.in.handle = lsa_handle; + r.in.num_entries = 0x1000; + r.in.resume_handle = &resume_handle; + r.out.sids = &lsa_sid_array; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), + "lsa_EnumAccounts failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to enum accounts"); + + for (i=0; i < lsa_sid_array.num_sids; i++) { + if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { + found_sid = true; + } + } + + torture_assert(tctx, found_sid, + "failed to list privileged account"); + } + + { + struct lsa_EnumAccountRights r; + struct lsa_RightSet user_rights; + + torture_comment(tctx, "Testing LSA EnumAccountRights\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.out.rights = &user_rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), + "lsa_EnumAccountRights failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to enum rights for account"); + + if (user_rights.count < 1) { + torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights"); + return false; + } + } + + { + struct lsa_OpenAccount r; + + torture_comment(tctx, "Testing LSA OpenAccount\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.acct_handle = &lsa_acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r), + "lsa_OpenAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to open lsa account"); + } + + { + struct lsa_GetSystemAccessAccount r; + uint32_t access_mask; + + torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n"); + + r.in.handle = &lsa_acct_handle; + r.out.access_mask = &access_mask; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r), + "lsa_GetSystemAccessAccount failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to get lsa system access account"); + } + + { + struct lsa_DeleteObject r; + + torture_comment(tctx, "Testing LSA DeleteObject\n"); + + r.in.handle = &lsa_acct_handle; + r.out.handle = &lsa_acct_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(lb, tctx, &r), + "lsa_DeleteObject failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to delete object"); + } + + { + struct lsa_EnumAccounts r; + uint32_t resume_handle = 0; + struct lsa_SidArray lsa_sid_array; + int i; + bool found_sid = false; + + torture_comment(tctx, "Testing LSA EnumAccounts\n"); + + r.in.handle = lsa_handle; + r.in.num_entries = 0x1000; + r.in.resume_handle = &resume_handle; + r.out.sids = &lsa_sid_array; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r), + "lsa_EnumAccounts failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "Failed to enum accounts"); + + for (i=0; i < lsa_sid_array.num_sids; i++) { + if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) { + found_sid = true; + } + } + + torture_assert(tctx, !found_sid, + "should not have listed privileged account"); + } + + { + struct lsa_EnumAccountRights r; + struct lsa_RightSet user_rights; + + torture_comment(tctx, "Testing LSA EnumAccountRights\n"); + + r.in.handle = lsa_handle; + r.in.sid = user_sid; + r.out.rights = &user_rights; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r), + "lsa_EnumAccountRights failed"); + torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, + "Failed to enum rights for account"); + } + + return ret; +} + +static bool test_user_ops(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *user_handle, + struct policy_handle *domain_handle, + const struct dom_sid *domain_sid, + uint32_t base_acct_flags, + const char *base_acct_name, enum torture_samr_choice which_ops, + struct cli_credentials *machine_credentials) +{ + char *password = NULL; + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + + bool ret = true; + int i; + uint32_t rid; + const uint32_t password_fields[] = { + SAMR_FIELD_NT_PASSWORD_PRESENT, + SAMR_FIELD_LM_PASSWORD_PRESENT, + SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT, + 0 + }; + + status = test_LookupName(b, tctx, domain_handle, base_acct_name, &rid); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + } + + switch (which_ops) { + case TORTURE_SAMR_USER_ATTRIBUTES: + if (!test_QuerySecurity(b, tctx, user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo(b, tctx, user_handle)) { + ret = false; + } + + if (!test_QueryUserInfo2(b, tctx, user_handle)) { + ret = false; + } + + if (!test_SetUserInfo(b, tctx, user_handle, base_acct_flags, + base_acct_name)) { + ret = false; + } + + if (!test_GetUserPwInfo(b, tctx, user_handle)) { + ret = false; + } + + if (!test_TestPrivateFunctionsUser(b, tctx, user_handle)) { + ret = false; + } + + if (!test_SetUserPass(p, tctx, user_handle, &password)) { + ret = false; + } + break; + case TORTURE_SAMR_PASSWORDS: + if (base_acct_flags & (ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST)) { + char simple_pass[9]; + char *v = generate_random_str(tctx, 1); + + ZERO_STRUCT(simple_pass); + memset(simple_pass, *v, sizeof(simple_pass) - 1); + + torture_comment(tctx, "Testing machine account password policy rules\n"); + + /* Workstation trust accounts don't seem to need to honour password quality policy */ + if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { + ret = false; + } + + if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, simple_pass, false)) { + ret = false; + } + + /* reset again, to allow another 'user' password change */ + if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) { + ret = false; + } + + /* Try a 'short' password */ + if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, samr_rand_pass(tctx, 4), false)) { + ret = false; + } + + /* Try a completely random password */ + if (!test_ChangePasswordRandomBytes(p, tctx, base_acct_name, user_handle, &password)) { + ret = false; + } + } + + for (i = 0; password_fields[i]; i++) { + if (!test_SetUserPass_23(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + for (i = 0; password_fields[i]; i++) { + if (!test_SetUserPass_25(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + if (!test_SetUserPass_31(p, tctx, user_handle, false, &password)) { + ret = false; + } + + for (i = 0; password_fields[i]; i++) { + if (!test_SetUserPass_32(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) { + ret = false; + } + + if (!test_SetUserPassEx(p, tctx, user_handle, false, &password)) { + ret = false; + } + + if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) { + ret = false; + } + + if (!test_SetUserPass_18(p, tctx, user_handle, &password)) { + ret = false; + } + + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + + for (i = 0; password_fields[i]; i++) { + + if (password_fields[i] == SAMR_FIELD_LM_PASSWORD_PRESENT) { + /* we need to skip as that would break + * the ChangePasswordUser3 verify */ + continue; + } + + if (!test_SetUserPass_21(p, tctx, user_handle, password_fields[i], &password)) { + ret = false; + } + + /* check it was set right */ + if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) { + ret = false; + } + } + + q.in.user_handle = user_handle; + q.in.level = 5; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), + "QueryUserInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(q.out.result)); + ret = false; + } else { + uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED); + if ((info->info5.acct_flags) != expected_flags) { + /* FIXME: GD */ + if (!torture_setting_bool(tctx, "samba3", false)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + info->info5.acct_flags, + expected_flags); + ret = false; + } + } + if (info->info5.rid != rid) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned %u when we expected rid of %u\n", + info->info5.rid, rid); + + } + } + + break; + + case TORTURE_SAMR_PASSWORDS_PWDLASTSET: + + /* test last password change timestamp behaviour */ + torture_assert(tctx, test_SetPassword_pwdlastset(p, tctx, base_acct_flags, + base_acct_name, + user_handle, &password, + machine_credentials), + "pwdLastSet test failed\n"); + break; + + case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: + + /* test bad pwd count change behaviour */ + torture_assert(tctx, test_Password_badpwdcount_wrap(p, tctx, base_acct_flags, + base_acct_name, + domain_handle, + user_handle, &password, + machine_credentials), + "badPwdCount test failed\n"); + break; + + case TORTURE_SAMR_PASSWORDS_LOCKOUT: + + torture_assert(tctx, test_Password_lockout_wrap(p, tctx, base_acct_flags, + base_acct_name, + domain_handle, + user_handle, &password, + machine_credentials), + "Lockout test failed"); + break; + + + case TORTURE_SAMR_USER_PRIVILEGES: { + + struct dcerpc_pipe *lp; + struct policy_handle *lsa_handle; + struct dcerpc_binding_handle *lb; + + status = torture_rpc_connection(tctx, &lp, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(tctx, status, "Failed to open LSA pipe"); + lb = lp->binding_handle; + + if (!test_lsa_OpenPolicy2(lb, tctx, &lsa_handle)) { + ret = false; + } + + if (!test_DeleteUser_with_privs(p, lp, tctx, + domain_handle, lsa_handle, user_handle, + domain_sid, rid, + machine_credentials)) { + ret = false; + } + + if (!test_lsa_Close(lb, tctx, lsa_handle)) { + ret = false; + } + + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "privileged user delete test failed\n"); + } + + break; + } + case TORTURE_SAMR_OTHER: + case TORTURE_SAMR_MANY_ACCOUNTS: + case TORTURE_SAMR_MANY_GROUPS: + case TORTURE_SAMR_MANY_ALIASES: + /* We just need the account to exist */ + break; + } + return ret; +} + +static bool test_alias_ops(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid) +{ + bool ret = true; + + if (!torture_setting_bool(tctx, "samba3", false)) { + if (!test_QuerySecurity(b, tctx, alias_handle)) { + ret = false; + } + } + + if (!test_QueryAliasInfo(b, tctx, alias_handle)) { + ret = false; + } + + if (!test_SetAliasInfo(b, tctx, alias_handle)) { + ret = false; + } + + if (!test_AddMemberToAlias(b, tctx, alias_handle, domain_sid)) { + ret = false; + } + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping MultipleMembers Alias tests against Samba\n"); + return ret; + } + + if (!test_AddMultipleMembersToAlias(b, tctx, alias_handle)) { + ret = false; + } + + return ret; +} + + +static bool test_DeleteUser(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *user_handle) +{ + struct samr_DeleteUser d; + torture_comment(tctx, "Testing DeleteUser\n"); + + d.in.user_handle = user_handle; + d.out.user_handle = user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d), + "DeleteUser failed"); + torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteUser"); + + return true; +} + +bool test_DeleteUser_byname(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + + status = test_LookupName(b, tctx, handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + status = test_OpenUser_byname(b, tctx, handle, name, &user_handle); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d), + "DeleteUser failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + status = d.out.result; + goto failed; + } + + return true; + +failed: + torture_result(tctx, TORTURE_FAIL, "DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + + +static bool test_DeleteGroup_byname(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_OpenGroup r; + struct samr_DeleteDomainGroup d; + struct policy_handle group_handle; + uint32_t rid; + + status = test_LookupName(b, tctx, handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.group_handle = &group_handle; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r), + "OpenGroup failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + status = r.out.result; + goto failed; + } + + d.in.group_handle = &group_handle; + d.out.group_handle = &group_handle; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomainGroup_r(b, tctx, &d), + "DeleteDomainGroup failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + status = d.out.result; + goto failed; + } + + return true; + +failed: + torture_result(tctx, TORTURE_FAIL, "DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + + +static bool test_DeleteAlias_byname(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *name) +{ + NTSTATUS status; + struct samr_OpenAlias r; + struct samr_DeleteDomAlias d; + struct policy_handle alias_handle; + uint32_t rid; + + torture_comment(tctx, "Testing DeleteAlias_byname\n"); + + status = test_LookupName(b, tctx, domain_handle, name, &rid); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.alias_handle = &alias_handle; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenAlias_r(b, tctx, &r), + "OpenAlias failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + status = r.out.result; + goto failed; + } + + d.in.alias_handle = &alias_handle; + d.out.alias_handle = &alias_handle; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d), + "DeleteDomAlias failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + status = d.out.result; + goto failed; + } + + return true; + +failed: + torture_result(tctx, TORTURE_FAIL, "DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status)); + return false; +} + +static bool test_DeleteAlias(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *alias_handle) +{ + struct samr_DeleteDomAlias d; + bool ret = true; + + torture_comment(tctx, "Testing DeleteAlias\n"); + + d.in.alias_handle = alias_handle; + d.out.alias_handle = alias_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d), + "DeleteDomAlias failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + torture_result(tctx, TORTURE_FAIL, "DeleteAlias failed - %s\n", nt_errstr(d.out.result)); + ret = false; + } + + return ret; +} + +static bool test_CreateAlias(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *alias_name, + struct policy_handle *alias_handle, + const struct dom_sid *domain_sid, + bool test_alias) +{ + struct samr_CreateDomAlias r; + struct lsa_String name; + uint32_t rid; + bool ret = true; + + init_lsa_String(&name, alias_name); + r.in.domain_handle = domain_handle; + r.in.alias_name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.alias_handle = alias_handle; + r.out.rid = &rid; + + torture_comment(tctx, "Testing CreateAlias (%s)\n", r.in.alias_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r), + "CreateDomAlias failed"); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.alias_name->string); + return true; + } else { + torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string, + nt_errstr(r.out.result)); + return false; + } + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ALIAS_EXISTS)) { + if (!test_DeleteAlias_byname(b, tctx, domain_handle, r.in.alias_name->string)) { + return false; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r), + "CreateDomAlias failed"); + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "CreateAlias failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + if (!test_alias) { + return ret; + } + + if (!test_alias_ops(b, tctx, alias_handle, domain_sid)) { + ret = false; + } + + return ret; +} + +static bool test_ChangePassword(struct dcerpc_pipe *p, + struct torture_context *tctx, + const char *acct_name, + struct policy_handle *domain_handle, char **password) +{ + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!*password) { + return false; + } + + if (!test_ChangePasswordUser(b, tctx, acct_name, domain_handle, password)) { + ret = false; + } + + if (!test_ChangePasswordUser2(p, tctx, acct_name, password, 0, true)) { + ret = false; + } + + if (!test_OemChangePasswordUser2(p, tctx, acct_name, domain_handle, password)) { + ret = false; + } + + /* test what happens when setting the old password again */ + if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, *password, 0, true)) { + ret = false; + } + + { + char simple_pass[9]; + char *v = generate_random_str(tctx, 1); + + ZERO_STRUCT(simple_pass); + memset(simple_pass, *v, sizeof(simple_pass) - 1); + + /* test what happens when picking a simple password */ + if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, simple_pass, 0, true)) { + ret = false; + } + } + + /* set samr_SetDomainInfo level 1 with min_length 5 */ + { + struct samr_QueryDomainInfo r; + union samr_DomainInfo *info = NULL; + struct samr_SetDomainInfo s; + uint16_t len_old, len; + uint32_t pwd_prop_old; + int64_t min_pwd_age_old; + + len = 5; + + r.in.domain_handle = domain_handle; + r.in.level = 1; + r.out.info = &info; + + torture_comment(tctx, "Testing samr_QueryDomainInfo level 1\n"); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), + "QueryDomainInfo failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + return false; + } + + s.in.domain_handle = domain_handle; + s.in.level = 1; + s.in.info = info; + + /* remember the old min length, so we can reset it */ + len_old = s.in.info->info1.min_password_length; + s.in.info->info1.min_password_length = len; + pwd_prop_old = s.in.info->info1.password_properties; + /* turn off password complexity checks for this test */ + s.in.info->info1.password_properties &= ~DOMAIN_PASSWORD_COMPLEX; + + min_pwd_age_old = s.in.info->info1.min_password_age; + s.in.info->info1.min_password_age = 0; + + torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n"); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), + "SetDomainInfo failed"); + if (!NT_STATUS_IS_OK(s.out.result)) { + return false; + } + + torture_comment(tctx, "calling test_ChangePasswordUser3 with too short password\n"); + + if (!test_ChangePasswordUser3(p, tctx, acct_name, len - 1, password, NULL, 0, true)) { + ret = false; + } + + s.in.info->info1.min_password_length = len_old; + s.in.info->info1.password_properties = pwd_prop_old; + s.in.info->info1.min_password_age = min_pwd_age_old; + + torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n"); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), + "SetDomainInfo failed"); + if (!NT_STATUS_IS_OK(s.out.result)) { + return false; + } + + } + + { + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + struct samr_LookupNames n; + struct policy_handle user_handle; + struct samr_Ids rids, types; + + n.in.domain_handle = domain_handle; + n.in.num_names = 1; + n.in.names = talloc_array(tctx, struct lsa_String, 1); + n.in.names[0].string = acct_name; + n.out.rids = &rids; + n.out.types = &types; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n), + "LookupNames failed"); + if (!NT_STATUS_IS_OK(n.out.result)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result)); + return false; + } + + r.in.domain_handle = domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = n.out.rids->ids[0]; + r.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), + "OpenUser failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", n.out.rids->ids[0], nt_errstr(r.out.result)); + return false; + } + + q.in.user_handle = &user_handle; + q.in.level = 5; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), + "QueryUserInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo failed - %s\n", nt_errstr(q.out.result)); + return false; + } + + torture_comment(tctx, "calling test_ChangePasswordUser3 with too early password change\n"); + + if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, + info->info5.last_password_change, true)) { + ret = false; + } + } + + /* we change passwords twice - this has the effect of verifying + they were changed correctly for the final call */ + if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, 0, true)) { + ret = false; + } + + if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, 0, true)) { + ret = false; + } + + if (!test_ChangePasswordUser4(p, tctx, acct_name, 0, password, NULL)) { + ret = false; + } + + return ret; +} + +static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *user_name, + struct policy_handle *user_handle_out, + struct dom_sid *domain_sid, + enum torture_samr_choice which_ops, + struct cli_credentials *machine_credentials, + bool test_user) +{ + + TALLOC_CTX *user_ctx; + + struct samr_CreateUser r; + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + struct samr_DeleteUser d; + uint32_t rid; + + /* This call creates a 'normal' account - check that it really does */ + const uint32_t acct_flags = ACB_NORMAL; + struct lsa_String name; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct policy_handle user_handle; + user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); + init_lsa_String(&name, user_name); + + r.in.domain_handle = domain_handle; + r.in.account_name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + r.out.rid = &rid; + + torture_comment(tctx, "Testing CreateUser(%s)\n", r.in.account_name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r), + "CreateUser failed"); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string); + return true; + } else { + torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, + nt_errstr(r.out.result)); + return false; + } + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) { + talloc_free(user_ctx); + return false; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r), + "CreateUser failed"); + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + talloc_free(user_ctx); + torture_result(tctx, TORTURE_FAIL, "CreateUser failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + if (!test_user) { + if (user_handle_out) { + *user_handle_out = user_handle; + } + return ret; + } + + { + q.in.user_handle = &user_handle; + q.in.level = 16; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q), + "QueryUserInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(q.out.result)); + ret = false; + } else { + if ((info->info16.acct_flags & acct_flags) != acct_flags) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + info->info16.acct_flags, + acct_flags); + ret = false; + } + } + + if (!test_user_ops(p, tctx, &user_handle, domain_handle, + domain_sid, acct_flags, name.string, which_ops, + machine_credentials)) { + ret = false; + } + + if (user_handle_out) { + *user_handle_out = user_handle; + } else { + torture_comment(tctx, "Testing DeleteUser (createuser test)\n"); + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d), + "DeleteUser failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result)); + ret = false; + } + } + + } + + talloc_free(user_ctx); + + return ret; +} + + +static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + enum torture_samr_choice which_ops, + struct cli_credentials *machine_credentials) +{ + struct samr_CreateUser2 r; + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + struct lsa_String name; + bool ret = true; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct { + uint32_t acct_flags; + const char *account_name; + NTSTATUS nt_status; + } account_types[] = { + { ACB_NORMAL, TEST_ACCOUNT_NAME, NT_STATUS_OK }, + { ACB_NORMAL | ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_NORMAL | ACB_PWNOEXP, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_WSTRUST, TEST_MACHINENAME, NT_STATUS_OK }, + { ACB_WSTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_WSTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_SVRTRUST, TEST_MACHINENAME, NT_STATUS_OK }, + { ACB_SVRTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_SVRTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_ACCESS_DENIED }, + { ACB_DOMTRUST | ACB_DISABLED, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DOMTRUST | ACB_PWNOEXP, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER }, + { 0, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER }, + { 0, NULL, NT_STATUS_INVALID_PARAMETER } + }; + + for (i = 0; account_types[i].account_name; i++) { + TALLOC_CTX *user_ctx; + uint32_t acct_flags = account_types[i].acct_flags; + uint32_t access_granted; + user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); + init_lsa_String(&name, account_types[i].account_name); + + r.in.domain_handle = domain_handle; + r.in.account_name = &name; + r.in.acct_flags = acct_flags; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &user_handle; + r.out.access_granted = &access_granted; + r.out.rid = &rid; + + torture_comment(tctx, "Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r), + "CreateUser2 failed"); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) { + torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string); + continue; + } else { + torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string, + nt_errstr(r.out.result)); + ret = false; + continue; + } + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) { + talloc_free(user_ctx); + ret = false; + continue; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r), + "CreateUser2 failed"); + + } + if (!NT_STATUS_EQUAL(r.out.result, account_types[i].nt_status)) { + torture_result(tctx, TORTURE_FAIL, "CreateUser2 failed gave incorrect error return - %s (should be %s)\n", + nt_errstr(r.out.result), nt_errstr(account_types[i].nt_status)); + ret = false; + } + + if (NT_STATUS_IS_OK(r.out.result)) { + q.in.user_handle = &user_handle; + q.in.level = 5; + q.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q), + "QueryUserInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n", + q.in.level, nt_errstr(q.out.result)); + ret = false; + } else { + uint32_t expected_flags = (acct_flags | ACB_PWNOTREQ | ACB_DISABLED); + if (acct_flags == ACB_NORMAL) { + expected_flags |= ACB_PW_EXPIRED; + } + if ((info->info5.acct_flags) != expected_flags) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", + info->info5.acct_flags, + expected_flags); + ret = false; + } + switch (acct_flags) { + case ACB_SVRTRUST: + if (info->info5.primary_gid != DOMAIN_RID_DCS) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: DC should have had Primary Group %d, got %d\n", + DOMAIN_RID_DCS, info->info5.primary_gid); + ret = false; + } + break; + case ACB_WSTRUST: + if (info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n", + DOMAIN_RID_DOMAIN_MEMBERS, info->info5.primary_gid); + ret = false; + } + break; + case ACB_NORMAL: + if (info->info5.primary_gid != DOMAIN_RID_USERS) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Users should have had Primary Group %d, got %d\n", + DOMAIN_RID_USERS, info->info5.primary_gid); + ret = false; + } + break; + } + } + + if (!test_user_ops(p, tctx, &user_handle, domain_handle, + domain_sid, acct_flags, name.string, which_ops, + machine_credentials)) { + ret = false; + } + + if (!ndr_policy_handle_empty(&user_handle)) { + torture_comment(tctx, "Testing DeleteUser (createuser2 test)\n"); + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d), + "DeleteUser failed"); + if (!NT_STATUS_IS_OK(d.out.result)) { + torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result)); + ret = false; + } + } + } + talloc_free(user_ctx); + } + + return ret; +} + +static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryAliasInfo r; + union samr_AliasInfo *info; + uint16_t levels[] = {1, 2, 3}; + int i; + bool ret = true; + + for (i=0;istring, "NewName"); + } +#endif + + if (s.in.level == 4) { + init_lsa_String(&s.in.info->description, "test description"); + } + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetGroupInfo_r(b, tctx, &s), + "SetGroupInfo failed"); + if (set_ok[i]) { + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u failed - %s\n", + r.in.level, nt_errstr(s.out.result)); + ret = false; + continue; + } + } else { + if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", + r.in.level, nt_errstr(s.out.result)); + ret = false; + continue; + } + } + } + + return ret; +} + +static bool test_QueryUserInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryUserInfo r; + union samr_UserInfo *info; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 16, 17, 20, 21}; + int i; + bool ret = true; + + for (i=0;iinfo16.acct_flags) == 0) { + torture_result(tctx, TORTURE_FAIL, "Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n", + acct_flag_mask, info->info16.acct_flags, rid); + ret = false; + } + } + + if (!test_samr_handle_Close(b, tctx, &user_handle)) { + ret = false; + } + + return ret; +} + +static bool test_EnumDomainUsers_all(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_EnumDomainUsers r; + uint32_t mask, resume_handle=0; + int i, mask_idx; + bool ret = true; + struct samr_LookupNames n; + struct samr_LookupRids lr ; + struct lsa_Strings names; + struct samr_Ids rids, types; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + + uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST, + ACB_DISABLED, ACB_NORMAL | ACB_DISABLED, + ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST, + ACB_PWNOEXP, 0}; + + torture_comment(tctx, "Testing EnumDomainUsers\n"); + + for (mask_idx=0;mask_idxcount == 0) { + continue; + } + + for (i=0;icount;i++) { + if (mask) { + if (!check_mask(b, tctx, handle, sam->entries[i].idx, mask)) { + ret = false; + } + } else if (!test_OpenUser(b, tctx, handle, sam->entries[i].idx)) { + ret = false; + } + } + } + + torture_comment(tctx, "Testing LookupNames\n"); + n.in.domain_handle = handle; + n.in.num_names = sam->count; + n.in.names = talloc_array(tctx, struct lsa_String, sam->count); + n.out.rids = &rids; + n.out.types = &types; + for (i=0;icount;i++) { + n.in.names[i].string = sam->entries[i].name.string; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n), + "LookupNames failed"); + if (!NT_STATUS_IS_OK(n.out.result)) { + torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result)); + ret = false; + } + + + torture_comment(tctx, "Testing LookupRids\n"); + lr.in.domain_handle = handle; + lr.in.num_rids = sam->count; + lr.in.rids = talloc_array(tctx, uint32_t, sam->count); + lr.out.names = &names; + lr.out.types = &types; + for (i=0;icount;i++) { + lr.in.rids[i] = sam->entries[i].idx; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &lr), + "LookupRids failed"); + torture_assert_ntstatus_ok(tctx, lr.out.result, "LookupRids"); + + return ret; +} + +/* + try blasting the server with a bunch of sync requests +*/ +static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_EnumDomainUsers r; + uint32_t resume_handle=0; + int i; +#define ASYNC_COUNT 100 + struct tevent_req *req[ASYNC_COUNT]; + + if (!torture_setting_bool(tctx, "dangerous", false)) { + torture_skip(tctx, "samr async test disabled - enable dangerous tests to use\n"); + } + + torture_comment(tctx, "Testing EnumDomainUsers_async\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.acct_flags = 0; + r.in.max_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + + for (i=0;iev, p->binding_handle, &r); + } + + for (i=0;iev); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r_recv(req[i], tctx), + talloc_asprintf(tctx, "EnumDomainUsers[%d] failed - %s\n", + i, nt_errstr(r.out.result))); + } + + torture_comment(tctx, "%d async requests OK\n", i); + + return true; +} + +static bool test_EnumDomainGroups_all(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_EnumDomainGroups r; + uint32_t resume_handle=0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + int i; + bool ret = true; + bool universal_group_found = false; + + torture_comment(tctx, "Testing EnumDomainGroups\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + r.out.num_entries = &num_entries; + r.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), + "EnumDomainGroups failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "EnumDomainGroups failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + if (!sam) { + return false; + } + + for (i=0;icount;i++) { + if (!test_OpenGroup(b, tctx, handle, sam->entries[i].idx)) { + ret = false; + } + if ((ret == true) && (strcasecmp(sam->entries[i].name.string, + "Enterprise Admins") == 0)) { + universal_group_found = true; + } + } + + /* when we are running this on s4 we should get back at least the + * "Enterprise Admins" universal group. If we don't get a group entry + * at all we probably are performing the test on the builtin domain. + * So ignore this case. */ + if (torture_setting_bool(tctx, "samba4", false)) { + if ((sam->count > 0) && (!universal_group_found)) { + ret = false; + } + } + + return ret; +} + +static bool test_EnumDomainAliases_all(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_EnumDomainAliases r; + uint32_t resume_handle=0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + int i; + bool ret = true; + + torture_comment(tctx, "Testing EnumDomainAliases\n"); + + r.in.domain_handle = handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = (uint32_t)-1; + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), + "EnumDomainAliases failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "EnumDomainAliases failed - %s\n", nt_errstr(r.out.result)); + return false; + } + + if (!sam) { + return false; + } + + for (i=0;icount;i++) { + if (!test_OpenAlias(b, tctx, handle, sam->entries[i].idx)) { + ret = false; + } + } + + return ret; +} + +static bool test_GetDisplayEnumerationIndex(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_GetDisplayEnumerationIndex r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; + struct lsa_String name; + uint32_t idx = 0; + int i; + + for (i=0;iin.domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + for (i = 0; ; i++) { + switch (querydisplayinfo->in.level) { + case 1: + if (i >= querydisplayinfo->out.info->info1.count) { + return ret; + } + r.in.rid = querydisplayinfo->out.info->info1.entries[i].rid; + break; + case 2: + if (i >= querydisplayinfo->out.info->info2.count) { + return ret; + } + r.in.rid = querydisplayinfo->out.info->info2.entries[i].rid; + break; + case 3: + /* Groups */ + case 4: + case 5: + /* Not interested in validating just the account name */ + return true; + } + + r.out.user_handle = &user_handle; + + switch (querydisplayinfo->in.level) { + case 1: + case 2: + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), + "OpenUser failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result)); + return false; + } + } + + q.in.user_handle = &user_handle; + q.in.level = 21; + q.out.info = &info; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q), + "QueryUserInfo failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result)); + return false; + } + + switch (querydisplayinfo->in.level) { + case 1: + if (seen_testuser && strcmp(info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) { + *seen_testuser = true; + } + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].full_name, + info->info21.full_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].account_name, + info->info21.account_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].description, + info->info21.description, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].rid, + info->info21.rid, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].acct_flags, + info->info21.acct_flags, info->info21.account_name); + + break; + case 2: + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].account_name, + info->info21.account_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].description, + info->info21.description, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].rid, + info->info21.rid, info->info21.account_name); + INT_EQUAL_QUERY((querydisplayinfo->out.info->info2.entries[i].acct_flags & ~ACB_NORMAL), + info->info21.acct_flags, info->info21.account_name); + + if (!(querydisplayinfo->out.info->info2.entries[i].acct_flags & ACB_NORMAL)) { + torture_result(tctx, TORTURE_FAIL, "Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n", + info->info21.account_name.string); + } + + if (!(info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) { + torture_result(tctx, TORTURE_FAIL, "Found non-trust account %s in trust account listing: 0x%x 0x%x\n", + info->info21.account_name.string, + querydisplayinfo->out.info->info2.entries[i].acct_flags, + info->info21.acct_flags); + return false; + } + + break; + } + + if (!test_samr_handle_Close(b, tctx, &user_handle)) { + return false; + } + } + return ret; +} + +static bool test_QueryDisplayInfo(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryDisplayInfo r; + struct samr_QueryDomainInfo dom_info; + union samr_DomainInfo *info = NULL; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + bool seen_testuser = false; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo disp_info; + + + for (i=0;iinfo1.count; + break; + case 2: + if (!test_each_DisplayInfo_user(b, tctx, &r, NULL)) { + ret = false; + } + r.in.start_idx += r.out.info->info2.count; + break; + case 3: + r.in.start_idx += r.out.info->info3.count; + break; + case 4: + r.in.start_idx += r.out.info->info4.count; + break; + case 5: + r.in.start_idx += r.out.info->info5.count; + break; + } + } + dom_info.in.domain_handle = handle; + dom_info.in.level = 2; + dom_info.out.info = &info; + + /* Check number of users returned is correct */ + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &dom_info), + "QueryDomainInfo failed"); + if (!NT_STATUS_IS_OK(dom_info.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(dom_info.out.result)); + ret = false; + break; + } + switch (r.in.level) { + case 1: + case 4: + if (info->general.num_users < r.in.start_idx) { + /* On AD deployments this numbers don't match + * since QueryDisplayInfo returns universal and + * global groups, QueryDomainInfo only global + * ones. */ + if (torture_setting_bool(tctx, "samba3", false)) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n", + r.in.start_idx, info->general.num_groups, + info->general.domain_name.string); + ret = false; + } + } + if (!seen_testuser) { + struct policy_handle user_handle; + if (NT_STATUS_IS_OK(test_OpenUser_byname(b, tctx, handle, TEST_ACCOUNT_NAME, &user_handle))) { + torture_result(tctx, TORTURE_FAIL, "Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n", + info->general.domain_name.string); + ret = false; + test_samr_handle_Close(b, tctx, &user_handle); + } + } + break; + case 3: + case 5: + if (info->general.num_groups != r.in.start_idx) { + /* On AD deployments this numbers don't match + * since QueryDisplayInfo returns universal and + * global groups, QueryDomainInfo only global + * ones. */ + if (torture_setting_bool(tctx, "samba3", false)) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n", + r.in.start_idx, info->general.num_groups, + info->general.domain_name.string); + ret = false; + } + } + + break; + } + + } + + return ret; +} + +static bool test_QueryDisplayInfo2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryDisplayInfo2 r; + bool ret = true; + uint16_t levels[] = {1, 2, 3, 4, 5}; + int i; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; + + for (i=0;iinfo1.entries[0].idx != r.in.start_idx + 1) { + torture_result(tctx, TORTURE_FAIL, "expected idx %d but got %d\n", + r.in.start_idx + 1, + r.out.info->info1.entries[0].idx); + break; + } + } + if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) && + !NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level %u failed - %s\n", + r.in.level, nt_errstr(r.out.result)); + ret = false; + break; + } + r.in.start_idx++; + } while ((NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) || + NT_STATUS_IS_OK(r.out.result)) && + *r.out.returned_size != 0); + + return ret; +} + +static bool test_QueryDomainInfo(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryDomainInfo r; + union samr_DomainInfo *info = NULL; + struct samr_SetDomainInfo s; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + uint16_t set_ok[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; + int i; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *domain_comment = talloc_asprintf(tctx, + "Tortured by Samba4 RPC-SAMR: %s", + timestring(tctx, time(NULL))); + + s.in.domain_handle = handle; + s.in.level = 4; + s.in.info = talloc(tctx, union samr_DomainInfo); + + s.in.info->oem.oem_information.string = domain_comment; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), + "SetDomainInfo failed"); + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u (set comment) failed - %s\n", + s.in.level, nt_errstr(s.out.result)); + return false; + } + + for (i=0;igeneral.oem_information.string, domain_comment) != 0) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", + levels[i], info->general.oem_information.string, domain_comment); + if (!torture_setting_bool(tctx, "samba3", false)) { + ret = false; + } + } + if (!info->general.primary.string) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n", + levels[i]); + ret = false; + } else if (info->general.role == SAMR_ROLE_DOMAIN_PDC) { + if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), info->general.primary.string) != 0) { + if (torture_setting_bool(tctx, "samba3", false)) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n", + levels[i], info->general.primary.string, dcerpc_server_name(p)); + } + } + } + break; + case 4: + if (strcmp(info->oem.oem_information.string, domain_comment) != 0) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", + levels[i], info->oem.oem_information.string, domain_comment); + if (!torture_setting_bool(tctx, "samba3", false)) { + ret = false; + } + } + break; + case 6: + if (!info->info6.primary.string) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n", + levels[i]); + ret = false; + } + break; + case 11: + if (strcmp(info->general2.general.oem_information.string, domain_comment) != 0) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different comment (%s, expected %s)\n", + levels[i], info->general2.general.oem_information.string, domain_comment); + if (!torture_setting_bool(tctx, "samba3", false)) { + ret = false; + } + } + break; + } + + torture_comment(tctx, "Testing SetDomainInfo level %u\n", levels[i]); + + s.in.domain_handle = handle; + s.in.level = levels[i]; + s.in.info = info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s), + "SetDomainInfo failed"); + if (set_ok[i]) { + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(s.out.result)); + ret = false; + continue; + } + } else { + if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) { + torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n", + r.in.level, nt_errstr(s.out.result)); + ret = false; + continue; + } + } + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), + "QueryDomainInfo failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n", + r.in.level, nt_errstr(r.out.result)); + ret = false; + continue; + } + } + + return ret; +} + + +static bool test_QueryDomainInfo2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_QueryDomainInfo2 r; + union samr_DomainInfo *info = NULL; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + int i; + bool ret = true; + + for (i=0;ientries[i].name.string, + &names, &num_names); + } + } + + torture_assert_ntstatus_ok(tctx, status, "EnumDomainGroups"); + + torture_assert(tctx, sam, "EnumDomainGroups failed to return sam"); + + if (builtin_domain) { + torture_assert(tctx, num_names == 0, + "EnumDomainGroups shouldn't return any group in the builtin domain!"); + } + + q2.in.domain_handle = handle; + q2.in.level = 5; + q2.in.start_idx = 0; + q2.in.max_entries = 5; + q2.in.buf_size = (uint32_t)-1; + q2.out.total_size = &total_size; + q2.out.returned_size = &returned_size; + q2.out.info = &info; + + status = STATUS_MORE_ENTRIES; + while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &q2), + "QueryDisplayInfo failed"); + status = q2.out.result; + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) + break; + + for (i=0; iinfo5.count; i++) { + int j; + const char *name = q2.out.info->info5.entries[i].account_name.string; + bool found = false; + for (j=0; jinfo5.count; + } + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level 5 failed - %s\n", + nt_errstr(status)); + ret = false; + } + + if (builtin_domain) { + torture_assert(tctx, q2.in.start_idx != 0, + "QueryDisplayInfo should return all domain groups also on the builtin domain handle!"); + } + + for (i=0; icount; i++) { + if (rids->rids[i] == rid) { + found_member = true; + } + } + + torture_assert(tctx, found_member, "QueryGroupMember did not list newly added member"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteGroupMember_r(b, tctx, &d), + "DeleteGroupMember failed"); + torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteGroupMember"); + + rids = NULL; + found_member = false; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &q), + "QueryGroupMember failed"); + torture_assert_ntstatus_ok(tctx, q.out.result, "QueryGroupMember"); + torture_assert(tctx, rids, "QueryGroupMember did not fill in rids structure"); + + for (i=0; i < rids->count; i++) { + if (rids->rids[i] == rid) { + found_member = true; + } + } + + torture_assert(tctx, !found_member, "QueryGroupMember does still list removed member"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r), + "AddGroupMember failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "AddGroupMember"); + + return true; +} + + +static bool test_CreateDomainGroup(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *group_name, + struct policy_handle *group_handle, + struct dom_sid *domain_sid, + bool test_group) +{ + struct samr_CreateDomainGroup r; + uint32_t rid; + struct lsa_String name; + bool ret = true; + + init_lsa_String(&name, group_name); + + r.in.domain_handle = domain_handle; + r.in.name = &name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.group_handle = group_handle; + r.out.rid = &rid; + + torture_comment(tctx, "Testing CreateDomainGroup(%s)\n", r.in.name->string); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), + "CreateDomainGroup failed"); + + if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) { + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.name->string); + return true; + } else { + torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.name->string, + nt_errstr(r.out.result)); + return false; + } + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_GROUP_EXISTS)) { + if (!test_DeleteGroup_byname(b, tctx, domain_handle, r.in.name->string)) { + torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string, + nt_errstr(r.out.result)); + return false; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), + "CreateDomainGroup failed"); + } + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.name->string)) { + + torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string, + nt_errstr(r.out.result)); + return false; + } + torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r), + "CreateDomainGroup failed"); + } + torture_assert_ntstatus_ok(tctx, r.out.result, "CreateDomainGroup"); + + if (!test_group) { + return ret; + } + + if (!test_AddGroupMember(b, tctx, domain_handle, group_handle)) { + torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } + + if (!test_SetGroupInfo(b, tctx, group_handle)) { + ret = false; + } + + return ret; +} + + +/* + its not totally clear what this does. It seems to accept any sid you like. +*/ +static bool test_RemoveMemberFromForeignDomain(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle) +{ + struct samr_RemoveMemberFromForeignDomain r; + + r.in.domain_handle = domain_handle; + r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32-12-34-56-78"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMemberFromForeignDomain_r(b, tctx, &r), + "RemoveMemberFromForeignDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMemberFromForeignDomain"); + + return true; +} + +static bool test_EnumDomainUsers(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainUsers r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.acct_flags = 0; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_comment(tctx, "Testing EnumDomainUsers\n"); + + do { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r(b, tctx, &r), + "EnumDomainUsers failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to enumerate users"); + } + status = r.out.result; + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_EnumDomainGroups(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainGroups r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_comment(tctx, "Testing EnumDomainGroups\n"); + + do { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), + "EnumDomainGroups failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to enumerate groups"); + } + status = r.out.result; + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_EnumDomainAliases(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainAliases r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_comment(tctx, "Testing EnumDomainAliases\n"); + + do { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), + "EnumDomainAliases failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to enumerate aliases"); + } + status = r.out.result; + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_QueryDisplayInfo_level(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + uint16_t level, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_QueryDisplayInfo r; + uint32_t total_num_entries = 0; + + r.in.domain_handle = handle; + r.in.level = level; + r.in.start_idx = 0; + r.in.max_entries = (uint32_t)-1; + r.in.buf_size = (uint32_t)-1; + + torture_comment(tctx, "Testing QueryDisplayInfo\n"); + + do { + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; + + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r), + "failed to query displayinfo"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query displayinfo"); + } + status = r.out.result; + + if (*r.out.returned_size == 0) { + break; + } + + switch (r.in.level) { + case 1: + total_num_entries += info.info1.count; + r.in.start_idx += info.info1.entries[info.info1.count - 1].idx + 1; + break; + case 2: + total_num_entries += info.info2.count; + r.in.start_idx += info.info2.entries[info.info2.count - 1].idx + 1; + break; + case 3: + total_num_entries += info.info3.count; + r.in.start_idx += info.info3.entries[info.info3.count - 1].idx + 1; + break; + case 4: + total_num_entries += info.info4.count; + r.in.start_idx += info.info4.entries[info.info4.count - 1].idx + 1; + break; + case 5: + total_num_entries += info.info5.count; + r.in.start_idx += info.info5.entries[info.info5.count - 1].idx + 1; + break; + default: + return false; + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_ManyObjects(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + struct torture_samr_context *ctx) +{ + uint32_t num_total = ctx->num_objects_large_dc; + uint32_t num_enum = 0; + uint32_t num_disp = 0; + uint32_t num_created = 0; + uint32_t num_anounced = 0; + uint32_t i; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct policy_handle *handles = talloc_zero_array(tctx, struct policy_handle, num_total); + + /* query */ + + { + struct samr_QueryDomainInfo2 r; + union samr_DomainInfo *info; + r.in.domain_handle = domain_handle; + r.in.level = 2; + r.out.info = &info; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r), + "QueryDomainInfo2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query domain info"); + + switch (ctx->choice) { + case TORTURE_SAMR_MANY_ACCOUNTS: + num_anounced = info->general.num_users; + break; + case TORTURE_SAMR_MANY_GROUPS: + num_anounced = info->general.num_groups; + break; + case TORTURE_SAMR_MANY_ALIASES: + num_anounced = info->general.num_aliases; + break; + default: + return false; + } + } + + /* create */ + + for (i=0; i < num_total; i++) { + + const char *name = NULL; + + switch (ctx->choice) { + case TORTURE_SAMR_MANY_ACCOUNTS: + name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i); + torture_assert(tctx, + test_CreateUser(p, tctx, domain_handle, name, &handles[i], domain_sid, 0, NULL, false), + "failed to create user"); + break; + case TORTURE_SAMR_MANY_GROUPS: + name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i); + torture_assert(tctx, + test_CreateDomainGroup(b, tctx, domain_handle, name, &handles[i], domain_sid, false), + "failed to create group"); + break; + case TORTURE_SAMR_MANY_ALIASES: + name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i); + torture_assert(tctx, + test_CreateAlias(b, tctx, domain_handle, name, &handles[i], domain_sid, false), + "failed to create alias"); + break; + default: + return false; + } + if (!ndr_policy_handle_empty(&handles[i])) { + num_created++; + } + } + + /* enum */ + + switch (ctx->choice) { + case TORTURE_SAMR_MANY_ACCOUNTS: + torture_assert(tctx, + test_EnumDomainUsers(b, tctx, domain_handle, &num_enum), + "failed to enum users"); + break; + case TORTURE_SAMR_MANY_GROUPS: + torture_assert(tctx, + test_EnumDomainGroups(b, tctx, domain_handle, &num_enum), + "failed to enum groups"); + break; + case TORTURE_SAMR_MANY_ALIASES: + torture_assert(tctx, + test_EnumDomainAliases(b, tctx, domain_handle, &num_enum), + "failed to enum aliases"); + break; + default: + return false; + } + + /* dispinfo */ + + switch (ctx->choice) { + case TORTURE_SAMR_MANY_ACCOUNTS: + torture_assert(tctx, + test_QueryDisplayInfo_level(b, tctx, domain_handle, 1, &num_disp), + "failed to query display info"); + break; + case TORTURE_SAMR_MANY_GROUPS: + torture_assert(tctx, + test_QueryDisplayInfo_level(b, tctx, domain_handle, 3, &num_disp), + "failed to query display info"); + break; + case TORTURE_SAMR_MANY_ALIASES: + /* no aliases in dispinfo */ + break; + default: + return false; + } + + /* close or delete */ + + for (i=0; i < num_total; i++) { + + if (ndr_policy_handle_empty(&handles[i])) { + continue; + } + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_assert(tctx, + test_samr_handle_Close(b, tctx, &handles[i]), + "failed to close handle"); + } else { + switch (ctx->choice) { + case TORTURE_SAMR_MANY_ACCOUNTS: + torture_assert(tctx, + test_DeleteUser(b, tctx, &handles[i]), + "failed to delete user"); + break; + case TORTURE_SAMR_MANY_GROUPS: + torture_assert(tctx, + test_DeleteDomainGroup(b, tctx, &handles[i]), + "failed to delete group"); + break; + case TORTURE_SAMR_MANY_ALIASES: + torture_assert(tctx, + test_DeleteAlias(b, tctx, &handles[i]), + "failed to delete alias"); + break; + default: + return false; + } + } + } + + talloc_free(handles); + + if (ctx->choice == TORTURE_SAMR_MANY_ACCOUNTS && num_enum != num_anounced + num_created) { + torture_comment(tctx, + "unexpected number of results (%u) returned in enum call, expected %u\n", + num_enum, num_anounced + num_created); + + torture_comment(tctx, + "unexpected number of results (%u) returned in dispinfo, call, expected %u\n", + num_disp, num_anounced + num_created); + } + + return true; +} + +static bool test_Connect(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle); + +static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, + struct torture_samr_context *ctx, struct dom_sid *sid) +{ + struct samr_OpenDomain r; + struct policy_handle domain_handle; + struct policy_handle alias_handle; + struct policy_handle user_handle; + struct policy_handle group_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(alias_handle); + ZERO_STRUCT(user_handle); + ZERO_STRUCT(group_handle); + ZERO_STRUCT(domain_handle); + + torture_comment(tctx, "Testing OpenDomain of %s\n", dom_sid_string(tctx, sid)); + + r.in.connect_handle = &ctx->handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.sid = sid; + r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r), + "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "OpenDomain failed"); + + /* run the domain tests with the main handle closed - this tests + the servers reference counting */ + torture_assert(tctx, test_samr_handle_Close(b, tctx, &ctx->handle), "Failed to close SAMR handle"); + + switch (ctx->choice) { + case TORTURE_SAMR_PASSWORDS: + case TORTURE_SAMR_USER_PRIVILEGES: + if (!torture_setting_bool(tctx, "samba3", false)) { + ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL); + } + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Testing PASSWORDS or PRIVILEGES on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + case TORTURE_SAMR_USER_ATTRIBUTES: + if (!torture_setting_bool(tctx, "samba3", false)) { + ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL); + } + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); + /* This test needs 'complex' users to validate */ + ret &= test_QueryDisplayInfo(b, tctx, &domain_handle); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Testing ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + case TORTURE_SAMR_PASSWORDS_PWDLASTSET: + case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT: + case TORTURE_SAMR_PASSWORDS_LOCKOUT: + if (!torture_setting_bool(tctx, "samba3", false)) { + ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, ctx->machine_credentials); + } + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, ctx->machine_credentials, true); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Testing PASSWORDS PWDLASTSET or BADPWDCOUNT on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + case TORTURE_SAMR_MANY_ACCOUNTS: + case TORTURE_SAMR_MANY_GROUPS: + case TORTURE_SAMR_MANY_ALIASES: + ret &= test_ManyObjects(p, tctx, &domain_handle, sid, ctx); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Testing MANY-{ACCOUNTS,GROUPS,ALIASES} on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + case TORTURE_SAMR_OTHER: + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true); + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid)); + } + if (!torture_setting_bool(tctx, "samba3", false)) { + ret &= test_QuerySecurity(b, tctx, &domain_handle); + } + ret &= test_RemoveMemberFromForeignDomain(b, tctx, &domain_handle); + ret &= test_CreateAlias(b, tctx, &domain_handle, TEST_ALIASNAME, &alias_handle, sid, true); + ret &= test_CreateDomainGroup(b, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true); + ret &= test_GetAliasMembership(b, tctx, &domain_handle); + ret &= test_QueryDomainInfo(p, tctx, &domain_handle); + ret &= test_QueryDomainInfo2(b, tctx, &domain_handle); + ret &= test_EnumDomainUsers_all(b, tctx, &domain_handle); + ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle); + ret &= test_EnumDomainGroups_all(b, tctx, &domain_handle); + ret &= test_EnumDomainAliases_all(b, tctx, &domain_handle); + ret &= test_QueryDisplayInfo2(b, tctx, &domain_handle); + ret &= test_QueryDisplayInfo3(b, tctx, &domain_handle); + ret &= test_QueryDisplayInfo_continue(b, tctx, &domain_handle); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping GetDisplayEnumerationIndex test against Samba4\n"); + } else { + ret &= test_GetDisplayEnumerationIndex(b, tctx, &domain_handle); + ret &= test_GetDisplayEnumerationIndex2(b, tctx, &domain_handle); + } + ret &= test_GroupList(b, tctx, sid, &domain_handle); + ret &= test_TestPrivateFunctionsDomain(b, tctx, &domain_handle); + ret &= test_RidToSid(b, tctx, sid, &domain_handle); + ret &= test_GetBootKeyInformation(b, tctx, &domain_handle); + if (!ret) { + torture_comment(tctx, "Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid)); + } + break; + } + + if (!ndr_policy_handle_empty(&user_handle) && + !test_DeleteUser(b, tctx, &user_handle)) { + ret = false; + } + + if (!ndr_policy_handle_empty(&alias_handle) && + !test_DeleteAlias(b, tctx, &alias_handle)) { + ret = false; + } + + if (!ndr_policy_handle_empty(&group_handle) && + !test_DeleteDomainGroup(b, tctx, &group_handle)) { + ret = false; + } + + torture_assert(tctx, test_samr_handle_Close(b, tctx, &domain_handle), "Failed to close SAMR domain handle"); + + torture_assert(tctx, test_Connect(b, tctx, &ctx->handle), "Failed to re-connect SAMR handle"); + /* reconnect the main handle */ + + if (!ret) { + torture_result(tctx, TORTURE_FAIL, "Testing domain %s failed!\n", dom_sid_string(tctx, sid)); + } + + return ret; +} + +static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tctx, + struct torture_samr_context *ctx, const char *domain) +{ + struct samr_LookupDomain r; + struct dom_sid2 *sid = NULL; + struct lsa_String n1; + struct lsa_String n2; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing LookupDomain(%s)\n", domain); + + /* check for correct error codes */ + r.in.connect_handle = &ctx->handle; + r.in.domain_name = &n2; + r.out.sid = &sid; + n2.string = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), + "LookupDomain failed"); + torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER, r.out.result, "LookupDomain expected NT_STATUS_INVALID_PARAMETER"); + + init_lsa_String(&n2, "xxNODOMAINxx"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), + "LookupDomain failed"); + torture_assert_ntstatus_equal(tctx, NT_STATUS_NO_SUCH_DOMAIN, r.out.result, "LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN"); + + r.in.connect_handle = &ctx->handle; + + init_lsa_String(&n1, domain); + r.in.domain_name = &n1; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), + "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LookupDomain"); + + if (!test_GetDomPwInfo(p, tctx, &n1)) { + ret = false; + } + + if (!test_OpenDomain(p, tctx, ctx, *r.out.sid)) { + ret = false; + } + + return ret; +} + + +static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx, + struct torture_samr_context *ctx) +{ + struct samr_EnumDomains r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + struct samr_SamArray *sam = NULL; + int i; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.connect_handle = &ctx->handle; + r.in.resume_handle = &resume_handle; + r.in.buf_size = (uint32_t)-1; + r.out.resume_handle = &resume_handle; + r.out.num_entries = &num_entries; + r.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), + "EnumDomains failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed"); + + if (!*r.out.sam) { + return false; + } + + for (i=0;icount;i++) { + if (!test_LookupDomain(p, tctx, ctx, + sam->entries[i].name.string)) { + ret = false; + } + } + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), + "EnumDomains failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed"); + + return ret; +} + + +static bool test_Connect(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct samr_Connect r; + struct samr_Connect2 r2; + struct samr_Connect3 r3; + struct samr_Connect4 r4; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + struct policy_handle h; + uint32_t level_out = 0; + bool ret = true, got_handle = false; + + torture_comment(tctx, "Testing samr_Connect\n"); + + r.in.system_name = NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect_r(b, tctx, &r), + "Connect failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "Connect failed - %s\n", nt_errstr(r.out.result)); + ret = false; + } else { + got_handle = true; + *handle = h; + } + + torture_comment(tctx, "Testing samr_Connect2\n"); + + r2.in.system_name = NULL; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect2_r(b, tctx, &r2), + "Connect2 failed"); + if (!NT_STATUS_IS_OK(r2.out.result)) { + torture_comment(tctx, "Connect2 failed - %s\n", nt_errstr(r2.out.result)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(b, tctx, handle); + } + got_handle = true; + *handle = h; + } + + torture_comment(tctx, "Testing samr_Connect3\n"); + + r3.in.system_name = NULL; + r3.in.unknown = 0; + r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r3.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect3_r(b, tctx, &r3), + "Connect3 failed"); + if (!NT_STATUS_IS_OK(r3.out.result)) { + torture_result(tctx, TORTURE_FAIL, "Connect3 failed - %s\n", nt_errstr(r3.out.result)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(b, tctx, handle); + } + got_handle = true; + *handle = h; + } + + torture_comment(tctx, "Testing samr_Connect4\n"); + + r4.in.system_name = ""; + r4.in.client_version = 0; + r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r4.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect4_r(b, tctx, &r4), + "Connect4 failed"); + if (!NT_STATUS_IS_OK(r4.out.result)) { + torture_result(tctx, TORTURE_FAIL, "Connect4 failed - %s\n", nt_errstr(r4.out.result)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(b, tctx, handle); + } + got_handle = true; + *handle = h; + } + + torture_comment(tctx, "Testing samr_Connect5\n"); + + info.info1.client_version = 0; + info.info1.supported_features = 0; + + r5.in.system_name = ""; + r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r5.in.level_in = 1; + r5.out.level_out = &level_out; + r5.in.info_in = &info; + r5.out.info_out = &info; + r5.out.connect_handle = &h; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect5_r(b, tctx, &r5), + "Connect5 failed"); + if (!NT_STATUS_IS_OK(r5.out.result)) { + torture_result(tctx, TORTURE_FAIL, "Connect5 failed - %s\n", nt_errstr(r5.out.result)); + ret = false; + } else { + if (got_handle) { + test_samr_handle_Close(b, tctx, handle); + } + got_handle = true; + *handle = h; + } + + return ret; +} + + +static bool test_samr_ValidatePassword(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct samr_ValidatePassword r; + union samr_ValidatePasswordReq req; + union samr_ValidatePasswordRep *repp = NULL; + NTSTATUS status; + const char *passwords[] = { "penguin", "p@ssw0rd", "p@ssw0rd123$", NULL }; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing samr_ValidatePassword\n"); + + if (p->conn->transport.transport != NCACN_IP_TCP) { + torture_comment(tctx, "samr_ValidatePassword only should succeed over NCACN_IP_TCP!\n"); + } + + ZERO_STRUCT(r); + r.in.level = NetValidatePasswordReset; + r.in.req = &req; + r.out.rep = &repp; + + ZERO_STRUCT(req); + req.req3.account.string = "non-existent-account-aklsdji"; + + for (i=0; passwords[i]; i++) { + req.req3.password.string = passwords[i]; + + status = dcerpc_samr_ValidatePassword_r(b, tctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + torture_skip(tctx, "ValidatePassword not supported by server\n"); + } + torture_assert_ntstatus_ok(tctx, status, + "samr_ValidatePassword failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_ValidatePassword failed"); + torture_comment(tctx, "Server %s password '%s' with code %i\n", + repp->ctr3.status==SAMR_VALIDATION_STATUS_SUCCESS?"allowed":"refused", + req.req3.password.string, repp->ctr3.status); + } + + return true; +} + +bool torture_rpc_samr(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_OTHER; + + ret &= test_Connect(b, torture, &ctx->handle); + + if (!torture_setting_bool(torture, "samba3", false)) { + ret &= test_QuerySecurity(b, torture, &ctx->handle); + } + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_SetDsrmPassword(b, torture, &ctx->handle); + + ret &= test_Shutdown(b, torture, &ctx->handle); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + + +bool torture_rpc_samr_users(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_USER_ATTRIBUTES; + + ret &= test_Connect(b, torture, &ctx->handle); + + if (!torture_setting_bool(torture, "samba3", false)) { + ret &= test_QuerySecurity(b, torture, &ctx->handle); + } + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_SetDsrmPassword(b, torture, &ctx->handle); + + ret &= test_Shutdown(b, torture, &ctx->handle); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + + +bool torture_rpc_samr_passwords(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_PASSWORDS; + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +static bool torture_rpc_samr_pwdlastset(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_PASSWORDS_PWDLASTSET; + ctx->machine_credentials = machine_credentials; + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_passwords_pwdlastset(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.pwdlastset"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME_PWD); + + torture_rpc_tcase_add_test_creds(tcase, "pwdLastSet", + torture_rpc_samr_pwdlastset); + + return suite; +} + +static bool torture_rpc_samr_users_privileges_delete_user(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_USER_PRIVILEGES; + ctx->machine_credentials = machine_credentials; + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_user_privileges(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.users.privileges"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME_PWD); + + torture_rpc_tcase_add_test_creds(tcase, "delete_privileged_user", + torture_rpc_samr_users_privileges_delete_user); + + return suite; +} + +static bool torture_rpc_samr_many_accounts(struct torture_context *torture, + struct dcerpc_pipe *p2, + void *data) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx = + talloc_get_type_abort(data, struct torture_samr_context); + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx->choice = TORTURE_SAMR_MANY_ACCOUNTS; + ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", + ctx->num_objects_large_dc); + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +static bool torture_rpc_samr_many_groups(struct torture_context *torture, + struct dcerpc_pipe *p2, + void *data) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx = + talloc_get_type_abort(data, struct torture_samr_context); + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx->choice = TORTURE_SAMR_MANY_GROUPS; + ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", + ctx->num_objects_large_dc); + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +static bool torture_rpc_samr_many_aliases(struct torture_context *torture, + struct dcerpc_pipe *p2, + void *data) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx = + talloc_get_type_abort(data, struct torture_samr_context); + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx->choice = TORTURE_SAMR_MANY_ALIASES; + ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc", + ctx->num_objects_large_dc); + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.large-dc"); + struct torture_rpc_tcase *tcase; + struct torture_samr_context *ctx; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", &ndr_table_samr); + + ctx = talloc_zero(suite, struct torture_samr_context); + ctx->num_objects_large_dc = 150; + + torture_rpc_tcase_add_test_ex(tcase, "many_aliases", + torture_rpc_samr_many_aliases, ctx); + torture_rpc_tcase_add_test_ex(tcase, "many_groups", + torture_rpc_samr_many_groups, ctx); + torture_rpc_tcase_add_test_ex(tcase, "many_accounts", + torture_rpc_samr_many_accounts, ctx); + + return suite; +} + +static bool torture_rpc_samr_badpwdcount(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_PASSWORDS_BADPWDCOUNT; + ctx->machine_credentials = machine_credentials; + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_passwords_badpwdcount(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.badpwdcount"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME_PWD); + + torture_rpc_tcase_add_test_creds(tcase, "badPwdCount", + torture_rpc_samr_badpwdcount); + + return suite; +} + +static bool torture_rpc_samr_lockout(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct torture_samr_context *ctx; + struct dcerpc_binding_handle *b; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + b = p->binding_handle; + + ctx = talloc_zero(torture, struct torture_samr_context); + + ctx->choice = TORTURE_SAMR_PASSWORDS_LOCKOUT; + ctx->machine_credentials = machine_credentials; + + ret &= test_Connect(b, torture, &ctx->handle); + + ret &= test_EnumDomains(p, torture, ctx); + + ret &= test_samr_handle_Close(b, torture, &ctx->handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_passwords_lockout(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.lockout"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME_PWD); + + torture_rpc_tcase_add_test_creds(tcase, "lockout", + torture_rpc_samr_lockout); + + return suite; +} + +struct torture_suite *torture_rpc_samr_passwords_validate(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.validate"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", + &ndr_table_samr); + torture_rpc_tcase_add_test(tcase, "validate", + test_samr_ValidatePassword); + + return suite; +} diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c new file mode 100644 index 0000000..1ed0d67 --- /dev/null +++ b/source4/torture/rpc/samr_accessmask.c @@ -0,0 +1,1197 @@ +/* + Unix SMB/CIFS implementation. + test suite for accessmasks on the SAMR pipe + + Copyright (C) Ronnie Sahlberg 2007 + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "libcli/security/security.h" + + +/* test user created to test the ACLs associated to SAMR objects */ +#define TEST_USER_NAME "samr_testuser" +#define TEST_MACHINENAME "samrtestmach" + +static NTSTATUS torture_samr_Close(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Close cl; + + cl.in.handle = h; + cl.out.handle = h; + status = dcerpc_samr_Close_r(b, tctx, &cl); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return cl.out.result; +} + +static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t mask, struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + uint32_t level_out = 0; + + info.info1.client_version = 0; + info.info1.supported_features = 0; + r5.in.system_name = ""; + r5.in.level_in = 1; + r5.in.info_in = &info; + r5.out.info_out = &info; + r5.out.level_out = &level_out; + r5.out.connect_handle = h; + r5.in.access_mask = mask; + + status = dcerpc_samr_Connect5_r(b, tctx, &r5); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return r5.out.result; +} + +/* check which bits in accessmask allows us to connect to the server */ +static bool test_samr_accessmask_Connect5(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct policy_handle h; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in accessmask allows us to connect\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5 with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &h); + mask <<= 1; + + switch (i) { + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 23: + case 26: + case 27: + printf(" expecting to fail"); + /* of only one of these bits are set we expect to + fail by default + */ + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, b, &h); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to EnumDomains() + by default we must specify at least one of : + SAMR/EnumDomains + Maximum + GenericAll + GenericRead + in the access mask to Connect5() in order to be allowed to perform + EnumDomains() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_EnumDomains ed; + struct policy_handle ch; + int i; + uint32_t mask; + uint32_t resume_handle = 0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in Connect5 accessmask allows us to EnumDomains\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/EnumDomains with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 4: /* SAMR/EnumDomains */ + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 31: /* GenericRead */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed), + "EnumDomains failed"); + if (!NT_STATUS_IS_OK(ed.out.result)) { + printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ed.in.connect_handle = &ch; + ed.in.resume_handle = &resume_handle; + ed.in.buf_size = (uint32_t)-1; + ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed), + "EnumDomains failed"); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ed.out.result)) { + printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + + +/* + * test how ACLs affect how/if a user can connect to the SAMR service + * + * samr_SetSecurity() returns SUCCESS when changing the ACL for + * a policy handle got from Connect5() but the ACL is not changed on + * the server + */ +static bool test_samr_connect_user_acl(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle ch; + struct policy_handle uch; + struct samr_QuerySecurity qs; + struct samr_SetSecurity ss; + struct security_ace ace = {}; + struct security_descriptor *sd; + struct sec_desc_buf sdb, *sdbuf = NULL; + bool ret = true; + int sd_size; + struct dcerpc_pipe *test_p; + struct dcerpc_binding_handle *test_b; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("Testing ACLs to allow/prevent users to connect to SAMR"); + + /* connect to SAMR */ + status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + + /* get the current ACL for the SAMR policy handle */ + qs.in.handle = &ch; + qs.in.sec_info = SECINFO_DACL; + qs.out.sdbuf = &sdbuf; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs), + "QuerySecurity failed"); + if (!NT_STATUS_IS_OK(qs.out.result)) { + printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result)); + ret = false; + } + + /* how big is the security descriptor? */ + sd_size = sdbuf->sd_size; + + + /* add an ACE to the security descriptor to deny the user the + * 'connect to server' right + */ + sd = sdbuf->sd; + ace.type = SEC_ACE_TYPE_ACCESS_DENIED; + ace.flags = 0; + ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER; + ace.trustee = *test_sid; + status = security_descriptor_dacl_add(sd, &ace); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to add ACE to security descriptor\n"); + ret = false; + } + ss.in.handle = &ch; + ss.in.sec_info = SECINFO_DACL; + ss.in.sdbuf = &sdb; + sdb.sd = sd; + torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &ss), + "SetSecurity failed"); + if (!NT_STATUS_IS_OK(ss.out.result)) { + printf("SetSecurity failed - %s\n", nt_errstr(ss.out.result)); + ret = false; + } + + + /* Try to connect as the test user */ + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status)); + return false; + } + test_b = test_p->binding_handle; + + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_b, SEC_FLAG_MAXIMUM_ALLOWED, &uch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + /* disconnect the user */ + talloc_free(test_p); + + + /* read the sequrity descriptor back. it should not have changed + * even though samr_SetSecurity returned SUCCESS + */ + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs), + "QuerySecurity failed"); + if (!NT_STATUS_IS_OK(qs.out.result)) { + printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result)); + ret = false; + } + if (sd_size != sdbuf->sd_size) { + printf("security descriptor changed\n"); + ret = false; + } + + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + ret = false; + } + + if (ret == true) { + printf(" OK\n"); + } + return ret; +} + +/* + * test if the ACLs are enforced for users. + * a normal testuser only gets the rights provided in the ACL for + * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER + * right. If the ACLs are checked when a user connects + * a testuser that requests the accessmask with only this bit set + * the connect should fail. + */ +static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct cli_credentials *test_credentials, + const struct dom_sid *test_sid) + +{ + NTSTATUS status; + struct policy_handle uch; + bool ret = true; + struct dcerpc_pipe *test_p; + struct dcerpc_binding_handle *test_b; + const char *binding = torture_setting_string(tctx, "binding", NULL); + + printf("Testing if ACLs are enforced for non domain admin users when connecting to SAMR"); + + + status = dcerpc_pipe_connect(tctx, + &test_p, binding, &ndr_table_samr, + test_credentials, tctx->ev, tctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status)); + return false; + } + test_b = test_p->binding_handle; + + /* connect to SAMR as the user */ + status = torture_samr_Connect5(tctx, test_b, SAMR_ACCESS_SHUTDOWN_SERVER, &uch); + if (NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + printf(" OK\n"); + + /* disconnect the user */ + talloc_free(test_p); + + return ret; +} + +/* check which bits in accessmask allows us to LookupDomain() + by default we must specify at least one of : + in the access mask to Connect5() in order to be allowed to perform + case 5: samr/opendomain + case 25: Maximum + case 28: GenericAll + case 29: GenericExecute + LookupDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; + struct policy_handle ch; + struct lsa_String dn; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + printf("Testing which bits in Connect5 accessmask allows us to LookupDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/LookupDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if (!NT_STATUS_IS_OK(ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +/* check which bits in accessmask allows us to OpenDomain() + by default we must specify at least one of : + samr/opendomain + Maximum + GenericAll + GenericExecute + in the access mask to Connect5() in order to be allowed to perform + OpenDomain() on the policy handle returned from Connect5() +*/ +static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain od; + struct policy_handle ch; + struct policy_handle dh; + struct lsa_String dn; + int i; + uint32_t mask; + struct dcerpc_binding_handle *b = p->binding_handle; + + + /* first we must grab the sid of the domain */ + status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld), + "LookupDomain failed"); + if (!NT_STATUS_IS_OK(ld.out.result)) { + printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result)); + return false; + } + + + + printf("Testing which bits in Connect5 accessmask allows us to OpenDomain\n"); + mask = 1; + for (i=0;i<33;i++) { + printf("Testing Connect5/OpenDomain with access mask 0x%08x", mask); + status = torture_samr_Connect5(tctx, b, mask, &ch); + mask <<= 1; + + switch (i) { + case 5: + case 25: /* Maximum */ + case 28: /* GenericAll */ + case 29: /* GenericExecute */ + /* these bits set are expected to succeed by default */ + if (!NT_STATUS_IS_OK(status)) { + printf("Connect5 failed - %s\n", nt_errstr(status)); + return false; + } + + od.in.connect_handle = &ch; + od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od.in.sid = sid; + od.out.domain_handle = &dh; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &od), + "OpenDomain failed"); + if (!NT_STATUS_IS_OK(od.out.result)) { + printf("OpenDomain failed - %s\n", nt_errstr(od.out.result)); + return false; + } + + status = torture_samr_Close(tctx, b, &dh); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + default: + printf(" expecting to fail"); + + if (!NT_STATUS_IS_OK(status)) { + printf(" OK\n"); + continue; + } + + status = torture_samr_Close(tctx, b, &ch); + if (!NT_STATUS_IS_OK(status)) { + printf("Close failed - %s\n", nt_errstr(status)); + return false; + } + break; + } + printf(" OK\n"); + } + + return true; +} + +static bool test_samr_connect(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + void *testuser; + const char *testuser_passwd; + struct cli_credentials *test_credentials; + bool ret = true; + const struct dom_sid *test_sid; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "Skipping test against Samba 3"); + } + + /* create a test user */ + testuser = torture_create_testuser(tctx, TEST_USER_NAME, lpcfg_workgroup(tctx->lp_ctx), + ACB_NORMAL, &testuser_passwd); + if (!testuser) { + printf("Failed to create test user\n"); + return false; + } + test_credentials = cli_credentials_init(tctx); + cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, lpcfg_workgroup(tctx->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED); + test_sid = torture_join_user_sid(testuser); + + + /* test if ACLs can be changed for the policy handle + * returned by Connect5 + */ + if (!test_samr_connect_user_acl(tctx, b, test_credentials, test_sid)) { + ret = false; + } + + /* test if the ACLs that are reported from the Connect5 + * policy handle is enforced. + * i.e. an ordinary user only has the same rights as Everybody + * ReadControl + * Samr/OpenDomain + * Samr/EnumDomains + * Samr/ConnectToServer + * is granted and should therefore not be able to connect when + * requesting SAMR_ACCESS_SHUTDOWN_SERVER + */ + if (!test_samr_connect_user_acl_enforced(tctx, b, test_credentials, test_sid)) { + ret = false; + } + + /* remove the test user */ + torture_leave_domain(tctx, testuser); + + return ret; +} + +struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.accessmask"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", + &ndr_table_samr); + + torture_rpc_tcase_add_test(tcase, "connect", test_samr_connect); + + /* test which bits in the accessmask to Connect5 will allow + * us to call OpenDomain() */ + torture_rpc_tcase_add_test(tcase, "OpenDomain", + test_samr_accessmask_OpenDomain); + + /* test which bits in the accessmask to Connect5 will allow + * us to call LookupDomain() */ + torture_rpc_tcase_add_test(tcase, "LookupDomain", + test_samr_accessmask_LookupDomain); + + /* test which bits in the accessmask to Connect5 will allow + * us to call EnumDomains() */ + torture_rpc_tcase_add_test(tcase, "EnumDomains", + test_samr_accessmask_EnumDomains); + + /* test which bits in the accessmask to Connect5 + will allow us to connect to the server */ + torture_rpc_tcase_add_test(tcase, "Connect5", + test_samr_accessmask_Connect5); + + return suite; +} + +static bool test_LookupRids(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t rid) +{ + struct samr_LookupRids r; + struct lsa_Strings names; + struct samr_Ids types; + + torture_comment(tctx, "Testing LookupRids %d\n", rid); + + r.in.domain_handle = domain_handle; + r.in.num_rids = 1; + r.in.rids = &rid; + r.out.names = &names; + r.out.types = &types; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &r), + "failed to call samr_LookupRids"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call samr_LookupRids"); + + return true; +} + + +static bool test_user(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t access_mask, + struct samr_DispEntryGeneral *u) +{ + struct policy_handle user_handle; + + torture_comment(tctx, "Testing user %s (%d)\n", u->account_name.string, u->rid); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, u->rid), + "failed to call lookuprids"); + + { + struct samr_OpenUser r; + + r.in.domain_handle = domain_handle; + r.in.access_mask = access_mask; + r.in.rid = u->rid; + r.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r), + "failed to open user"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open user"); + } + { + struct samr_QueryUserInfo r; + union samr_UserInfo *info; + uint32_t levels[] = { 16, 21 }; + int i; + + r.in.user_handle = &user_handle; + r.out.info = &info; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_comment(tctx, "Testing QueryUserInfo rid: %d level: %d\n", + u->rid, r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r), + talloc_asprintf(tctx, "failed to query user info level %d", r.in.level)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "failed to query user info level %d", r.in.level)); + } + } + { + struct samr_GetGroupsForUser r; + struct samr_RidWithAttributeArray *rids; + + r.in.user_handle = &user_handle; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r), + "failed to query groups for user"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query groups for user"); + } + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &user_handle), + "failed to close user handle"); + + return true; +} + +static bool test_samr_group(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + uint32_t access_mask, + struct samr_SamEntry *g) +{ + struct policy_handle group_handle; + + torture_comment(tctx, "Testing group %s (%d)\n", g->name.string, g->idx); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, g->idx), + "failed to call lookuprids"); + { + struct samr_OpenGroup r; + + r.in.domain_handle = domain_handle; + r.in.access_mask = access_mask; + r.in.rid = g->idx; + r.out.group_handle = &group_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r), + "failed to open group"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open group"); + } + { + struct samr_QueryGroupMember r; + struct samr_RidAttrArray *rids; + + r.in.group_handle = &group_handle; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &r), + "failed to query group member"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to query group member"); + + } + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &group_handle), + "failed to close group handle"); + + return true; +} + +static bool test_samr_alias(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + struct samr_SamEntry *a) +{ + torture_comment(tctx, "Testing alias %s (%d)\n", a->name.string, a->idx); + + torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, a->idx), + "failed to call lookuprids"); + + { + struct samr_GetAliasMembership r; + struct lsa_SidArray sids; + struct samr_Ids rids; + + ZERO_STRUCT(sids); + + r.in.domain_handle = domain_handle; + r.in.sids = &sids; + r.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r), + "failed to get alias membership"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to get alias membership"); + } + + + return true; +} + +static bool test_samr_domain(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + const char *domain_name, + struct policy_handle *connect_handle, + struct policy_handle *domain_handle_p) +{ + struct policy_handle domain_handle; + struct dom_sid *domain_sid; + + if (!domain_name) { + struct samr_EnumDomains r; + uint32_t resume_handle; + struct samr_SamArray *sam; + uint32_t num_entries; + int i; + + r.in.connect_handle = connect_handle; + r.in.buf_size = 0xffff; + r.in.resume_handle = &resume_handle; + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r), + "failed to enum domains"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to enum domains"); + + torture_assert_int_equal(tctx, num_entries, 2, + "unexpected number of domains"); + + torture_assert(tctx, sam, + "no domain pointer returned"); + + for (i=0; i < sam->count; i++) { + if (!strequal(sam->entries[i].name.string, "builtin")) { + domain_name = sam->entries[i].name.string; + break; + } + } + + torture_assert(tctx, domain_name, + "no domain found other than builtin found"); + } + + { + struct samr_LookupDomain r; + struct dom_sid2 *sid; + struct lsa_String name; + + name.string = talloc_strdup(tctx, domain_name); + + r.in.connect_handle = connect_handle; + r.in.domain_name = &name; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r), + "failed to lookup domain"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to lookup domain"); + + domain_sid = dom_sid_dup(tctx, sid); + } + + { + struct samr_OpenDomain r; + + r.in.connect_handle = connect_handle; + r.in.access_mask = access_mask; + r.in.sid = domain_sid; + r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r), + "failed to open domain"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to open domain"); + + } + + { + struct samr_QueryDomainInfo r; + union samr_DomainInfo *info; + uint32_t levels[] = { 1, 2, 8, 12 }; + int i; + + r.in.domain_handle = &domain_handle; + r.out.info = &info; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i]; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r), + talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level)); + } + + } + + *domain_handle_p = domain_handle; + + return true; +} + +static void get_query_dispinfo_params(int loop_count, + uint32_t *max_entries, + uint32_t *buf_size) +{ + switch(loop_count) { + case 0: + *max_entries = 512; + *buf_size = 16383; + break; + case 1: + *max_entries = 1024; + *buf_size = 32766; + break; + case 2: + *max_entries = 2048; + *buf_size = 65532; + break; + case 3: + *max_entries = 4096; + *buf_size = 131064; + break; + default: /* loop_count >= 4 */ + *max_entries = 4096; + *buf_size = 131071; + break; + } +} + + +static bool test_samr_users(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_QueryDisplayInfo r; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; + int loop_count = 0; + + r.in.domain_handle = domain_handle; + r.in.level = 1; + r.in.start_idx = 0; + + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; + + do { + int i; + + r.in.max_entries = 0xffff; + r.in.buf_size = 0xffff; + + get_query_dispinfo_params(loop_count, + &r.in.max_entries, + &r.in.buf_size); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r), + "QueryDisplayInfo failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call QueryDisplayInfo"); + } + + for (i=0; i < info.info1.count; i++) { + torture_assert(tctx, + test_user(tctx, b, domain_handle, access_mask, &info.info1.entries[i]), + "failed to test user"); + } + loop_count++; + r.in.start_idx += info.info1.count; + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool test_samr_groups(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_EnumDomainGroups r; + uint32_t resume_handle = 0; + struct samr_SamArray *sam; + uint32_t num_entries; + + r.in.domain_handle = domain_handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 0xFFFF; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + do { + int i; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r), + "EnumDomainGroups failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call EnumDomainGroups"); + } + + for (i=0; i < num_entries; i++) { + torture_assert(tctx, + test_samr_group(tctx, b, domain_handle, access_mask, &sam->entries[i]), + "failed to test group"); + } + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool test_samr_aliases(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *domain_handle) +{ + { + struct samr_EnumDomainAliases r; + uint32_t resume_handle = 0; + struct samr_SamArray *sam; + uint32_t num_entries; + + r.in.domain_handle = domain_handle; + r.in.resume_handle = &resume_handle; + r.in.max_size = 0xFFFF; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + do { + int i; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r), + "EnumDomainAliases failed"); + if (NT_STATUS_IS_ERR(r.out.result)) { + torture_assert_ntstatus_ok(tctx, r.out.result, + "failed to call EnumDomainAliases"); + } + + for (i=0; i < num_entries; i++) { + torture_assert(tctx, + test_samr_alias(tctx, b, domain_handle, &sam->entries[i]), + "failed to test alias"); + } + + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return true; +} + +static bool torture_rpc_samr_workstation_query(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert_ntstatus_ok(tctx, + torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &connect_handle), + "failed to connect to samr server"); + + torture_assert(tctx, + test_samr_domain(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + lpcfg_workgroup(tctx->lp_ctx), + &connect_handle, &domain_handle), + "failed to test domain"); + + torture_assert(tctx, + test_samr_users(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test users"); + + torture_assert(tctx, + test_samr_groups(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test groups"); + + torture_assert(tctx, + test_samr_aliases(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, + &domain_handle), + "failed to test aliases"); + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &domain_handle), + "failed to close domain handle"); + + torture_assert_ntstatus_ok(tctx, + torture_samr_Close(tctx, b, &connect_handle), + "failed to close connect handle"); + + return true; +} + +/* The purpose of this test is to verify that an account authenticated as a + * domain member workstation can query a DC for various remote read calls all + * opening objects while requesting SEC_FLAG_MAXIMUM_ALLOWED access rights on + * the object open calls. This is the behavior of winbind (and most of samba's + * client code) - gd */ + +struct torture_suite *torture_rpc_samr_workstation_auth(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.machine.auth"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_MACHINENAME); + + torture_rpc_tcase_add_test_creds(tcase, "workstation_query", + torture_rpc_samr_workstation_query); + + return suite; +} diff --git a/source4/torture/rpc/samr_handletype.c b/source4/torture/rpc/samr_handletype.c new file mode 100644 index 0000000..ab5e7d0 --- /dev/null +++ b/source4/torture/rpc/samr_handletype.c @@ -0,0 +1,217 @@ +/* + Unix SMB/CIFS implementation. + + test suite for handle types on the SAMR pipe + + Copyright (C) Samuel Cabrero 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "libcli/security/security.h" + +enum samr_handle { + SAMR_HANDLE_CONNECT, + SAMR_HANDLE_DOMAIN, + SAMR_HANDLE_USER, + SAMR_HANDLE_GROUP, + SAMR_HANDLE_ALIAS +}; + +static NTSTATUS torture_samr_Close(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Close cl; + + cl.in.handle = h; + cl.out.handle = h; + status = dcerpc_samr_Close_r(b, tctx, &cl); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return cl.out.result; +} + +static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t mask, struct policy_handle *h) +{ + NTSTATUS status; + struct samr_Connect5 r5; + union samr_ConnectInfo info; + uint32_t level_out = 0; + + info.info1.client_version = 0; + info.info1.supported_features = 0; + r5.in.system_name = ""; + r5.in.level_in = 1; + r5.in.info_in = &info; + r5.out.info_out = &info; + r5.out.level_out = &level_out; + r5.out.connect_handle = h; + r5.in.access_mask = mask; + + status = dcerpc_samr_Connect5_r(b, tctx, &r5); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return r5.out.result; +} + +static bool test_samr_handletype_OpenDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; + struct samr_OpenDomain od; + struct samr_OpenUser ou; + struct samr_OpenGroup og; + struct policy_handle ch; + struct policy_handle bad; + struct policy_handle dh; + struct policy_handle oh; + struct lsa_String dn; + struct dcerpc_binding_handle *b = p->binding_handle; + + /* first we must grab the sid of the domain */ + status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch); + torture_assert_ntstatus_ok(tctx, status, "Connect5 failed"); + + ld.in.connect_handle = &ch; + ld.in.domain_name = &dn; + ld.out.sid = &sid; + dn.string = lpcfg_workgroup(tctx->lp_ctx); + status = dcerpc_samr_LookupDomain_r(b, tctx, &ld); + torture_assert_ntstatus_ok(tctx, status, "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, ld.out.result, "LookupDomain failed"); + + status = torture_samr_Connect5(tctx, b, 1, &ch); + torture_assert_ntstatus_ok(tctx, status, "Connect5 failed"); + + od.in.connect_handle = &bad; + od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od.in.sid = sid; + od.out.domain_handle = &dh; + + /* Open domain, wrong handle GUID */ + bad = ch; + bad.uuid = GUID_random(); + + status = dcerpc_samr_OpenDomain_r(b, tctx, &od); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "OpenDomain succeeded with random GUID"); + + /* Open domain, wrong handle type */ + bad = ch; + bad.handle_type = SAMR_HANDLE_USER; + + status = dcerpc_samr_OpenDomain_r(b, tctx, &od); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "OpenDomain succeeded with wrong type"); + + /* Open domain */ + bad = ch; + + status = dcerpc_samr_OpenDomain_r(b, tctx, &od); + torture_assert_ntstatus_ok(tctx, status, "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, od.out.result, "OpenDomain failed"); + + bad = dh; + + /* Open user, wrong handle type */ + ou.in.domain_handle = &bad; + ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + ou.in.rid = 501; + ou.out.user_handle = &oh; + + bad.handle_type = SAMR_HANDLE_ALIAS; + + status = dcerpc_samr_OpenUser_r(b, tctx, &ou); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "OpenUser succeeded with wrong type"); + + /* Open user */ + bad.handle_type = SAMR_HANDLE_DOMAIN; + + status = dcerpc_samr_OpenUser_r(b, tctx, &ou); + torture_assert_ntstatus_ok(tctx, status, "OpenUser failed"); + torture_assert_ntstatus_ok(tctx, ou.out.result, "OpenUser failed"); + + /* Close user */ + status = torture_samr_Close(tctx, b, &oh); + torture_assert_ntstatus_ok(tctx, status, "Close failed"); + + bad = dh; + + /* Open group, wrong type */ + og.in.domain_handle = &bad; + og.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + og.in.rid = 513; + og.out.group_handle = &oh; + + bad.handle_type = SAMR_HANDLE_GROUP; + + status = dcerpc_samr_OpenGroup_r(b, tctx, &og); + torture_assert_ntstatus_equal(tctx, + status, + NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "OpenGroup succeeded with wrong type"); + + /* Open group */ + bad.handle_type = SAMR_HANDLE_DOMAIN; + + status = dcerpc_samr_OpenGroup_r(b, tctx, &og); + torture_assert_ntstatus_ok(tctx, status, "OpenGroup failed"); + torture_assert_ntstatus_ok(tctx, ou.out.result, "OpenGroup failed"); + + /* Close group */ + status = torture_samr_Close(tctx, b, &oh); + torture_assert_ntstatus_ok(tctx, status, "Close failed"); + + /* Close connect */ + status = torture_samr_Close(tctx, b, &ch); + torture_assert_ntstatus_ok(tctx, status, "Close failed"); + + return true; +} + +struct torture_suite *torture_rpc_samr_handletype(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = NULL; + struct torture_rpc_tcase *tcase = NULL; + + suite = torture_suite_create(mem_ctx, "samr.handletype"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", + &ndr_table_samr); + + torture_rpc_tcase_add_test(tcase, "OpenDomainHandleType", + test_samr_handletype_OpenDomain); + + return suite; +} diff --git a/source4/torture/rpc/samr_priv.c b/source4/torture/rpc/samr_priv.c new file mode 100644 index 0000000..a5aa4b7 --- /dev/null +++ b/source4/torture/rpc/samr_priv.c @@ -0,0 +1,580 @@ +/* + * Unix SMB/CIFS implementation. + * test suite for samr rpc operations + * + * Copyright (c) 2011 Andreas Schneider + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "param/param.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "libcli/security/security.h" +#include "torture/rpc/torture_rpc.h" + +#define TEST_ACCOUNT_NAME "guru" + +struct torture_user { + const char *username; + const char *password; + const char *domain; + uint32_t *builtin_memberships; + uint32_t num_builtin_memberships; + bool admin_rights; +}; + +struct torture_access_context { + struct dcerpc_pipe *pipe; + struct torture_user user; + struct test_join *join; +}; + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool test_samr_queryUserInfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *user_handle) +{ + struct samr_QueryUserInfo r; + union samr_UserInfo *info; + NTSTATUS status; + + r.in.level = UserGeneralInformation; + r.in.user_handle = user_handle; + r.out.info = &info; + + status = dcerpc_samr_QueryUserInfo_r(b, + tctx, + &r); + torture_assert_ntstatus_ok(tctx, status, "queryUserInfo failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "queryUserInfo failed"); + + return true; +} + +static bool test_LookupName(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *domain_handle, + const char *name, + uint32_t *rid) +{ + NTSTATUS status; + struct samr_LookupNames n; + struct lsa_String sname[1]; + struct samr_Ids rids, types; + + init_lsa_String(&sname[0], name); + + n.in.domain_handle = domain_handle; + n.in.num_names = 1; + n.in.names = sname; + n.out.rids = &rids; + n.out.types = &types; + + status = dcerpc_samr_LookupNames_r(b, tctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + if (!NT_STATUS_IS_OK(n.out.result)) { + return false; + } + + *rid = n.out.rids->ids[0]; + return true; +} + +static bool test_samr_CreateUser(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + const char *name, + struct policy_handle *user_handle) +{ + struct lsa_String username; + struct samr_CreateUser r; + uint32_t rid = 0; + NTSTATUS status; + + init_lsa_String(&username, name); + + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.domain_handle = domain_handle; + r.in.account_name = &username; + r.out.user_handle = user_handle; + r.out.rid = &rid; + + status = dcerpc_samr_CreateUser_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "CreateUser failed"); + + return NT_STATUS_IS_OK(r.out.result); +} + +static bool test_samr_OpenUser(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *domain_handle, + const char *name, + struct policy_handle *user_handle, + bool expected) +{ + struct samr_OpenUser r; + uint32_t rid = 0; + NTSTATUS status; + bool ok; + + ok = test_LookupName(b, tctx, domain_handle, name, &rid); + if (!ok && expected) { + torture_comment(tctx, " - lookup name for %s failed\n", name); + return true; + } else if (!ok) { + return false; + } + + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.domain_handle = domain_handle; + r.in.rid = rid; + r.out.user_handle = user_handle; + + status = dcerpc_samr_OpenUser_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "OpenUser failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "OpenUser failed"); + + return true; +} + +static bool test_samr_openDomain(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *connect_handle, + const char *domain, + struct policy_handle *domain_handle) +{ + struct samr_LookupDomain r; + struct samr_OpenDomain r2; + struct lsa_String n; + struct dom_sid *sid; + NTSTATUS status; + + r.in.connect_handle = connect_handle; + init_lsa_String(&n, domain); + r.in.domain_name = &n; + r.out.sid = &sid; + + status = dcerpc_samr_LookupDomain_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "LookupDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "LookupDomain failed"); + + r2.in.connect_handle = connect_handle; + r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r2.in.sid = sid; + r2.out.domain_handle = domain_handle; + + status = dcerpc_samr_OpenDomain_r(b, tctx, &r2); + torture_assert_ntstatus_ok(tctx, status, "OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r2.out.result, "OpenDomain failed"); + + return true; +} + +static bool test_samr_Connect(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *connect_handle) +{ + struct samr_Connect r; + NTSTATUS status; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = connect_handle; + + status = dcerpc_samr_Connect_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "SAMR connect failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "SAMR connect failed"); + + return true; +} + +static bool test_samr_create_user(struct torture_context *tctx, + struct torture_access_context *t, + const char *name) +{ + struct dcerpc_binding_handle *b = t->pipe->binding_handle; + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct policy_handle user_handle; + bool ok = false; + + torture_comment(tctx, "Connecting to SAMR\n"); + ZERO_STRUCT(connect_handle); + ok = test_samr_Connect(tctx, b, &connect_handle); + torture_assert(tctx, ok, "Unable to connect to domain"); + + torture_comment(tctx, "Opening domain %s\n", t->user.domain); + ZERO_STRUCT(domain_handle); + ok = test_samr_openDomain(tctx, + b, + &connect_handle, + t->user.domain, + &domain_handle); + torture_assert(tctx, ok, "Unable to open to domain"); + + torture_comment(tctx, "Creating account %s\n", name); + ZERO_STRUCT(user_handle); + ok = test_samr_CreateUser(tctx, + b, + &domain_handle, + name, + &user_handle); + + /* We don't check ok with torture macros here because the + * caller might be looking for failure */ + test_samr_handle_Close(b, tctx, &domain_handle); + test_samr_handle_Close(b, tctx, &connect_handle); + + return ok; +} + +static bool test_samr_userinfo_getinfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + bool expected) +{ + const char *name; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b; + struct policy_handle connect_handle; + struct policy_handle domain_handle; + struct policy_handle user_handle; + NTSTATUS status; + uint32_t i = 0; + bool ok; + + status = torture_rpc_connection(tctx, &p2, &ndr_table_samr); + torture_assert_ntstatus_ok(tctx, status, + "Creating secondary connection failed"); + b = p2->binding_handle; + + torture_comment(tctx, " - 2nd connect\n"); + /* connect */ + ZERO_STRUCT(connect_handle); + ok = test_samr_Connect(tctx, b, &connect_handle); + torture_assert(tctx, ok, "Unable to connect to domain"); + + torture_comment(tctx, " - 2nd open domain\n"); + /* open domain */ + ZERO_STRUCT(domain_handle); + ok = test_samr_openDomain(tctx, + b, + &connect_handle, + torture_setting_string(tctx, "workgroup", + lpcfg_workgroup(tctx->lp_ctx)), + &domain_handle); + torture_assert(tctx, ok, "Unable to open to domain"); + + /* create user */ + name = talloc_asprintf(tctx, + "%s%04d", + TEST_ACCOUNT_NAME, + i); + + torture_comment(tctx, " - 2nd open user\n"); + ZERO_STRUCT(user_handle); + ok = test_samr_OpenUser(tctx, + b, + &domain_handle, + name, + &user_handle, + expected); + torture_assert(tctx, ok, "Unable to open user"); + + if (!expected) { + torture_comment(tctx, " - 2nd query user\n"); + ok = test_samr_queryUserInfo(tctx, b, &user_handle); + torture_assert(tctx, ok, "Unable to query user"); + + test_samr_handle_Close(b, tctx, &user_handle); + } + + test_samr_handle_Close(b, tctx, &domain_handle); + test_samr_handle_Close(b, tctx, &connect_handle); + + talloc_free(p2); + + return true; +} + +#define NUM_RUNS 20 +static bool torture_rpc_samr_caching(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct test_join *join; + const char *password = NULL; + const char *name; + NTSTATUS status; + uint32_t i = 0; + bool ok; + + torture_comment(tctx, ">>> Testing User Info Caching\n"); + + /* create user */ + name = talloc_asprintf(tctx, + "%s%04d", + TEST_ACCOUNT_NAME, + i); + + torture_comment(tctx, "- Creating user %s\n", name); + + join = torture_create_testuser(tctx, + name, + torture_setting_string(tctx, "workgroup", + lpcfg_workgroup(tctx->lp_ctx)), + ACB_NORMAL, + &password); + torture_assert(tctx, join, "failed to join domain"); + + torture_comment(tctx, "- Query user information\n"); + for (i = 0; i < NUM_RUNS; i++) { + ok = test_samr_userinfo_getinfo(tctx, p, false); + torture_assert(tctx, ok, "test_samr_userinfo_getinfo failed"); + } + + torture_comment(tctx, "- Delete user\n"); + status = torture_delete_testuser(tctx, + join, + name); + torture_assert_ntstatus_ok(tctx, status, "DeleteUser failed"); + + torture_comment(tctx, "- Try to query user information again (should fail)\n"); + for (i = 0; i < NUM_RUNS; i++) { + ok = test_samr_userinfo_getinfo(tctx, + p, + true); + torture_assert(tctx, ok, "test_samr_userinfo_getinfo failed"); + } + + return true; +} +#undef NUM_RUNS + +static bool torture_rpc_samr_access_setup_membership(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t num_members, + uint32_t *members, + struct dom_sid *user_sid) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle connect_handle, domain_handle; + int i; + + torture_comment(tctx, + "Setting up BUILTIN membership for %s\n", + dom_sid_string(tctx, user_sid)); + + for (i=0; i < num_members; i++) { + torture_comment(tctx, "adding user to S-1-5-32-%d\n", members[i]); + } + + /* connect */ + { + struct samr_Connect2 r; + r.in.system_name = ""; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + ZERO_STRUCT(connect_handle); + r.out.connect_handle = &connect_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect2_r(b, tctx, &r), + "samr_Connect2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_Connect2 failed"); + } + + /* open domain */ + { + struct samr_OpenDomain r; + r.in.connect_handle = &connect_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32"); + ZERO_STRUCT(domain_handle); + r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, tctx, &r), + "samr_OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_OpenDomain failed"); + } + + for (i = 0; i < num_members; i++) { + + struct policy_handle alias_handle; + + /* open alias */ + { + struct samr_OpenAlias r; + r.in.domain_handle = &domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = members[i]; + ZERO_STRUCT(alias_handle); + r.out.alias_handle = &alias_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenAlias_r(b, tctx, &r), + "samr_OpenAlias failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_OpenAlias failed"); + } + + /* add alias member */ + { + struct samr_AddAliasMember r; + ZERO_STRUCT(alias_handle); + r.in.alias_handle = &alias_handle; + r.in.sid = user_sid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_AddAliasMember_r(b, tctx, &r), + "samr_AddAliasMember failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_AddAliasMember failed"); + } + + test_samr_handle_Close(b, tctx, &alias_handle); + } + + test_samr_handle_Close(b, tctx, &domain_handle); + test_samr_handle_Close(b, tctx, &connect_handle); + + return true; +} + +static bool torture_rpc_samr_access_setup(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct torture_access_context *t) +{ + const char *binding = torture_setting_string(tctx, "binding", NULL); + struct cli_credentials *test_credentials; + struct test_join *join; + struct dom_sid *test_sid; + struct dcerpc_pipe *samr_pipe; + + t->user.domain = torture_setting_string(tctx, "workgroup", + lpcfg_workgroup(tctx->lp_ctx)), + + join = torture_create_testuser(tctx, + t->user.username, + t->user.domain, + ACB_NORMAL, + &t->user.password); + torture_assert(tctx, join, "failed to join domain"); + t->join = join; + + test_credentials = cli_credentials_init(tctx); + + cli_credentials_set_workstation(test_credentials, + "localhost", + CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, + torture_setting_string(tctx, "workgroup", + lpcfg_workgroup(tctx->lp_ctx)), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, + t->user.username, + CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, + t->user.password, + CRED_SPECIFIED); + test_sid = discard_const_p(struct dom_sid, + torture_join_user_sid(t->join)); + + if (t->user.num_builtin_memberships) { + torture_assert(tctx, + torture_rpc_samr_access_setup_membership(tctx, + p, + t->user.num_builtin_memberships, + t->user.builtin_memberships, + test_sid), + "failed to setup membership"); + } + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect(tctx, + &samr_pipe, + binding, + &ndr_table_samr, + test_credentials, + tctx->ev, + tctx->lp_ctx), + "Error connecting to server"); + + t->pipe = samr_pipe; + + return true; +} + +static bool torture_rpc_samr_access(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_access_context *t; + const char *testuser; + bool ok; + + torture_comment(tctx, "Testing non-privileged user access\n"); + + t = talloc_zero(tctx, struct torture_access_context); + torture_assert(tctx, t, "talloc failed"); + + t->user.username = talloc_asprintf(t, "%s%04d", TEST_ACCOUNT_NAME, 100); + + torture_comment(tctx, "*** Setting up non-privleged user\n" + "***\n"); + + ok = torture_rpc_samr_access_setup(tctx, p, t); + torture_assert(tctx, ok, "torture_rpc_samr_access_setup failed"); + + testuser = talloc_asprintf(t, "%s%04d", TEST_ACCOUNT_NAME, 200); + + torture_comment(tctx, "*** Try to create user (%s) as non-privileged " + "user - should fail\n" + "***\n", testuser); + + ok = test_samr_create_user(tctx, t, testuser); + + torture_assert(tctx, ok == false, "*** Creating user was successful but it should fail"); + + return true; +} + +struct torture_suite *torture_rpc_samr_priv(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = + torture_suite_create(mem_ctx, "samr.priv"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, + "samr", + &ndr_table_samr); + + torture_rpc_tcase_add_test(tcase, + "caching", + torture_rpc_samr_caching); + + torture_rpc_tcase_add_test(tcase, + "access", + torture_rpc_samr_access); + + return suite; +} diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c new file mode 100644 index 0000000..a8541d3 --- /dev/null +++ b/source4/torture/rpc/samsync.c @@ -0,0 +1,1798 @@ +/* + Unix SMB/CIFS implementation. + + test suite for netlogon rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2003-2004 + Copyright (C) Tim Potter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "../lib/util/dlinklist.h" +#include "../lib/crypto/crypto.h" +#include "system/time.h" +#include "torture/rpc/torture_rpc.h" +#include "auth/gensec/gensec.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/samsync/samsync.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "param/param.h" +#include "lib/crypto/gnutls_helpers.h" + +#define TEST_MACHINE_NAME "samsynctest" +#define TEST_WKSTA_MACHINE_NAME "samsynctest2" +#define TEST_USER_NAME "samsynctestuser" + +/* + try a netlogon SamLogon +*/ +static NTSTATUS test_SamLogon(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct netlogon_creds_CredentialState *creds, + const char *domain, const char *account_name, + const char *workstation, + struct samr_Password *lm_hash, + struct samr_Password *nt_hash, + struct netr_SamInfo3 **info3) +{ + NTSTATUS status; + struct netr_LogonSamLogon r; + struct netr_Authenticator auth, auth2; + struct netr_NetworkInfo ninfo; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative; + struct dcerpc_binding_handle *b = p->binding_handle; + int rc; + + ninfo.identity_info.domain_name.string = domain; + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.account_name.string = account_name; + ninfo.identity_info.workstation.string = workstation; + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + if (nt_hash) { + ninfo.nt.length = 24; + ninfo.nt.data = talloc_array(mem_ctx, uint8_t, 24); + rc = SMBOWFencrypt(nt_hash->hash, ninfo.challenge, + ninfo.nt.data); + if (rc != 0) { + return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER); + } + } else { + ninfo.nt.length = 0; + ninfo.nt.data = NULL; + } + + if (lm_hash) { + ninfo.lm.length = 24; + ninfo.lm.data = talloc_array(mem_ctx, uint8_t, 24); + rc = SMBOWFencrypt(lm_hash->hash, ninfo.challenge, + ninfo.lm.data); + if (rc != 0) { + return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER); + } + } else { + ninfo.lm.length = 0; + ninfo.lm.data = NULL; + } + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = workstation; + r.in.credential = &auth; + r.in.return_authenticator = &auth2; + r.in.logon_level = NetlogonNetworkInformation; + r.in.logon = &logon; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + + ZERO_STRUCT(auth2); + netlogon_creds_client_authenticator(creds, &auth); + + r.in.validation_level = 3; + + status = dcerpc_netr_LogonSamLogon_r(b, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + if (info3) { + *info3 = validation.sam3; + } + + return r.out.result; +} + +struct samsync_state { +/* we remember the sequence numbers so we can easily do a DatabaseDelta */ + uint64_t seq_num[3]; + const char *domain_name[2]; + struct samsync_secret *secrets; + struct samsync_trusted_domain *trusted_domains; + struct netlogon_creds_CredentialState *creds; + struct netlogon_creds_CredentialState *creds_netlogon_wksta; + struct policy_handle *connect_handle; + struct policy_handle *domain_handle[2]; + struct dom_sid *sid[2]; + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + struct dcerpc_pipe *p_netlogon_wksta; + struct dcerpc_pipe *p_samr; + struct dcerpc_binding_handle *b_samr; + struct dcerpc_pipe *p_lsa; + struct dcerpc_binding_handle *b_lsa; + struct policy_handle *lsa_handle; +}; + +struct samsync_secret { + struct samsync_secret *prev, *next; + DATA_BLOB secret; + const char *name; + NTTIME mtime; +}; + +struct samsync_trusted_domain { + struct samsync_trusted_domain *prev, *next; + struct dom_sid *sid; + const char *name; +}; + +static struct policy_handle *samsync_open_domain(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + const char *domain, + struct dom_sid **sid_p) +{ + struct lsa_String name; + struct samr_OpenDomain o; + struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; + struct policy_handle *domain_handle = talloc(mem_ctx, struct policy_handle); + NTSTATUS nt_status; + + name.string = domain; + l.in.connect_handle = samsync_state->connect_handle; + l.in.domain_name = &name; + l.out.sid = &sid; + + nt_status = dcerpc_samr_LookupDomain_r(samsync_state->b_samr, mem_ctx, &l); + if (!NT_STATUS_IS_OK(nt_status)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(nt_status)); + return NULL; + } + if (!NT_STATUS_IS_OK(l.out.result)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result)); + return NULL; + } + + o.in.connect_handle = samsync_state->connect_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = *l.out.sid; + o.out.domain_handle = domain_handle; + + if (sid_p) { + *sid_p = *l.out.sid; + } + + nt_status = dcerpc_samr_OpenDomain_r(samsync_state->b_samr, mem_ctx, &o); + if (!NT_STATUS_IS_OK(nt_status)) { + torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(nt_status)); + return NULL; + } + if (!NT_STATUS_IS_OK(o.out.result)) { + torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(o.out.result)); + return NULL; + } + + return domain_handle; +} + +static struct sec_desc_buf *samsync_query_samr_sec_desc(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + struct policy_handle *handle) +{ + struct samr_QuerySecurity r; + struct sec_desc_buf *sdbuf = NULL; + NTSTATUS status; + + r.in.handle = handle; + r.in.sec_info = 0x7; + r.out.sdbuf = &sdbuf; + + status = dcerpc_samr_QuerySecurity_r(samsync_state->b_samr, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "SAMR QuerySecurity failed - %s\n", nt_errstr(status)); + return NULL; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "SAMR QuerySecurity failed - %s\n", nt_errstr(r.out.result)); + return NULL; + } + + return sdbuf; +} + +static struct sec_desc_buf *samsync_query_lsa_sec_desc(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct samsync_state *samsync_state, + struct policy_handle *handle) +{ + struct lsa_QuerySecurity r; + struct sec_desc_buf *sdbuf = NULL; + NTSTATUS status; + + r.in.handle = handle; + r.in.sec_info = 0x7; + r.out.sdbuf = &sdbuf; + + status = dcerpc_lsa_QuerySecurity_r(samsync_state->b_lsa, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "LSA QuerySecurity failed - %s\n", nt_errstr(status)); + return NULL; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "LSA QuerySecurity failed - %s\n", nt_errstr(r.out.result)); + return NULL; + } + + return sdbuf; +} + +#define TEST_UINT64_EQUAL(i1, i2) do {\ + if (i1 != i2) {\ + torture_comment(tctx, "%s: uint64 mismatch: " #i1 ": 0x%016llx (%lld) != " #i2 ": 0x%016llx (%lld)\n", \ + __location__, \ + (long long)i1, (long long)i1, \ + (long long)i2, (long long)i2);\ + ret = false;\ + } \ +} while (0) +#define TEST_INT_EQUAL(i1, i2) do {\ + if (i1 != i2) {\ + torture_comment(tctx, "%s: integer mismatch: " #i1 ": 0x%08x (%d) != " #i2 ": 0x%08x (%d)\n", \ + __location__, i1, i1, i2, i2); \ + ret = false;\ + } \ +} while (0) +#define TEST_TIME_EQUAL(t1, t2) do {\ + if (t1 != t2) {\ + torture_comment(tctx, "%s: NTTIME mismatch: " #t1 ":%s != " #t2 ": %s\n", \ + __location__, nt_time_string(mem_ctx, t1), nt_time_string(mem_ctx, t2));\ + ret = false;\ + } \ +} while (0) + +#define TEST_STRING_EQUAL(s1, s2) do {\ + if (!((!s1.string || s1.string[0]=='\0') && (!s2.string || s2.string[0]=='\0')) \ + && strcmp_safe(s1.string, s2.string) != 0) {\ + torture_comment(tctx, "%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, s1.string, s2.string);\ + ret = false;\ + } \ +} while (0) + +#define TEST_BINARY_STRING_EQUAL(s1, s2) do {\ + if (!((!s1.array || s1.array[0]=='\0') && (!s2.array || s2.array[0]=='\0')) \ + && memcmp(s1.array, s2.array, s1.length * 2) != 0) {\ + torture_comment(tctx, "%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, (const char *)s1.array, (const char *)s2.array);\ + ret = false;\ + } \ +} while (0) + +#define TEST_SID_EQUAL(s1, s2) do {\ + if (!dom_sid_equal(s1, s2)) {\ + torture_comment(tctx, "%s: dom_sid mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, dom_sid_string(mem_ctx, s1), dom_sid_string(mem_ctx, s2));\ + ret = false;\ + } \ +} while (0) + +/* The ~SEC_DESC_SACL_PRESENT is because we don't, as administrator, + * get back the SACL part of the SD when we ask over SAMR */ + +#define TEST_SEC_DESC_EQUAL(sd1, pipe, handle) do {\ + struct sec_desc_buf *sdbuf = samsync_query_ ##pipe## _sec_desc(tctx, mem_ctx, samsync_state, \ + handle); \ + if (!sdbuf || !sdbuf->sd) { \ + torture_comment(tctx, "Could not obtain security descriptor to match " #sd1 "\n");\ + ret = false; \ + } else {\ + if (!security_descriptor_mask_equal(sd1.sd, sdbuf->sd, \ + ~SEC_DESC_SACL_PRESENT)) {\ + torture_comment(tctx, "Security Descriptor Mismatch for %s:\n", #sd1);\ + NDR_PRINT_DEBUG(security_descriptor, sd1.sd);\ + NDR_PRINT_DEBUG(security_descriptor, sdbuf->sd);\ + ret = false;\ + }\ + }\ +} while (0) + +static bool samsync_handle_domain(struct torture_context *tctx, TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain; + struct dom_sid *dom_sid; + struct samr_QueryDomainInfo q[14]; /* q[0] will be unused simple for clarity */ + union samr_DomainInfo *info[14]; + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; + int i; + bool ret = true; + + samsync_state->seq_num[database_id] = + domain->sequence_num; + switch (database_id) { + case SAM_DATABASE_DOMAIN: + break; + case SAM_DATABASE_BUILTIN: + if (strcasecmp_m("BUILTIN", domain->domain_name.string) != 0) { + torture_comment(tctx, "BUILTIN domain has different name: %s\n", domain->domain_name.string); + } + break; + case SAM_DATABASE_PRIVS: + torture_comment(tctx, "DOMAIN entry on privs DB!\n"); + return false; + break; + } + + if (!samsync_state->domain_name[database_id]) { + samsync_state->domain_name[database_id] = + talloc_strdup(samsync_state, domain->domain_name.string); + } else { + if (strcasecmp_m(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) { + torture_comment(tctx, "Domain has name varies!: %s != %s\n", samsync_state->domain_name[database_id], + domain->domain_name.string); + return false; + } + } + + if (!samsync_state->domain_handle[database_id]) { + samsync_state->domain_handle[database_id] = + samsync_open_domain(tctx, + samsync_state, + samsync_state, + samsync_state->domain_name[database_id], + &dom_sid); + } + if (samsync_state->domain_handle[database_id]) { + samsync_state->sid[database_id] = dom_sid_dup(samsync_state, dom_sid); + } + + torture_comment(tctx, "\tsequence_nums[%d/%s]=%llu\n", + database_id, domain->domain_name.string, + (long long)samsync_state->seq_num[database_id]); + + for (i=0;idomain_handle[database_id]; + q[levels[i]].in.level = levels[i]; + q[levels[i]].out.info = &info[levels[i]]; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryDomainInfo_r(samsync_state->b_samr, mem_ctx, &q[levels[i]]), + talloc_asprintf(tctx, "QueryDomainInfo level %u failed", q[levels[i]].in.level)); + torture_assert_ntstatus_ok(tctx, q[levels[i]].out.result, + talloc_asprintf(tctx, "QueryDomainInfo level %u failed", q[levels[i]].in.level)); + } + + TEST_STRING_EQUAL(info[5]->info5.domain_name, domain->domain_name); + + TEST_STRING_EQUAL(info[2]->general.oem_information, domain->oem_information); + TEST_STRING_EQUAL(info[4]->oem.oem_information, domain->oem_information); + TEST_TIME_EQUAL(info[2]->general.force_logoff_time, domain->force_logoff_time); + TEST_TIME_EQUAL(info[3]->info3.force_logoff_time, domain->force_logoff_time); + + TEST_TIME_EQUAL(info[1]->info1.min_password_length, domain->min_password_length); + TEST_TIME_EQUAL(info[1]->info1.password_history_length, domain->password_history_length); + TEST_TIME_EQUAL(info[1]->info1.max_password_age, domain->max_password_age); + TEST_TIME_EQUAL(info[1]->info1.min_password_age, domain->min_password_age); + + TEST_UINT64_EQUAL(info[8]->info8.sequence_num, + domain->sequence_num); + TEST_TIME_EQUAL(info[8]->info8.domain_create_time, + domain->domain_create_time); + TEST_TIME_EQUAL(info[13]->info13.domain_create_time, + domain->domain_create_time); + + TEST_SEC_DESC_EQUAL(domain->sdbuf, samr, samsync_state->domain_handle[database_id]); + + return ret; +} + +static bool samsync_handle_policy(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_POLICY *policy = delta->delta_union.policy; + + switch (database_id) { + case SAM_DATABASE_DOMAIN: + case SAM_DATABASE_BUILTIN: + break; + case SAM_DATABASE_PRIVS: + torture_comment(tctx, "DOMAIN entry on privs DB!\n"); + return false; + } + + samsync_state->seq_num[database_id] = + policy->sequence_num; + + if (!samsync_state->domain_name[SAM_DATABASE_DOMAIN]) { + samsync_state->domain_name[SAM_DATABASE_DOMAIN] = + talloc_strdup(samsync_state, policy->primary_domain_name.string); + } else { + if (strcasecmp_m(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) { + torture_comment(tctx, "PRIMARY domain has name varies between DOMAIN and POLICY!: %s != %s\n", samsync_state->domain_name[SAM_DATABASE_DOMAIN], + policy->primary_domain_name.string); + return false; + } + } + + if (!dom_sid_equal(samsync_state->sid[SAM_DATABASE_DOMAIN], policy->sid)) { + torture_comment(tctx, "Domain SID from POLICY (%s) does not match domain sid from SAMR (%s)\n", + dom_sid_string(mem_ctx, policy->sid), dom_sid_string(mem_ctx, samsync_state->sid[SAM_DATABASE_DOMAIN])); + return false; + } + + torture_comment(tctx, "\tsequence_nums[%d/PRIVS]=%llu\n", + database_id, + (long long)samsync_state->seq_num[database_id]); + return true; +} + +static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_USER *user = delta->delta_union.user; + struct netr_SamInfo3 *info3 = NULL; + struct samr_Password lm_hash; + struct samr_Password nt_hash; + struct samr_Password *lm_hash_p = NULL; + struct samr_Password *nt_hash_p = NULL; + const char *domain; + const char *username = user->account_name.string; + NTSTATUS nt_status; + bool ret = true; + struct samr_OpenUser r; + struct samr_QueryUserInfo q; + union samr_UserInfo *info; + struct policy_handle user_handle; + struct samr_GetGroupsForUser getgr; + struct samr_RidWithAttributeArray *rids; + + switch (database_id) { + case SAM_DATABASE_DOMAIN: + case SAM_DATABASE_BUILTIN: + break; + case SAM_DATABASE_PRIVS: + torture_comment(tctx, "DOMAIN entry on privs DB!\n"); + return false; + } + + domain = samsync_state->domain_name[database_id]; + + if (domain == NULL || + samsync_state->domain_handle[database_id] == NULL) { + torture_comment(tctx, "SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenUser_r(samsync_state->b_samr, mem_ctx, &r), + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + + q.in.user_handle = &user_handle; + q.in.level = 21; + q.out.info = &info; + + TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryUserInfo_r(samsync_state->b_samr, mem_ctx, &q), + talloc_asprintf(tctx, "OpenUserInfo level %u failed", q.in.level)); + torture_assert_ntstatus_ok(tctx, q.out.result, + talloc_asprintf(tctx, "OpenUserInfo level %u failed", q.in.level)); + + getgr.in.user_handle = &user_handle; + getgr.out.rids = &rids; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_GetGroupsForUser_r(samsync_state->b_samr, mem_ctx, &getgr), + "GetGroupsForUser failed"); + torture_assert_ntstatus_ok(tctx, getgr.out.result, + "GetGroupsForUser failed"); + + if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &user_handle)) { + torture_comment(tctx, "samr_handle_Close failed\n"); + ret = false; + } + if (!ret) { + return false; + } + + TEST_STRING_EQUAL(info->info21.account_name, user->account_name); + TEST_STRING_EQUAL(info->info21.full_name, user->full_name); + TEST_INT_EQUAL(info->info21.rid, user->rid); + TEST_INT_EQUAL(info->info21.primary_gid, user->primary_gid); + TEST_STRING_EQUAL(info->info21.home_directory, user->home_directory); + TEST_STRING_EQUAL(info->info21.home_drive, user->home_drive); + TEST_STRING_EQUAL(info->info21.logon_script, user->logon_script); + TEST_STRING_EQUAL(info->info21.description, user->description); + TEST_STRING_EQUAL(info->info21.workstations, user->workstations); + + TEST_TIME_EQUAL(info->info21.last_logon, user->last_logon); + TEST_TIME_EQUAL(info->info21.last_logoff, user->last_logoff); + + + TEST_INT_EQUAL(info->info21.logon_hours.units_per_week, + user->logon_hours.units_per_week); + if (ret) { + if (memcmp(info->info21.logon_hours.bits, user->logon_hours.bits, + info->info21.logon_hours.units_per_week/8) != 0) { + torture_comment(tctx, "Logon hours mismatch\n"); + ret = false; + } + } + + TEST_INT_EQUAL(info->info21.bad_password_count, + user->bad_password_count); + TEST_INT_EQUAL(info->info21.logon_count, + user->logon_count); + + TEST_TIME_EQUAL(info->info21.last_password_change, + user->last_password_change); + TEST_TIME_EQUAL(info->info21.acct_expiry, + user->acct_expiry); + + TEST_INT_EQUAL((info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags); + if (user->acct_flags & ACB_PWNOEXP) { + if (info->info21.acct_flags & ACB_PW_EXPIRED) { + torture_comment(tctx, "ACB flags mismatch: both expired and no expiry!\n"); + ret = false; + } + if (info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) { + torture_comment(tctx, "ACB flags mismatch: no password expiry, but force password change 0x%016llx (%lld) != 0x%016llx (%lld)\n", + (unsigned long long)info->info21.force_password_change, + (unsigned long long)info->info21.force_password_change, + (unsigned long long)0x7FFFFFFFFFFFFFFFULL, (unsigned long long)0x7FFFFFFFFFFFFFFFULL + ); + ret = false; + } + } + + TEST_INT_EQUAL(info->info21.nt_password_set, user->nt_password_present); + TEST_INT_EQUAL(info->info21.lm_password_set, user->lm_password_present); + TEST_INT_EQUAL(info->info21.password_expired, user->password_expired); + + TEST_STRING_EQUAL(info->info21.comment, user->comment); + TEST_BINARY_STRING_EQUAL(info->info21.parameters, user->parameters); + + TEST_INT_EQUAL(info->info21.country_code, user->country_code); + TEST_INT_EQUAL(info->info21.code_page, user->code_page); + + TEST_STRING_EQUAL(info->info21.profile_path, user->profile_path); + + if (user->lm_password_present) { + lm_hash_p = &lm_hash; + } + if (user->nt_password_present) { + nt_hash_p = &nt_hash; + } + + if (user->user_private_info.SensitiveData) { + DATA_BLOB data; + struct netr_USER_KEYS keys; + enum ndr_err_code ndr_err; + data.data = user->user_private_info.SensitiveData; + data.length = user->user_private_info.DataLength; + ndr_err = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + if (keys.keys.keys2.lmpassword.length == 16) { + lm_hash_p = &lm_hash; + } + if (keys.keys.keys2.ntpassword.length == 16) { + nt_hash_p = &nt_hash; + } + } else { + torture_comment(tctx, "Failed to parse Sensitive Data for %s:\n", username); +#if 0 + dump_data(0, data.data, data.length); +#endif + return false; + } + } + + if (nt_hash_p) { + DATA_BLOB nt_hash_blob = data_blob_const(nt_hash_p, 16); + DEBUG(100,("ACCOUNT [%s\\%-25s] NTHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string_upper(mem_ctx, &nt_hash_blob))); + } + if (lm_hash_p) { + DATA_BLOB lm_hash_blob = data_blob_const(lm_hash_p, 16); + DEBUG(100,("ACCOUNT [%s\\%-25s] LMHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string_upper(mem_ctx, &lm_hash_blob))); + } + + nt_status = test_SamLogon(tctx, + samsync_state->p_netlogon_wksta, mem_ctx, samsync_state->creds_netlogon_wksta, + domain, + username, + TEST_WKSTA_MACHINE_NAME, + lm_hash_p, + nt_hash_p, + &info3); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) { + if (user->acct_flags & ACB_DISABLED) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_WSTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_SVRTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_DOMTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + if (user->acct_flags & ACB_DOMTRUST) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT)) { + if (user->acct_flags & ACB_AUTOLOCK) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED)) { + if (info->info21.acct_flags & ACB_PW_EXPIRED) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + if (!lm_hash_p && !nt_hash_p) { + return true; + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE)) { + /* We would need to know the server's current time to test this properly */ + return true; + } else if (NT_STATUS_IS_OK(nt_status)) { + TEST_INT_EQUAL(user->rid, info3->base.rid); + TEST_INT_EQUAL(user->primary_gid, info3->base.primary_gid); + /* this is 0x0 from NT4 sp6 */ + if (info3->base.acct_flags) { + TEST_INT_EQUAL(user->acct_flags, info3->base.acct_flags); + } + /* this is NULL from NT4 sp6 */ + if (info3->base.account_name.string) { + TEST_STRING_EQUAL(user->account_name, info3->base.account_name); + } + /* this is NULL from Win2k3 */ + if (info3->base.full_name.string) { + TEST_STRING_EQUAL(user->full_name, info3->base.full_name); + } + TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script); + TEST_STRING_EQUAL(user->profile_path, info3->base.profile_path); + TEST_STRING_EQUAL(user->home_directory, info3->base.home_directory); + TEST_STRING_EQUAL(user->home_drive, info3->base.home_drive); + TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script); + + + TEST_TIME_EQUAL(user->last_logon, info3->base.logon_time); + TEST_TIME_EQUAL(user->acct_expiry, info3->base.kickoff_time); + TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change); + TEST_TIME_EQUAL(info->info21.force_password_change, info3->base.force_password_change); + + /* Does the concept of a logoff time ever really + * exist? (not in any sensible way, according to the + * doco I read -- abartlet) */ + + /* This copes with the two different versions of 0 I see */ + /* with NT4 sp6 we have the || case */ + if (!((user->last_logoff == 0) + || (info3->base.logoff_time == 0x7fffffffffffffffLL))) { + TEST_TIME_EQUAL(user->last_logoff, info3->base.logoff_time); + } + + TEST_INT_EQUAL(rids->count, info3->base.groups.count); + if (rids->count == info3->base.groups.count) { + int i, j; + int count = rids->count; + bool *matched = talloc_zero_array(mem_ctx, bool, rids->count); + + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + if ((rids->rids[i].rid == + info3->base.groups.rids[j].rid) + && (rids->rids[i].attributes == + info3->base.groups.rids[j].attributes)) { + matched[i] = true; + } + } + } + + for (i = 0; i < rids->count; i++) { + if (matched[i] == false) { + ret = false; + torture_comment(tctx, "Could not find group RID %u found in getgroups in NETLOGON reply\n", + rids->rids[i].rid); + } + } + } + return ret; + } else { + torture_comment(tctx, "Could not validate password for user %s\\%s: %s\n", + domain, username, nt_errstr(nt_status)); + return false; + } + return false; +} + +static bool samsync_handle_alias(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_ALIAS *alias = delta->delta_union.alias; + bool ret = true; + + struct samr_OpenAlias r; + struct samr_QueryAliasInfo q; + union samr_AliasInfo *info; + struct policy_handle alias_handle; + + switch (database_id) { + case SAM_DATABASE_DOMAIN: + case SAM_DATABASE_BUILTIN: + break; + case SAM_DATABASE_PRIVS: + torture_comment(tctx, "DOMAIN entry on privs DB!\n"); + return false; + } + + if (samsync_state->domain_name[database_id] == NULL || + samsync_state->domain_handle[database_id] == NULL) { + torture_comment(tctx, "SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.alias_handle = &alias_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenAlias_r(samsync_state->b_samr, mem_ctx, &r), + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + + q.in.alias_handle = &alias_handle; + q.in.level = 1; + q.out.info = &info; + + TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryAliasInfo_r(samsync_state->b_samr, mem_ctx, &q), + "QueryAliasInfo failed"); + if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &alias_handle)) { + return false; + } + + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_comment(tctx, "QueryAliasInfo level %u failed - %s\n", + q.in.level, nt_errstr(q.out.result)); + return false; + } + + TEST_STRING_EQUAL(info->all.name, alias->alias_name); + TEST_STRING_EQUAL(info->all.description, alias->description); + return ret; +} + +static bool samsync_handle_group(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_GROUP *group = delta->delta_union.group; + bool ret = true; + + struct samr_OpenGroup r; + struct samr_QueryGroupInfo q; + union samr_GroupInfo *info; + struct policy_handle group_handle; + + switch (database_id) { + case SAM_DATABASE_DOMAIN: + case SAM_DATABASE_BUILTIN: + break; + case SAM_DATABASE_PRIVS: + torture_comment(tctx, "DOMAIN entry on privs DB!\n"); + return false; + } + + if (samsync_state->domain_name[database_id] == NULL || + samsync_state->domain_handle[database_id] == NULL) { + torture_comment(tctx, "SamSync needs domain information before the users\n"); + return false; + } + + r.in.domain_handle = samsync_state->domain_handle[database_id]; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.group_handle = &group_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenGroup_r(samsync_state->b_samr, mem_ctx, &r), + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + torture_assert_ntstatus_ok(tctx, r.out.result, + talloc_asprintf(tctx, "OpenUser(%u) failed", rid)); + + q.in.group_handle = &group_handle; + q.in.level = 1; + q.out.info = &info; + + TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle); + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_QueryGroupInfo_r(samsync_state->b_samr, mem_ctx, &q), + "QueryGroupInfo failed"); + if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &group_handle)) { + return false; + } + + if (!NT_STATUS_IS_OK(q.out.result)) { + torture_comment(tctx, "QueryGroupInfo level %u failed - %s\n", + q.in.level, nt_errstr(q.out.result)); + return false; + } + + TEST_STRING_EQUAL(info->all.name, group->group_name); + TEST_INT_EQUAL(info->all.attributes, group->attributes); + TEST_STRING_EQUAL(info->all.description, group->description); + return ret; +} + +static bool samsync_handle_secret(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + struct netr_DELTA_SECRET *secret = delta->delta_union.secret; + const char *name = delta->delta_id_union.name; + struct samsync_secret *nsec = talloc(samsync_state, struct samsync_secret); + struct samsync_secret *old = talloc(mem_ctx, struct samsync_secret); + struct lsa_QuerySecret q; + struct lsa_OpenSecret o; + struct policy_handle sec_handle; + struct lsa_DATA_BUF_PTR bufp1; + struct lsa_DATA_BUF_PTR bufp2; + NTTIME nsec_mtime; + NTTIME old_mtime; + bool ret = true; + DATA_BLOB lsa_blob1, lsa_blob_out, session_key; + NTSTATUS status; + + nsec->name = talloc_strdup(nsec, name); + nsec->secret = data_blob_talloc(nsec, secret->current_cipher.cipher_data, secret->current_cipher.maxlen); + nsec->mtime = secret->current_cipher_set_time; + + DLIST_ADD(samsync_state->secrets, nsec); + + old->name = talloc_strdup(old, name); + old->secret = data_blob_const(secret->old_cipher.cipher_data, secret->old_cipher.maxlen); + old->mtime = secret->old_cipher_set_time; + + o.in.handle = samsync_state->lsa_handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.name.string = name; + o.out.sec_handle = &sec_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_OpenSecret_r(samsync_state->b_lsa, mem_ctx, &o), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, o.out.result, + "OpenSecret failed"); + +/* + We would like to do this, but it is NOT_SUPPORTED on win2k3 + TEST_SEC_DESC_EQUAL(secret->sdbuf, lsa, &sec_handle); +*/ + status = dcerpc_fetch_session_key(samsync_state->p_lsa, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n", nt_errstr(status)); + return false; + } + + + ZERO_STRUCT(nsec_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + q.in.sec_handle = &sec_handle; + q.in.new_val = &bufp1; + q.in.new_mtime = &nsec_mtime; + q.in.old_val = &bufp2; + q.in.old_mtime = &old_mtime; + + bufp1.buf = NULL; + bufp2.buf = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_QuerySecret_r(samsync_state->b_lsa, mem_ctx, &q), + "QuerySecret failed"); + if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, q.out.result)) { + /* some things are just off limits */ + return true; + } else if (!NT_STATUS_IS_OK(q.out.result)) { + torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(q.out.result)); + return false; + } + + if (q.out.old_val->buf == NULL) { + /* probably just not available due to ACLs */ + } else { + lsa_blob1.data = q.out.old_val->buf->data; + lsa_blob1.length = q.out.old_val->buf->length; + + status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to decrypt secrets OLD blob: %s\n", nt_errstr(status)); + return false; + } + + if (!q.out.old_mtime) { + torture_comment(tctx, "OLD mtime not available on LSA for secret %s\n", old->name); + ret = false; + } + if (old->mtime != *q.out.old_mtime) { + torture_comment(tctx, "OLD mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n", + old->name, nt_time_string(mem_ctx, old->mtime), + nt_time_string(mem_ctx, *q.out.old_mtime)); + ret = false; + } + + if (old->secret.length != lsa_blob_out.length) { + torture_comment(tctx, "Returned secret %s doesn't match: %d != %d\n", + old->name, (int)old->secret.length, (int)lsa_blob_out.length); + ret = false; + } else if (memcmp(lsa_blob_out.data, + old->secret.data, old->secret.length) != 0) { + torture_comment(tctx, "Returned secret %s doesn't match: \n", + old->name); + DEBUG(1, ("SamSync Secret:\n")); + dump_data(1, old->secret.data, old->secret.length); + DEBUG(1, ("LSA Secret:\n")); + dump_data(1, lsa_blob_out.data, lsa_blob_out.length); + ret = false; + } + + } + + if (q.out.new_val->buf == NULL) { + /* probably just not available due to ACLs */ + } else { + lsa_blob1.data = q.out.new_val->buf->data; + lsa_blob1.length = q.out.new_val->buf->length; + + status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to decrypt secrets OLD blob\n"); + return false; + } + + if (!q.out.new_mtime) { + torture_comment(tctx, "NEW mtime not available on LSA for secret %s\n", nsec->name); + ret = false; + } + if (nsec->mtime != *q.out.new_mtime) { + torture_comment(tctx, "NEW mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n", + nsec->name, nt_time_string(mem_ctx, nsec->mtime), + nt_time_string(mem_ctx, *q.out.new_mtime)); + ret = false; + } + + if (nsec->secret.length != lsa_blob_out.length) { + torture_comment(tctx, "Returned secret %s doesn't match: %d != %d\n", + nsec->name, (int)nsec->secret.length, (int)lsa_blob_out.length); + ret = false; + } else if (memcmp(lsa_blob_out.data, + nsec->secret.data, nsec->secret.length) != 0) { + torture_comment(tctx, "Returned secret %s doesn't match: \n", + nsec->name); + DEBUG(1, ("SamSync Secret:\n")); + dump_data(1, nsec->secret.data, nsec->secret.length); + DEBUG(1, ("LSA Secret:\n")); + dump_data(1, lsa_blob_out.data, lsa_blob_out.length); + ret = false; + } + } + + return ret; +} + +static bool samsync_handle_trusted_domain(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + bool ret = true; + struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain; + struct dom_sid *dom_sid = delta->delta_id_union.sid; + + struct samsync_trusted_domain *ndom = talloc(samsync_state, struct samsync_trusted_domain); + struct lsa_OpenTrustedDomain t; + struct policy_handle trustdom_handle; + struct lsa_QueryTrustedDomainInfo q; + union lsa_TrustedDomainInfo *info[9]; + union lsa_TrustedDomainInfo *_info = NULL; + int levels [] = {1, 3, 8}; + int i; + + ndom->name = talloc_strdup(ndom, trusted_domain->domain_name.string); + ndom->sid = dom_sid_dup(ndom, dom_sid); + + t.in.handle = samsync_state->lsa_handle; + t.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + t.in.sid = dom_sid; + t.out.trustdom_handle = &trustdom_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_OpenTrustedDomain_r(samsync_state->b_lsa, mem_ctx, &t), + "OpenTrustedDomain failed"); + torture_assert_ntstatus_ok(tctx, t.out.result, + "OpenTrustedDomain failed"); + + for (i=0; i< ARRAY_SIZE(levels); i++) { + q.in.trustdom_handle = &trustdom_handle; + q.in.level = levels[i]; + q.out.info = &_info; + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_QueryTrustedDomainInfo_r(samsync_state->b_lsa, mem_ctx, &q), + "QueryTrustedDomainInfo failed"); + if (!NT_STATUS_IS_OK(q.out.result)) { + if (q.in.level == 8 && NT_STATUS_EQUAL(q.out.result, NT_STATUS_INVALID_PARAMETER)) { + info[levels[i]] = NULL; + continue; + } + torture_comment(tctx, "QueryInfoTrustedDomain level %d failed - %s\n", + levels[i], nt_errstr(q.out.result)); + return false; + } + info[levels[i]] = _info; + } + + if (info[8]) { + TEST_SID_EQUAL(info[8]->full_info.info_ex.sid, dom_sid); + TEST_STRING_EQUAL(info[8]->full_info.info_ex.netbios_name, trusted_domain->domain_name); + } + TEST_STRING_EQUAL(info[1]->name.netbios_name, trusted_domain->domain_name); + TEST_INT_EQUAL(info[3]->posix_offset.posix_offset, trusted_domain->posix_offset); +/* + We would like to do this, but it is NOT_SUPPORTED on win2k3 + TEST_SEC_DESC_EQUAL(trusted_domain->sdbuf, lsa, &trustdom_handle); +*/ + DLIST_ADD(samsync_state->trusted_domains, ndom); + + return ret; +} + +static bool samsync_handle_account(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, + int database_id, struct netr_DELTA_ENUM *delta) +{ + bool ret = true; + struct netr_DELTA_ACCOUNT *account = delta->delta_union.account; + struct dom_sid *dom_sid = delta->delta_id_union.sid; + + struct lsa_OpenAccount a; + struct policy_handle acct_handle; + struct lsa_EnumPrivsAccount e; + struct lsa_PrivilegeSet *privs = NULL; + struct lsa_LookupPrivName r; + + int i, j; + + bool *found_priv_in_lsa; + + a.in.handle = samsync_state->lsa_handle; + a.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + a.in.sid = dom_sid; + a.out.acct_handle = &acct_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_OpenAccount_r(samsync_state->b_lsa, mem_ctx, &a), + "OpenAccount failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, + "OpenAccount failed"); + + TEST_SEC_DESC_EQUAL(account->sdbuf, lsa, &acct_handle); + + found_priv_in_lsa = talloc_zero_array(mem_ctx, bool, account->privilege_entries); + + e.in.handle = &acct_handle; + e.out.privs = &privs; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_EnumPrivsAccount_r(samsync_state->b_lsa, mem_ctx, &e), + "EnumPrivsAccount failed"); + torture_assert_ntstatus_ok(tctx, e.out.result, + "EnumPrivsAccount failed"); + + if ((account->privilege_entries && !privs)) { + torture_comment(tctx, "Account %s has privileges in SamSync, but not LSA\n", + dom_sid_string(mem_ctx, dom_sid)); + return false; + } + + if (!account->privilege_entries && privs && privs->count) { + torture_comment(tctx, "Account %s has privileges in LSA, but not SamSync\n", + dom_sid_string(mem_ctx, dom_sid)); + return false; + } + + TEST_INT_EQUAL(account->privilege_entries, privs->count); + + for (i=0;i< privs->count; i++) { + + struct lsa_StringLarge *name = NULL; + + r.in.handle = samsync_state->lsa_handle; + r.in.luid = &privs->set[i].luid; + r.out.name = &name; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_LookupPrivName_r(samsync_state->b_lsa, mem_ctx, &r), + "\nLookupPrivName failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "\nLookupPrivName failed"); + + if (!r.out.name) { + torture_comment(tctx, "\nLookupPrivName failed to return a name\n"); + return false; + } + for (j=0;jprivilege_entries; j++) { + if (strcmp(name->string, account->privilege_name[j].string) == 0) { + found_priv_in_lsa[j] = true; + break; + } + } + } + for (j=0;jprivilege_entries; j++) { + if (!found_priv_in_lsa[j]) { + torture_comment(tctx, "Privilege %s on account %s not found in LSA\n", account->privilege_name[j].string, + dom_sid_string(mem_ctx, dom_sid)); + ret = false; + } + } + return ret; +} + +/* + try a netlogon DatabaseSync +*/ +static bool test_DatabaseSync(struct torture_context *tctx, + struct samsync_state *samsync_state, + TALLOC_CTX *mem_ctx) +{ + TALLOC_CTX *loop_ctx, *delta_ctx, *trustdom_ctx; + struct netr_DatabaseSync r; + const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; + int i, d; + bool ret = true; + struct samsync_trusted_domain *t; + struct samsync_secret *s; + struct netr_Authenticator return_authenticator, credential; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + + const char *domain, *username; + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (i=0;icreds, &credential); + + r.in.credential = &credential; + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_DatabaseSync_r(samsync_state->b, loop_ctx, &r), + "DatabaseSync failed"); + if (!NT_STATUS_IS_OK(r.out.result) && + !NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) { + torture_comment(tctx, "DatabaseSync - %s\n", nt_errstr(r.out.result)); + ret = false; + break; + } + + if (!netlogon_creds_client_check(samsync_state->creds, &r.out.return_authenticator->cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + r.in.sync_context = r.out.sync_context; + + for (d=0; d < delta_enum_array->num_deltas; d++) { + delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); + + if (!NT_STATUS_IS_OK(samsync_fix_delta(delta_ctx, samsync_state->creds, + r.in.database_id, + &delta_enum_array->delta_enum[d]))) { + torture_comment(tctx, "Failed to decrypt delta\n"); + ret = false; + } + + switch (delta_enum_array->delta_enum[d].delta_type) { + case NETR_DELTA_DOMAIN: + if (!samsync_handle_domain(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_DOMAIN\n"); + ret = false; + } + break; + case NETR_DELTA_GROUP: + if (!samsync_handle_group(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_USER\n"); + ret = false; + } + break; + case NETR_DELTA_USER: + if (!samsync_handle_user(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_USER\n"); + ret = false; + } + break; + case NETR_DELTA_ALIAS: + if (!samsync_handle_alias(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_ALIAS\n"); + ret = false; + } + break; + case NETR_DELTA_POLICY: + if (!samsync_handle_policy(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_POLICY\n"); + ret = false; + } + break; + case NETR_DELTA_TRUSTED_DOMAIN: + if (!samsync_handle_trusted_domain(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_TRUSTED_DOMAIN\n"); + ret = false; + } + break; + case NETR_DELTA_ACCOUNT: + if (!samsync_handle_account(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_ACCOUNT\n"); + ret = false; + } + break; + case NETR_DELTA_SECRET: + if (!samsync_handle_secret(tctx, delta_ctx, samsync_state, + r.in.database_id, &delta_enum_array->delta_enum[d])) { + torture_comment(tctx, "Failed to handle DELTA_SECRET\n"); + ret = false; + } + break; + case NETR_DELTA_GROUP_MEMBER: + case NETR_DELTA_ALIAS_MEMBER: + /* These are harder to cross-check, and we expect them */ + break; + case NETR_DELTA_DELETE_GROUP: + case NETR_DELTA_RENAME_GROUP: + case NETR_DELTA_DELETE_USER: + case NETR_DELTA_RENAME_USER: + case NETR_DELTA_DELETE_ALIAS: + case NETR_DELTA_RENAME_ALIAS: + case NETR_DELTA_DELETE_TRUST: + case NETR_DELTA_DELETE_ACCOUNT: + case NETR_DELTA_DELETE_SECRET: + case NETR_DELTA_DELETE_GROUP2: + case NETR_DELTA_DELETE_USER2: + case NETR_DELTA_MODIFY_COUNT: + default: + torture_comment(tctx, "Uxpected delta type %d\n", delta_enum_array->delta_enum[d].delta_type); + ret = false; + break; + } + talloc_free(delta_ctx); + } + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + + } + + domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN]; + if (!domain) { + torture_comment(tctx, "Never got a DOMAIN object in samsync!\n"); + return false; + } + + trustdom_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync Trusted domains context"); + + username = talloc_asprintf(trustdom_ctx, "%s$", domain); + for (t=samsync_state->trusted_domains; t; t=t->next) { + char *secret_name = talloc_asprintf(trustdom_ctx, "G$$%s", t->name); + for (s=samsync_state->secrets; s; s=s->next) { + if (strcasecmp_m(s->name, secret_name) == 0) { + NTSTATUS nt_status; + struct samr_Password nt_hash; + mdfour(nt_hash.hash, s->secret.data, s->secret.length); + + torture_comment(tctx, "Checking password for %s\\%s\n", t->name, username); + nt_status = test_SamLogon(tctx, + samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta, + t->name, + username, + TEST_WKSTA_MACHINE_NAME, + NULL, + &nt_hash, + NULL); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS)) { + torture_comment(tctx, "Verifiction of trust password to %s failed: %s (the trusted domain is not available)\n", + t->name, nt_errstr(nt_status)); + + break; + } + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + torture_comment(tctx, "Verifiction of trust password to %s: should have failed (nologon interdomain trust account), instead: %s\n", + t->name, nt_errstr(nt_status)); + ret = false; + } + + /* break it */ + nt_hash.hash[0]++; + nt_status = test_SamLogon(tctx, + samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta, + t->name, + username, + TEST_WKSTA_MACHINE_NAME, + NULL, + &nt_hash, + NULL); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + torture_comment(tctx, "Verifiction of trust password to %s: should have failed (wrong password), instead: %s\n", + t->name, nt_errstr(nt_status)); + ret = false; + } + + break; + } + } + } + talloc_free(trustdom_ctx); + return ret; +} + + +/* + try a netlogon DatabaseDeltas +*/ +static bool test_DatabaseDeltas(struct torture_context *tctx, + struct samsync_state *samsync_state, TALLOC_CTX *mem_ctx) +{ + TALLOC_CTX *loop_ctx; + struct netr_DatabaseDeltas r; + struct netr_Authenticator credential; + struct netr_Authenticator return_authenticator; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + bool ret = true; + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.preferredmaximumlength = (uint32_t)-1; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (i=0;iseq_num[i]; + + r.in.database_id = database_ids[i]; + r.in.sequence_num = &seq_num; + r.out.sequence_num = &seq_num; + + if (seq_num == 0) continue; + + /* this shows that the bdc doesn't need to do a single call for + * each seqnumber, and the pdc doesn't need to know about old values + * -- metze + */ + seq_num -= 10; + + torture_comment(tctx, "Testing DatabaseDeltas of id %d at %llu\n", + r.in.database_id, (long long)seq_num); + + do { + loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseDeltas loop context"); + netlogon_creds_client_authenticator(samsync_state->creds, &credential); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_DatabaseDeltas_r(samsync_state->b, loop_ctx, &r), + "DatabaseDeltas failed"); + if (!NT_STATUS_IS_OK(r.out.result) && + !NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) && + !NT_STATUS_EQUAL(r.out.result, NT_STATUS_SYNCHRONIZATION_REQUIRED)) { + torture_comment(tctx, "DatabaseDeltas - %s\n", nt_errstr(r.out.result)); + ret = false; + } + + if (!netlogon_creds_client_check(samsync_state->creds, &return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + seq_num++; + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return ret; +} + + +/* + try a netlogon DatabaseSync2 +*/ +static bool test_DatabaseSync2(struct torture_context *tctx, + struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, + struct netlogon_creds_CredentialState *creds) +{ + TALLOC_CTX *loop_ctx; + struct netr_DatabaseSync2 r; + const uint32_t database_ids[] = {0, 1, 2}; + int i; + bool ret = true; + struct netr_Authenticator return_authenticator, credential; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.preferredmaximumlength = (uint32_t)-1; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (i=0;icred)) { + torture_comment(tctx, "Credential chaining failed\n"); + } + + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)); + } + + return ret; +} + + + +bool torture_rpc_samsync(struct torture_context *torture) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + bool ret = true; + struct test_join *join_ctx; + struct test_join *join_ctx2; + struct test_join *user_ctx; + const char *machine_password; + const char *wksta_machine_password; + struct dcerpc_binding *b; + struct dcerpc_binding *b_netlogon_wksta; + struct samr_Connect c; + struct samr_SetDomainInfo s; + struct policy_handle *domain_policy; + + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + struct lsa_OpenPolicy2 r; + struct cli_credentials *credentials; + struct cli_credentials *credentials_wksta; + + struct samsync_state *samsync_state; + + char *test_machine_account; + + char *test_wksta_machine_account; + + mem_ctx = talloc_init("torture_rpc_netlogon"); + + test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME); + join_ctx = torture_create_testuser(torture, test_machine_account, + lpcfg_workgroup(torture->lp_ctx), ACB_SVRTRUST, + &machine_password); + if (!join_ctx) { + talloc_free(mem_ctx); + torture_comment(torture, "Failed to join as BDC\n"); + return false; + } + + test_wksta_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_WKSTA_MACHINE_NAME); + join_ctx2 = torture_create_testuser(torture, test_wksta_machine_account, lpcfg_workgroup(torture->lp_ctx), ACB_WSTRUST, &wksta_machine_password); + if (!join_ctx2) { + talloc_free(mem_ctx); + torture_comment(torture, "Failed to join as member\n"); + return false; + } + + user_ctx = torture_create_testuser(torture, TEST_USER_NAME, + lpcfg_workgroup(torture->lp_ctx), + ACB_NORMAL, NULL); + if (!user_ctx) { + talloc_free(mem_ctx); + torture_comment(torture, "Failed to create test account\n"); + return false; + } + + samsync_state = talloc_zero(mem_ctx, struct samsync_state); + + samsync_state->p_samr = torture_join_samr_pipe(join_ctx); + samsync_state->b_samr = samsync_state->p_samr->binding_handle; + samsync_state->connect_handle = talloc_zero(samsync_state, struct policy_handle); + samsync_state->lsa_handle = talloc_zero(samsync_state, struct policy_handle); + c.in.system_name = NULL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.out.connect_handle = samsync_state->connect_handle; + + torture_assert_ntstatus_ok_goto(torture, + dcerpc_samr_Connect_r(samsync_state->b_samr, mem_ctx, &c), + ret, failed, + "samr_Connect failed"); + torture_assert_ntstatus_ok_goto(torture, c.out.result, + ret, failed, + "samr_Connect failed"); + + domain_policy = samsync_open_domain(torture, mem_ctx, samsync_state, lpcfg_workgroup(torture->lp_ctx), NULL); + if (!domain_policy) { + torture_comment(torture, "samrsync_open_domain failed\n"); + ret = false; + goto failed; + } + + s.in.domain_handle = domain_policy; + s.in.level = 4; + s.in.info = talloc(mem_ctx, union samr_DomainInfo); + + s.in.info->oem.oem_information.string + = talloc_asprintf(mem_ctx, + "Tortured by Samba4: %s", + timestring(mem_ctx, time(NULL))); + torture_assert_ntstatus_ok_goto(torture, + dcerpc_samr_SetDomainInfo_r(samsync_state->b_samr, mem_ctx, &s), + ret, failed, + "SetDomainInfo failed"); + + if (!test_samr_handle_Close(samsync_state->b_samr, torture, domain_policy)) { + ret = false; + goto failed; + } + + torture_assert_ntstatus_ok_goto(torture, s.out.result, + ret, failed, + talloc_asprintf(torture, "SetDomainInfo level %u failed", s.in.level)); + + status = torture_rpc_connection(torture, + &samsync_state->p_lsa, + &ndr_table_lsarpc); + + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + samsync_state->b_lsa = samsync_state->p_lsa->binding_handle; + + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = "\\"; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = samsync_state->lsa_handle; + + torture_assert_ntstatus_ok_goto(torture, + dcerpc_lsa_OpenPolicy2_r(samsync_state->b_lsa, mem_ctx, &r), + ret, failed, + "OpenPolicy2 failed"); + torture_assert_ntstatus_ok_goto(torture, r.out.result, + ret, failed, + "OpenPolicy2 failed"); + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + status = dcerpc_binding_set_flags(b, + DCERPC_SCHANNEL | DCERPC_SIGN, + DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(torture, status, "set flags"); + + credentials = cli_credentials_init(mem_ctx); + + cli_credentials_set_workstation(credentials, TEST_MACHINE_NAME, CRED_SPECIFIED); + cli_credentials_set_domain(credentials, lpcfg_workgroup(torture->lp_ctx), CRED_SPECIFIED); + cli_credentials_set_username(credentials, test_machine_account, CRED_SPECIFIED); + cli_credentials_set_password(credentials, machine_password, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(credentials, + SEC_CHAN_BDC); + + status = dcerpc_pipe_connect_b(samsync_state, + &samsync_state->p, b, + &ndr_table_netlogon, + credentials, torture->ev, torture->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Failed to connect to server as a BDC: %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + samsync_state->b = samsync_state->p->binding_handle; + + samsync_state->creds = cli_credentials_get_netlogon_creds(credentials); + if (samsync_state->creds == NULL) { + ret = false; + } + + + + status = torture_rpc_binding(torture, &b_netlogon_wksta); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto failed; + } + + status = dcerpc_binding_set_flags(b_netlogon_wksta, + DCERPC_SCHANNEL | DCERPC_SIGN, + DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(torture, status, "set flags"); + + credentials_wksta = cli_credentials_init(mem_ctx); + + cli_credentials_set_workstation(credentials_wksta, TEST_WKSTA_MACHINE_NAME, CRED_SPECIFIED); + cli_credentials_set_domain(credentials_wksta, lpcfg_workgroup(torture->lp_ctx), CRED_SPECIFIED); + cli_credentials_set_username(credentials_wksta, test_wksta_machine_account, CRED_SPECIFIED); + cli_credentials_set_password(credentials_wksta, wksta_machine_password, CRED_SPECIFIED); + cli_credentials_set_secure_channel_type(credentials_wksta, + SEC_CHAN_WKSTA); + + status = dcerpc_pipe_connect_b(samsync_state, + &samsync_state->p_netlogon_wksta, + b_netlogon_wksta, + &ndr_table_netlogon, + credentials_wksta, torture->ev, torture->lp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(torture, "Failed to connect to server as a Workstation: %s\n", nt_errstr(status)); + ret = false; + goto failed; + } + + samsync_state->creds_netlogon_wksta = cli_credentials_get_netlogon_creds(credentials_wksta); + if (samsync_state->creds_netlogon_wksta == NULL) { + torture_comment(torture, "Failed to obtail schanel creds!\n"); + ret = false; + } + + if (!test_DatabaseSync(torture, samsync_state, mem_ctx)) { + torture_comment(torture, "DatabaseSync failed\n"); + ret = false; + } + + if (!test_DatabaseDeltas(torture, samsync_state, mem_ctx)) { + torture_comment(torture, "DatabaseDeltas failed\n"); + ret = false; + } + + if (!test_DatabaseSync2(torture, samsync_state->p, mem_ctx, samsync_state->creds)) { + torture_comment(torture, "DatabaseSync2 failed\n"); + ret = false; + } +failed: + + torture_leave_domain(torture, join_ctx); + torture_leave_domain(torture, join_ctx2); + torture_leave_domain(torture, user_ctx); + + talloc_free(mem_ctx); + + return ret; +} diff --git a/source4/torture/rpc/scanner.c b/source4/torture/rpc/scanner.c new file mode 100644 index 0000000..261c3b9 --- /dev/null +++ b/source4/torture/rpc/scanner.c @@ -0,0 +1,187 @@ +/* + Unix SMB/CIFS implementation. + + scanner for rpc calls + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_mgmt_c.h" +#include "librpc/ndr/ndr_table.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + +/* + work out how many calls there are for an interface + */ +static bool test_num_calls(struct torture_context *tctx, + const struct ndr_interface_table *iface, + TALLOC_CTX *mem_ctx, + struct ndr_syntax_id *id) +{ + struct dcerpc_pipe *p; + NTSTATUS status; + unsigned int i; + DATA_BLOB stub_in, stub_out; + struct ndr_interface_table _tbl; + const struct ndr_interface_table *tbl; + + /* FIXME: This should be fixed when torture_rpc_connection + * takes a ndr_syntax_id */ + tbl = ndr_table_by_syntax(id); + if (tbl == NULL) { + _tbl = *iface; + _tbl.name = "__unknown__"; + _tbl.syntax_id = *id; + _tbl.num_calls = UINT32_MAX; + tbl = &_tbl; + } + + status = torture_rpc_connection(tctx, &p, tbl); + if (!NT_STATUS_IS_OK(status)) { + char *uuid_str = GUID_string(mem_ctx, &id->uuid); + printf("Failed to connect to '%s' on '%s' - %s\n", + uuid_str, iface->name, nt_errstr(status)); + talloc_free(uuid_str); + return true; + } + + /* make null calls */ + stub_in = data_blob_talloc(mem_ctx, NULL, 1000); + memset(stub_in.data, 0xFF, stub_in.length); + + for (i=0;i<200;i++) { + bool ok; + uint32_t out_flags = 0; + + status = dcerpc_binding_handle_raw_call(p->binding_handle, + NULL, i, + 0, /* in_flags */ + stub_in.data, + stub_in.length, + mem_ctx, + &stub_out.data, + &stub_out.length, + &out_flags); + ok = dcerpc_binding_handle_is_connected(p->binding_handle); + if (!ok) { + printf("\tpipe disconnected at %u\n", i); + goto done; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + break; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + printf("\taccess denied at %u\n", i); + goto done; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) { + printf("\tprotocol error at %u\n", i); + } + } + + printf("\t%d calls available\n", i); + if (tbl->num_calls == UINT32_MAX) { + printf("\tinterface not known in local IDL\n"); + } else if (tbl->num_calls != i) { + printf("\tWARNING: local IDL defines %u calls\n", + (unsigned int)tbl->num_calls); + } else { + printf("\tOK: matches num_calls in local IDL\n"); + } + +done: + talloc_free(p); + return true; +} + + + +bool torture_rpc_scanner(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + TALLOC_CTX *loop_ctx; + bool ret = true; + const struct ndr_interface_list *l; + struct dcerpc_binding *b; + enum dcerpc_transport_t transport; + + status = torture_rpc_binding(torture, &b); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + transport = dcerpc_binding_get_transport(b); + + for (l=ndr_table_list();l;l=l->next) { + loop_ctx = talloc_named(torture, 0, "torture_rpc_scanner loop context"); + /* some interfaces are not mappable */ + if (l->table->num_calls == 0 || + strcmp(l->table->name, "mgmt") == 0) { + talloc_free(loop_ctx); + continue; + } + + printf("\nTesting pipe '%s'\n", l->table->name); + + if (transport == NCACN_IP_TCP) { + status = dcerpc_epm_map_binding(torture, b, l->table, + torture->ev, + torture->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to map port for uuid %s\n", + GUID_string(loop_ctx, &l->table->syntax_id.uuid)); + talloc_free(loop_ctx); + continue; + } + } else { + status = dcerpc_binding_set_string_option(b, "endpoint", + l->table->name); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + ret = false; + continue; + } + status = dcerpc_binding_set_abstract_syntax(b, + &l->table->syntax_id); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + ret = false; + continue; + } + } + + lpcfg_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(torture, b)); + + status = torture_rpc_connection(torture, &p, &ndr_table_mgmt); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(loop_ctx); + ret = false; + continue; + } + + if (!test_inq_if_ids(torture, p->binding_handle, torture, test_num_calls, l->table)) { + ret = false; + } + } + + return ret; +} + diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c new file mode 100644 index 0000000..d6dca36 --- /dev/null +++ b/source4/torture/rpc/schannel.c @@ -0,0 +1,1338 @@ +/* + Unix SMB/CIFS implementation. + + test suite for schannel operations + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" +#include "torture/rpc/torture_rpc.h" +#include "lib/cmdline/cmdline.h" +#include "../libcli/auth/schannel.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "system/filesys.h" +#include "param/param.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "libcli/composite/composite.h" +#include "lib/events/events.h" + +#define TEST_MACHINE_NAME "schannel" + +/* + try a netlogon SamLogon +*/ +bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState *creds) +{ + NTSTATUS status; + struct netr_LogonSamLogonEx r; + struct netr_NetworkInfo ninfo; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative = 1; + uint32_t _flags = 0; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int i; + int flags = CLI_CRED_NTLM_AUTH; + struct dcerpc_binding_handle *b = p->binding_handle; + + struct netr_UserSessionKey key; + struct netr_LMSessionKey LMSessKey; + uint32_t validation_levels[] = { 2, 3 }; + struct netr_SamBaseInfo *base = NULL; + const char *crypto_alg = ""; + bool can_do_validation_6 = true; + enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; + + if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(), + tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(credentials), + cli_credentials_get_domain(credentials)); + + status = cli_credentials_get_ntlm_response( + samba_cmdline_get_creds(), + tctx, + &flags, + chal, + NULL, /* server_timestamp */ + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, + "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.logon_level = NetlogonNetworkInformation; + r.in.logon= &logon; + r.in.flags = &_flags; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + r.out.flags = &_flags; + + /* + - retrieve level6 + - save usrsession and lmsession key + - retrieve level 2 + - calculate, compare + - retrieve level 3 + - calculate, compare + */ + + if (creds) { + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + crypto_alg = "AES"; + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + crypto_alg = "ARCFOUR"; + } + } + + dcerpc_binding_handle_auth_info(b, NULL, &auth_level); + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + r.in.validation_level = 6; + + torture_comment(tctx, + "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "LogonSamLogonEx failed"); + } else { + torture_comment(tctx, + "Skip auth_level[%u] Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + auth_level, ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + r.out.result = NT_STATUS_INVALID_INFO_CLASS; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) { + can_do_validation_6 = false; + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, + "LogonSamLogonEx failed"); + + key = r.out.validation->sam6->base.key; + LMSessKey = r.out.validation->sam6->base.LMSessKey; + + DEBUG(1,("unencrypted session keys from validation_level 6:\n")); + dump_data(1, r.out.validation->sam6->base.key.key, 16); + dump_data(1, r.out.validation->sam6->base.LMSessKey.key, 8); + } + + for (i=0; i < ARRAY_SIZE(validation_levels); i++) { + + r.in.validation_level = validation_levels[i]; + + torture_comment(tctx, + "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "LogonSamLogonEx failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "LogonSamLogonEx failed"); + + if (creds == NULL) { + /* when this test is called without creds no point in + * testing the session keys */ + continue; + } + + switch (validation_levels[i]) { + case 2: + base = &r.out.validation->sam2->base; + break; + case 3: + base = &r.out.validation->sam3->base; + break; + default: + break; + } + + DEBUG(1,("encrypted keys validation_level %d:\n", + validation_levels[i])); + dump_data(1, base->key.key, 16); + dump_data(1, base->LMSessKey.key, 8); + + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_decrypt(creds, base->key.key, 16); + netlogon_creds_aes_decrypt(creds, base->LMSessKey.key, 8); + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + netlogon_creds_arcfour_crypt(creds, base->key.key, 16); + netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, 8); + } + + DEBUG(1,("decrypted keys validation_level %d\n", + validation_levels[i])); + + dump_data(1, base->key.key, 16); + dump_data(1, base->LMSessKey.key, 8); + + if (!can_do_validation_6) { + /* we can't compare against unencrypted keys */ + continue; + } + + torture_assert_mem_equal(tctx, + base->key.key, + key.key, + 16, + "unexpected user session key\n"); + torture_assert_mem_equal(tctx, + base->LMSessKey.key, + LMSessKey.key, + 8, + "unexpected LM session key\n"); + } + + return true; +} + +static bool test_netlogon_ex_bug14932(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct cli_credentials *credentials, + struct netlogon_creds_CredentialState *creds) +{ + NTSTATUS status; + struct netr_LogonSamLogonEx r; + struct netr_NetworkInfo ninfo; + union netr_LogonLevel logon; + union netr_Validation validation; + uint8_t authoritative = 1; + uint32_t _flags = 0; + static const char *netapp_magic = + "\x01\x01\x00\x00\x00\x00\x00\x00" + "\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f" + "\xb8\x82\x3a\xf1\xb3\xdd\x08\x15" + "\x00\x00\x00\x00\x11\xa2\x08\x81" + "\x50\x38\x22\x78\x2b\x94\x47\xfe" + "\x54\x94\x7b\xff\x17\x27\x5a\xb4" + "\xf4\x18\xba\xdc\x2c\x38\xfd\x5b" + "\xfb\x0e\xc1\x85\x1e\xcc\x92\xbb" + "\x9b\xb1\xc4\xd5\x53\x14\xff\x8c" + "\x76\x49\xf5\x45\x90\x19\xa2"; + NTTIME timestamp = BVAL(netapp_magic, 8); + DATA_BLOB names_blob = data_blob_string_const(netapp_magic + 28); + DATA_BLOB chal, lm_resp, nt_resp; + int i; + int flags = CLI_CRED_NTLM_AUTH; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_UserSessionKey key; + struct netr_LMSessionKey LMSessKey; + uint32_t validation_levels[] = { 2, 3 }; + struct netr_SamBaseInfo *base = NULL; + const char *crypto_alg = ""; + bool can_do_validation_6 = true; + enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; + + flags |= CLI_CRED_NTLMv2_AUTH; + + cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(), + tctx, + &ninfo.identity_info.account_name.string, + &ninfo.identity_info.domain_name.string); + + generate_random_buffer(ninfo.challenge, + sizeof(ninfo.challenge)); + + chal = data_blob_const(ninfo.challenge, + sizeof(ninfo.challenge)); + + status = cli_credentials_get_ntlm_response( + samba_cmdline_get_creds(), + tctx, + &flags, + chal, + ×tamp, + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(tctx, status, + "cli_credentials_get_ntlm_response failed"); + + ninfo.lm.data = lm_resp.data; + ninfo.lm.length = lm_resp.length; + + ninfo.nt.data = nt_resp.data; + ninfo.nt.length = nt_resp.length; + + ninfo.identity_info.parameter_control = 0; + ninfo.identity_info.logon_id = 0; + ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); + + logon.network = &ninfo; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computer_name = cli_credentials_get_workstation(credentials); + r.in.logon_level = NetlogonNetworkInformation; + r.in.logon= &logon; + r.in.flags = &_flags; + r.out.validation = &validation; + r.out.authoritative = &authoritative; + r.out.flags = &_flags; + + /* + - retrieve level6 + - save usrsession and lmsession key + - retrieve level 2 + - calculate, compare + - retrieve level 3 + - calculate, compare + */ + + if (creds != NULL) { + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + crypto_alg = "AES"; + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + crypto_alg = "ARCFOUR"; + } + } + + dcerpc_binding_handle_auth_info(b, NULL, &auth_level); + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + r.in.validation_level = 6; + + torture_comment(tctx, + "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "LogonSamLogonEx failed"); + } else { + torture_comment(tctx, + "Skip auth_level[%u] Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + auth_level, ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + r.out.result = NT_STATUS_INVALID_INFO_CLASS; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) { + can_do_validation_6 = false; + } else { + torture_assert_ntstatus_ok(tctx, r.out.result, + "LogonSamLogonEx failed"); + + key = r.out.validation->sam6->base.key; + LMSessKey = r.out.validation->sam6->base.LMSessKey; + + DEBUG(1,("unencrypted session keys from validation_level 6:\n")); + dump_data(1, r.out.validation->sam6->base.key.key, 16); + dump_data(1, r.out.validation->sam6->base.LMSessKey.key, 8); + } + + for (i=0; i < ARRAY_SIZE(validation_levels); i++) { + + r.in.validation_level = validation_levels[i]; + + torture_comment(tctx, + "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", + ninfo.identity_info.account_name.string, crypto_alg, + r.in.validation_level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), + "LogonSamLogonEx failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "LogonSamLogonEx failed"); + + if (creds == NULL) { + /* when this test is called without creds no point in + * testing the session keys */ + continue; + } + + switch (validation_levels[i]) { + case 2: + base = &r.out.validation->sam2->base; + break; + case 3: + base = &r.out.validation->sam3->base; + break; + default: + break; + } + + DEBUG(1,("encrypted keys validation_level %d:\n", + validation_levels[i])); + dump_data(1, base->key.key, 16); + dump_data(1, base->LMSessKey.key, 8); + + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_decrypt(creds, base->key.key, 16); + netlogon_creds_aes_decrypt(creds, base->LMSessKey.key, 8); + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + netlogon_creds_arcfour_crypt(creds, base->key.key, 16); + netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, 8); + } + + DEBUG(1,("decrypted keys validation_level %d\n", + validation_levels[i])); + + dump_data(1, base->key.key, 16); + dump_data(1, base->LMSessKey.key, 8); + + if (!can_do_validation_6) { + /* we can't compare against unencrypted keys */ + continue; + } + + torture_assert_mem_equal(tctx, + base->key.key, + key.key, + 16, + "unexpected user session key\n"); + torture_assert_mem_equal(tctx, + base->LMSessKey.key, + LMSessKey.key, + 8, + "unexpected LM session key\n"); + } + + return true; +} + +/* + do some samr ops using the schannel connection + */ +static bool test_samr_ops(struct torture_context *tctx, + struct dcerpc_binding_handle *b) +{ + struct samr_GetDomPwInfo r; + struct samr_PwInfo info; + struct samr_Connect connect_r; + struct samr_OpenDomain opendom; + int i; + struct lsa_String name; + struct policy_handle handle; + struct policy_handle domain_handle; + + name.string = lpcfg_workgroup(tctx->lp_ctx); + r.in.domain_name = &name; + r.out.info = &info; + + connect_r.in.system_name = 0; + connect_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + connect_r.out.connect_handle = &handle; + + torture_comment(tctx, "Testing Connect and OpenDomain on BUILTIN\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect_r(b, tctx, &connect_r), + "Connect failed"); + if (!NT_STATUS_IS_OK(connect_r.out.result)) { + if (NT_STATUS_EQUAL(connect_r.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "Connect failed (expected, schannel mapped to anonymous): %s\n", + nt_errstr(connect_r.out.result)); + } else { + torture_comment(tctx, "Connect failed - %s\n", nt_errstr(connect_r.out.result)); + return false; + } + } else { + opendom.in.connect_handle = &handle; + opendom.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + opendom.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32"); + opendom.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &opendom), + "OpenDomain failed"); + if (!NT_STATUS_IS_OK(opendom.out.result)) { + torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(opendom.out.result)); + return false; + } + } + + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); + + /* do several ops to test credential chaining */ + for (i=0;i<5;i++) { + torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r), + "GetDomPwInfo failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "GetDomPwInfo op %d failed - %s\n", i, nt_errstr(r.out.result)); + return false; + } + } + } + + return true; +} + + +/* + do some lsa ops using the schannel connection + */ +static bool test_lsa_ops(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct lsa_GetUserName r; + bool ret = true; + struct lsa_String *account_name_p = NULL; + struct lsa_String *authority_name_p = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "\nTesting GetUserName\n"); + + r.in.system_name = "\\"; + r.in.account_name = &account_name_p; + r.in.authority_name = &authority_name_p; + r.out.account_name = &account_name_p; + + /* do several ops to test credential chaining and various operations */ + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r), + "lsa_GetUserName failed"); + + authority_name_p = *r.out.authority_name; + + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "GetUserName failed - %s\n", nt_errstr(r.out.result)); + return false; + } else { + if (!r.out.account_name) { + return false; + } + + if (strcmp(account_name_p->string, "ANONYMOUS LOGON") != 0) { + torture_comment(tctx, "GetUserName returned wrong user: %s, expected %s\n", + account_name_p->string, "ANONYMOUS LOGON"); + /* FIXME: gd */ + if (!torture_setting_bool(tctx, "samba3", false)) { + return false; + } + } + if (!authority_name_p || !authority_name_p->string) { + return false; + } + + if (strcmp(authority_name_p->string, "NT AUTHORITY") != 0) { + torture_comment(tctx, "GetUserName returned wrong user: %s, expected %s\n", + authority_name_p->string, "NT AUTHORITY"); + /* FIXME: gd */ + if (!torture_setting_bool(tctx, "samba3", false)) { + return false; + } + } + } + + return ret; +} + + +/* + test a schannel connection with the given flags + */ +static bool test_schannel(struct torture_context *tctx, + uint16_t acct_flags, uint32_t dcerpc_flags, + int i) +{ + struct test_join *join_ctx; + NTSTATUS status; + const char *binding = torture_setting_string(tctx, "binding", NULL); + struct dcerpc_binding *b; + struct dcerpc_pipe *p = NULL; + struct dcerpc_pipe *p_netlogon = NULL; + struct dcerpc_pipe *p_netlogon2 = NULL; + struct dcerpc_pipe *p_netlogon3 = NULL; + struct dcerpc_pipe *p_samr2 = NULL; + struct dcerpc_pipe *p_lsa = NULL; + struct netlogon_creds_CredentialState *creds; + struct cli_credentials *credentials; + enum dcerpc_transport_t transport; + + join_ctx = torture_join_domain(tctx, + talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, i), + acct_flags, &credentials); + torture_assert(tctx, join_ctx != NULL, "Failed to join domain"); + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_samr, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect to samr with schannel"); + + torture_assert(tctx, test_samr_ops(tctx, p->binding_handle), + "Failed to process schannel secured SAMR ops"); + + /* Also test that when we connect to the netlogon pipe, that + * the credentials we setup on the first pipe are valid for + * the second */ + + /* Swap the binding details from SAMR to NETLOGON */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm map"); + + status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_secondary_auth_connection(p, b, &ndr_table_netlogon, + credentials, tctx->lp_ctx, + tctx, &p_netlogon); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + + creds = cli_credentials_get_netlogon_creds(credentials); + torture_assert(tctx, (creds != NULL), "schannel creds"); + + /* checks the capabilities */ + torture_assert(tctx, test_netlogon_capabilities(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured capability ops (on fresh connection)"); + + /* do a couple of logins */ + torture_assert(tctx, test_netlogon_ops(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON ops"); + + torture_assert(tctx, test_netlogon_ex_ops(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON EX ops"); + + /* regression test for https://bugzilla.samba.org/show_bug.cgi?id=14932 */ + torture_assert(tctx, test_netlogon_ex_bug14932(p_netlogon, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON EX for BUG 14932"); + + /* we *MUST* use ncacn_np for openpolicy etc. */ + transport = dcerpc_binding_get_transport(b); + status = dcerpc_binding_set_transport(b, NCACN_NP); + torture_assert_ntstatus_ok(tctx, status, "set transport"); + + /* Swap the binding details from SAMR to LSARPC */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm map"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc, + credentials, tctx->ev, tctx->lp_ctx), + "failed to connect lsarpc with schannel"); + + torture_assert(tctx, test_lsa_ops(tctx, p_lsa), + "Failed to process schannel secured LSA ops"); + + talloc_free(p_lsa); + p_lsa = NULL; + + /* we *MUST* use ncacn_ip_tcp for lookupsids3/lookupnames4 */ + status = dcerpc_binding_set_transport(b, NCACN_IP_TCP); + torture_assert_ntstatus_ok(tctx, status, "set transport"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx), + "failed to call epm map"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc, + credentials, tctx->ev, tctx->lp_ctx), + "failed to connect lsarpc with schannel"); + + torture_assert(tctx, + test_many_LookupSids(p_lsa, tctx, NULL, LSA_LOOKUP_NAMES_ALL), + "LsaLookupSids3 failed!\n"); + + status = dcerpc_binding_set_transport(b, transport); + torture_assert_ntstatus_ok(tctx, status, "set transport"); + + + /* Drop the socket, we want to start from scratch */ + talloc_free(p); + p = NULL; + + /* Now see what we are still allowed to do */ + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_pipe_connect_b(tctx, &p_samr2, b, &ndr_table_samr, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, + "Failed to connect with schannel"); + + /* do a some SAMR operations. We have *not* done a new serverauthenticate */ + torture_assert (tctx, test_samr_ops(tctx, p_samr2->binding_handle), + "Failed to process schannel secured SAMR ops (on fresh connection)"); + + /* Swap the binding details from SAMR to NETLOGON */ + status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "epm"); + + status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_secondary_auth_connection(p_samr2, b, &ndr_table_netlogon, + credentials, tctx->lp_ctx, + tctx, &p_netlogon2); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + + /* checks the capabilities */ + torture_assert(tctx, test_netlogon_capabilities(p_netlogon2, tctx, credentials, creds), + "Failed to process schannel secured capability ops (on fresh connection)"); + + /* Try the schannel-only SamLogonEx operation */ + torture_assert(tctx, test_netlogon_ex_ops(p_netlogon2, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON EX ops (on fresh connection)"); + + + /* And the more traditional style, proving that the + * credentials chaining state is fully present */ + torture_assert(tctx, test_netlogon_ops(p_netlogon2, tctx, credentials, creds), + "Failed to process schannel secured NETLOGON ops (on fresh connection)"); + + /* Drop the socket, we want to start from scratch (again) */ + talloc_free(p_samr2); + + /* We don't want schannel for this test */ + status = dcerpc_binding_set_flags(b, 0, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_pipe_connect_b(tctx, &p_netlogon3, b, &ndr_table_netlogon, + credentials, tctx->ev, tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); + + torture_assert(tctx, !test_netlogon_ex_ops(p_netlogon3, tctx, credentials, creds), + "Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)"); + + /* Required because the previous call will mark the current context as having failed */ + tctx->last_result = TORTURE_OK; + tctx->last_reason = NULL; + + torture_assert(tctx, test_netlogon_ops(p_netlogon3, tctx, credentials, creds), + "Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth"); + + torture_leave_domain(tctx, join_ctx); + return true; +} + +/* + * Purpose of this test is to demonstrate that a netlogon server carefully deals + * with anonymous attempts to set passwords, in particular when the server + * enforces the use of schannel. This test makes most sense to be run in an + * environment where the netlogon server enforces use of schannel. + */ + +static bool test_schannel_anonymous_setPassword(struct torture_context *tctx, + uint32_t dcerpc_flags, + bool use2) +{ + NTSTATUS status, result; + const char *binding = torture_setting_string(tctx, "binding", NULL); + struct dcerpc_binding *b; + struct dcerpc_pipe *p = NULL; + struct cli_credentials *credentials; + bool ok = true; + + credentials = cli_credentials_init(NULL); + torture_assert(tctx, credentials != NULL, "Bad credentials"); + cli_credentials_set_anonymous(credentials); + + status = dcerpc_parse_binding(tctx, binding, &b); + torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); + + status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(tctx, status, "set flags"); + + status = dcerpc_pipe_connect_b(tctx, + &p, + b, + &ndr_table_netlogon, + credentials, + tctx->ev, + tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); + + if (use2) { + struct netr_ServerPasswordSet2 r = {}; + struct netr_Authenticator credential = {}; + struct netr_Authenticator return_authenticator = {}; + struct netr_CryptPassword new_password = {}; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = 0; + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + status = dcerpc_netr_ServerPasswordSet2_r(p->binding_handle, tctx, &r); + result = r.out.result; + } else { + struct netr_ServerPasswordSet r = {}; + struct netr_Authenticator credential = {}; + struct netr_Authenticator return_authenticator = {}; + struct samr_Password new_password = {}; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); + r.in.secure_channel_type = 0; + r.in.computer_name = TEST_MACHINE_NAME; + r.in.credential = &credential; + r.in.new_password = &new_password; + r.out.return_authenticator = &return_authenticator; + + status = dcerpc_netr_ServerPasswordSet_r(p->binding_handle, tctx, &r); + result = r.out.result; + } + + torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet failed"); + + if (NT_STATUS_IS_OK(result)) { + torture_fail(tctx, "unexpectedly received NT_STATUS_OK"); + } + + return ok; +} + + +/* + a schannel test suite + */ +bool torture_rpc_schannel(struct torture_context *torture) +{ + bool ret = true; + struct { + uint16_t acct_flags; + uint32_t dcerpc_flags; + } tests[] = { + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AUTO}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AUTO}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 }, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AES}, + { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AES }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AUTO}, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AUTO}, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128 }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AES }, + { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AES } + }; + int i; + + for (i=0;iev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); + + torture_comment(torture, "Opening second connection\n"); + status = dcerpc_pipe_connect_b(torture, &p2, b, &ndr_table_netlogon, + credentials2, torture->ev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); + + cli_credentials_set_netlogon_creds(credentials1, NULL); + cli_credentials_set_netlogon_creds(credentials2, NULL); + + torture_comment(torture, "Testing logon on pipe1\n"); + if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) + return false; + + torture_comment(torture, "Testing logon on pipe2\n"); + if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) + return false; + + torture_comment(torture, "Again on pipe1\n"); + if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) + return false; + + torture_comment(torture, "Again on pipe2\n"); + if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) + return false; + + torture_leave_domain(torture, join_ctx); + return true; +} + +struct torture_schannel_bench; + +struct torture_schannel_bench_conn { + struct torture_schannel_bench *s; + int index; + struct cli_credentials *wks_creds; + struct dcerpc_pipe *pipe; + struct netr_LogonSamLogonEx r; + struct netr_NetworkInfo ninfo; + TALLOC_CTX *tmp; + uint64_t total; + uint32_t count; +}; + +struct torture_schannel_bench { + struct torture_context *tctx; + bool progress; + int timelimit; + int nprocs; + int nconns; + struct torture_schannel_bench_conn *conns; + struct test_join *join_ctx1; + struct cli_credentials *wks_creds1; + struct test_join *join_ctx2; + struct cli_credentials *wks_creds2; + struct cli_credentials *user1_creds; + struct cli_credentials *user2_creds; + struct dcerpc_binding *b; + NTSTATUS error; + uint64_t total; + uint32_t count; + bool stopped; +}; + +#if 0 +static void torture_schannel_bench_connected(struct composite_context *c) +{ + struct torture_schannel_bench_conn *conn = + (struct torture_schannel_bench_conn *)c->async.private_data; + struct torture_schannel_bench *s = talloc_get_type(conn->s, + struct torture_schannel_bench); + + s->error = dcerpc_pipe_connect_b_recv(c, s->conns, &conn->pipe); + torture_comment(s->tctx, "conn[%u]: %s\n", conn->index, nt_errstr(s->error)); + if (NT_STATUS_IS_OK(s->error)) { + s->nconns++; + } +} +#endif + +static void torture_schannel_bench_recv(struct tevent_req *subreq); + +static bool torture_schannel_bench_start(struct torture_schannel_bench_conn *conn) +{ + struct torture_schannel_bench *s = conn->s; + NTSTATUS status; + DATA_BLOB names_blob, chal, lm_resp, nt_resp; + int flags = CLI_CRED_NTLM_AUTH; + struct tevent_req *subreq; + struct cli_credentials *user_creds; + + if (conn->total % 2) { + user_creds = s->user1_creds; + } else { + user_creds = s->user2_creds; + } + + if (lpcfg_client_lanman_auth(s->tctx->lp_ctx)) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (lpcfg_client_ntlmv2_auth(s->tctx->lp_ctx)) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + talloc_free(conn->tmp); + conn->tmp = talloc_new(s); + ZERO_STRUCT(conn->ninfo); + ZERO_STRUCT(conn->r); + + cli_credentials_get_ntlm_username_domain(user_creds, conn->tmp, + &conn->ninfo.identity_info.account_name.string, + &conn->ninfo.identity_info.domain_name.string); + + generate_random_buffer(conn->ninfo.challenge, + sizeof(conn->ninfo.challenge)); + chal = data_blob_const(conn->ninfo.challenge, + sizeof(conn->ninfo.challenge)); + + names_blob = NTLMv2_generate_names_blob(conn->tmp, + cli_credentials_get_workstation(conn->wks_creds), + cli_credentials_get_domain(conn->wks_creds)); + + status = cli_credentials_get_ntlm_response(user_creds, conn->tmp, + &flags, + chal, + NULL, /* server_timestamp */ + names_blob, + &lm_resp, &nt_resp, + NULL, NULL); + torture_assert_ntstatus_ok(s->tctx, status, + "cli_credentials_get_ntlm_response failed"); + + conn->ninfo.lm.data = lm_resp.data; + conn->ninfo.lm.length = lm_resp.length; + + conn->ninfo.nt.data = nt_resp.data; + conn->ninfo.nt.length = nt_resp.length; + + conn->ninfo.identity_info.parameter_control = 0; + conn->ninfo.identity_info.logon_id = 0; + conn->ninfo.identity_info.workstation.string = cli_credentials_get_workstation(conn->wks_creds); + + conn->r.in.server_name = talloc_asprintf(conn->tmp, "\\\\%s", dcerpc_server_name(conn->pipe)); + conn->r.in.computer_name = cli_credentials_get_workstation(conn->wks_creds); + conn->r.in.logon_level = NetlogonNetworkInformation; + conn->r.in.logon = talloc(conn->tmp, union netr_LogonLevel); + conn->r.in.logon->network = &conn->ninfo; + conn->r.in.flags = talloc(conn->tmp, uint32_t); + conn->r.in.validation_level = 2; + conn->r.out.validation = talloc(conn->tmp, union netr_Validation); + conn->r.out.authoritative = talloc(conn->tmp, uint8_t); + conn->r.out.flags = conn->r.in.flags; + + subreq = dcerpc_netr_LogonSamLogonEx_r_send(s, s->tctx->ev, + conn->pipe->binding_handle, + &conn->r); + torture_assert(s->tctx, subreq, "Failed to setup LogonSamLogonEx request"); + + tevent_req_set_callback(subreq, torture_schannel_bench_recv, conn); + + return true; +} + +static void torture_schannel_bench_recv(struct tevent_req *subreq) +{ + bool ret; + struct torture_schannel_bench_conn *conn = + (struct torture_schannel_bench_conn *)tevent_req_callback_data_void(subreq); + struct torture_schannel_bench *s = talloc_get_type(conn->s, + struct torture_schannel_bench); + + s->error = dcerpc_netr_LogonSamLogonEx_r_recv(subreq, subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(s->error)) { + return; + } + + conn->total++; + conn->count++; + + if (s->stopped) { + return; + } + + ret = torture_schannel_bench_start(conn); + if (!ret) { + s->error = NT_STATUS_INTERNAL_ERROR; + } +} + +/* + test multiple schannel connection in parallel + */ +bool torture_rpc_schannel_bench1(struct torture_context *torture) +{ + bool ret = true; + NTSTATUS status; + const char *binding = torture_setting_string(torture, "binding", NULL); + struct torture_schannel_bench *s; + struct timeval start; + struct timeval end; + int i; + const char *tmp; + + s = talloc_zero(torture, struct torture_schannel_bench); + s->tctx = torture; + s->progress = torture_setting_bool(torture, "progress", true); + s->timelimit = torture_setting_int(torture, "timelimit", 10); + s->nprocs = torture_setting_int(torture, "nprocs", 4); + s->conns = talloc_zero_array(s, struct torture_schannel_bench_conn, s->nprocs); + + s->user1_creds = cli_credentials_shallow_copy(s, + samba_cmdline_get_creds()); + tmp = torture_setting_string(s->tctx, "extra_user1", NULL); + if (tmp) { + cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED); + } + s->user2_creds = cli_credentials_shallow_copy(s, + samba_cmdline_get_creds()); + tmp = torture_setting_string(s->tctx, "extra_user2", NULL); + if (tmp) { + cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED); + } + + s->join_ctx1 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sb", TEST_MACHINE_NAME), + ACB_WSTRUST, &s->wks_creds1); + torture_assert(torture, s->join_ctx1 != NULL, + "Failed to join domain with acct_flags=ACB_WSTRUST"); + s->join_ctx2 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sc", TEST_MACHINE_NAME), + ACB_WSTRUST, &s->wks_creds2); + torture_assert(torture, s->join_ctx2 != NULL, + "Failed to join domain with acct_flags=ACB_WSTRUST"); + + cli_credentials_set_kerberos_state(s->wks_creds1, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + cli_credentials_set_kerberos_state(s->wks_creds2, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + + for (i=0; i < s->nprocs; i++) { + struct cli_credentials *wks = s->wks_creds1; + + if ((i % 2) && (torture_setting_bool(torture, "multijoin", false))) { + wks = s->wks_creds2; + } + + s->conns[i].s = s; + s->conns[i].index = i; + s->conns[i].wks_creds = cli_credentials_shallow_copy(s->conns, wks); + cli_credentials_set_netlogon_creds(s->conns[i].wks_creds, NULL); + } + + status = dcerpc_parse_binding(s, binding, &s->b); + torture_assert_ntstatus_ok(torture, status, "Bad binding string"); + + status = dcerpc_binding_set_flags(s->b, DCERPC_SCHANNEL | DCERPC_SIGN, + DCERPC_AUTH_OPTIONS); + torture_assert_ntstatus_ok(torture, status, "set flags"); + + torture_comment(torture, "Opening %d connections in parallel\n", s->nprocs); + for (i=0; i < s->nprocs; i++) { +#if 1 + s->error = dcerpc_pipe_connect_b(s->conns, &s->conns[i].pipe, s->b, + &ndr_table_netlogon, + s->conns[i].wks_creds, + torture->ev, torture->lp_ctx); + torture_assert_ntstatus_ok(torture, s->error, "Failed to connect with schannel"); +#else + /* + * This path doesn't work against windows, + * because of windows drops the connections + * which haven't reached a session setup yet + * + * The same as the reset on zero vc stuff. + */ + struct composite_context *c; + c = dcerpc_pipe_connect_b_send(s->conns, s->b, + &ndr_table_netlogon, + s->conns[i].wks_creds, + torture->ev, + torture->lp_ctx); + torture_assert(torture, c != NULL, "Failed to setup connect"); + c->async.fn = torture_schannel_bench_connected; + c->async.private_data = &s->conns[i]; + } + + while (NT_STATUS_IS_OK(s->error) && s->nprocs != s->nconns) { + int ev_ret = tevent_loop_once(torture->ev); + torture_assert(torture, ev_ret == 0, "tevent_loop_once failed"); +#endif + } + torture_assert_ntstatus_ok(torture, s->error, "Failed establish a connect"); + + /* + * Change the workstation password after establishing the netlogon + * schannel connections to prove that existing connections are not + * affected by a wks pwchange. + */ + + { + struct netr_ServerPasswordSet pwset; + char *password = generate_random_password(s->join_ctx1, 8, 255); + struct netlogon_creds_CredentialState *creds_state; + struct dcerpc_pipe *net_pipe; + struct netr_Authenticator credential, return_authenticator; + struct samr_Password new_password; + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + pwset.in.server_name = talloc_asprintf( + net_pipe, "\\\\%s", dcerpc_server_name(net_pipe)); + pwset.in.computer_name = + cli_credentials_get_workstation(s->wks_creds1); + pwset.in.account_name = talloc_asprintf( + net_pipe, "%s$", pwset.in.computer_name); + pwset.in.secure_channel_type = SEC_CHAN_WKSTA; + pwset.in.credential = &credential; + pwset.in.new_password = &new_password; + pwset.out.return_authenticator = &return_authenticator; + + E_md4hash(password, new_password.hash); + + creds_state = cli_credentials_get_netlogon_creds( + s->wks_creds1); + netlogon_creds_des_encrypt(creds_state, &new_password); + netlogon_creds_client_authenticator(creds_state, &credential); + + torture_assert_ntstatus_ok(torture, dcerpc_netr_ServerPasswordSet_r(net_pipe->binding_handle, torture, &pwset), + "ServerPasswordSet failed"); + torture_assert_ntstatus_ok(torture, pwset.out.result, + "ServerPasswordSet failed"); + + if (!netlogon_creds_client_check(creds_state, + &pwset.out.return_authenticator->cred)) { + torture_comment(torture, "Credential chaining failed\n"); + } + + cli_credentials_set_password(s->wks_creds1, password, + CRED_SPECIFIED); + + talloc_free(net_pipe); + + /* Just as a test, connect with the new creds */ + + cli_credentials_set_netlogon_creds(s->wks_creds1, NULL); + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + talloc_free(net_pipe); + } + + torture_comment(torture, "Start looping LogonSamLogonEx on %d connections for %d secs\n", + s->nprocs, s->timelimit); + for (i=0; i < s->nprocs; i++) { + ret = torture_schannel_bench_start(&s->conns[i]); + torture_assert(torture, ret, "Failed to setup LogonSamLogonEx"); + } + + start = timeval_current(); + end = timeval_add(&start, s->timelimit, 0); + + while (NT_STATUS_IS_OK(s->error) && !timeval_expired(&end)) { + int ev_ret = tevent_loop_once(torture->ev); + torture_assert(torture, ev_ret == 0, "tevent_loop_once failed"); + } + torture_assert_ntstatus_ok(torture, s->error, "Failed some request"); + s->stopped = true; + talloc_free(s->conns); + + for (i=0; i < s->nprocs; i++) { + s->total += s->conns[i].total; + } + + torture_comment(torture, + "Total ops[%llu] (%u ops/s)\n", + (unsigned long long)s->total, + (unsigned)s->total/s->timelimit); + + torture_leave_domain(torture, s->join_ctx1); + torture_leave_domain(torture, s->join_ctx2); + return true; +} diff --git a/source4/torture/rpc/session_key.c b/source4/torture/rpc/session_key.c new file mode 100644 index 0000000..96f9965 --- /dev/null +++ b/source4/torture/rpc/session_key.c @@ -0,0 +1,250 @@ +/* + Unix SMB/CIFS implementation. + test suite for lsa rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" + +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/torture_rpc.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" + +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool test_CreateSecret_basic(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct policy_handle *sec_handle) +{ + NTSTATUS status; + struct lsa_CreateSecret r; + struct lsa_SetSecret r3; + struct lsa_QuerySecret r4; + struct lsa_DATA_BUF buf1; + struct lsa_DATA_BUF_PTR bufp1; + DATA_BLOB enc_key; + DATA_BLOB session_key; + NTTIME old_mtime, new_mtime; + DATA_BLOB blob1; + const char *secret1 = "abcdef12345699qwerty"; + char *secret2; + char *secname; + struct dcerpc_binding_handle *b = p->binding_handle; + + secname = talloc_asprintf(tctx, "torturesecret-%08x", (unsigned int)random()); + + torture_comment(tctx, "Testing CreateSecret of %s\n", secname); + + init_lsa_String(&r.in.name, secname); + + r.in.handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.sec_handle = sec_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r), + "CreateSecret failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "CreateSecret failed"); + + status = dcerpc_fetch_session_key(p, &session_key); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_fetch_session_key failed"); + + enc_key = sess_encrypt_string(secret1, &session_key); + + r3.in.sec_handle = sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + torture_comment(tctx, "Testing SetSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3), + "SetSecret failed"); + torture_assert_ntstatus_ok(tctx, r3.out.result, "SetSecret failed"); + + r3.in.sec_handle = sec_handle; + r3.in.new_val = &buf1; + r3.in.old_val = NULL; + r3.in.new_val->data = enc_key.data; + r3.in.new_val->length = enc_key.length; + r3.in.new_val->size = enc_key.length; + + /* break the encrypted data */ + enc_key.data[0]++; + + torture_comment(tctx, "Testing SetSecret with broken key\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3), + "SetSecret failed"); + torture_assert_ntstatus_equal(tctx, r3.out.result, NT_STATUS_UNKNOWN_REVISION, + "SetSecret should have failed UNKNOWN_REVISION"); + + data_blob_free(&enc_key); + + ZERO_STRUCT(new_mtime); + ZERO_STRUCT(old_mtime); + + /* fetch the secret back again */ + r4.in.sec_handle = sec_handle; + r4.in.new_val = &bufp1; + r4.in.new_mtime = &new_mtime; + r4.in.old_val = NULL; + r4.in.old_mtime = NULL; + + bufp1.buf = NULL; + + torture_comment(tctx, "Testing QuerySecret\n"); + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r4), + "QuerySecret failed"); + torture_assert_ntstatus_ok(tctx, r4.out.result, "QuerySecret failed"); + if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) + torture_fail(tctx, "No secret buffer returned"); + blob1.data = r4.out.new_val->buf->data; + blob1.length = r4.out.new_val->buf->size; + + secret2 = sess_decrypt_string(tctx, &blob1, &session_key); + + torture_assert_str_equal(tctx, secret1, secret2, "Returned secret invalid"); + + return true; +} + +struct secret_settings { + uint32_t bindoptions; + bool keyexchange; + bool ntlm2; + bool lm_key; +}; + +static bool test_secrets(struct torture_context *torture, const void *_data) +{ + struct dcerpc_pipe *p; + struct policy_handle *handle; + struct dcerpc_binding *binding; + const struct secret_settings *settings = + (const struct secret_settings *)_data; + NTSTATUS status; + struct dcerpc_binding_handle *b; + struct policy_handle sec_handle = {0}; + bool ok; + + lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False"); + lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False"); + lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp_client:lm_key", settings->lm_key?"True":"False"); + + torture_assert_ntstatus_ok(torture, torture_rpc_binding(torture, &binding), + "Getting bindoptions"); + + status = dcerpc_binding_set_flags(binding, settings->bindoptions, 0); + torture_assert_ntstatus_ok(torture, status, "dcerpc_binding_set_flags"); + + status = dcerpc_pipe_connect_b(torture, &p, binding, + &ndr_table_lsarpc, + samba_cmdline_get_creds(), + torture->ev, + torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, "connect"); + b = p->binding_handle; + + if (!test_lsa_OpenPolicy2(b, torture, &handle)) { + talloc_free(p); + return false; + } + + torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server"); + + ok = test_CreateSecret_basic(p, torture, handle, &sec_handle); + + if (is_valid_policy_hnd(&sec_handle)) { + struct lsa_DeleteObject d; + + d.in.handle = &sec_handle; + d.out.handle = &sec_handle; + + status = dcerpc_lsa_DeleteObject_r(b, torture, &d); + if (!NT_STATUS_IS_OK(status) || + !NT_STATUS_IS_OK(d.out.result)) { + torture_warning(torture, + "Failed to delete secrets object"); + } + } + + talloc_free(p); + return ok; +} + +static struct torture_tcase *add_test(struct torture_suite *suite, uint32_t bindoptions, + bool keyexchange, bool ntlm2, bool lm_key) +{ + char *name = NULL; + struct secret_settings *settings; + + settings = talloc_zero(suite, struct secret_settings); + settings->bindoptions = bindoptions; + + if (bindoptions == DCERPC_PUSH_BIGENDIAN) + name = talloc_strdup(suite, "bigendian"); + else if (bindoptions == DCERPC_SEAL) + name = talloc_strdup(suite, "seal"); + else if (bindoptions == 0) + name = talloc_strdup(suite, "none"); + else + name = talloc_strdup(suite, "unknown"); + + name = talloc_asprintf_append_buffer(name, " keyexchange:%s", keyexchange?"yes":"no"); + settings->keyexchange = keyexchange; + + name = talloc_asprintf_append_buffer(name, " ntlm2:%s", ntlm2?"yes":"no"); + settings->ntlm2 = ntlm2; + + name = talloc_asprintf_append_buffer(name, " lm_key:%s", lm_key?"yes":"no"); + settings->lm_key = lm_key; + + return torture_suite_add_simple_tcase_const(suite, name, test_secrets, + settings); +} + +static const bool bool_vals[] = { true, false }; + +/* TEST session key correctness by pushing and pulling secrets */ +struct torture_suite *torture_rpc_lsa_secrets(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "lsa.secrets"); + int keyexchange, ntlm2, lm_key; + + for (keyexchange = 0; keyexchange < ARRAY_SIZE(bool_vals); keyexchange++) { + for (ntlm2 = 0; ntlm2 < ARRAY_SIZE(bool_vals); ntlm2++) { + for (lm_key = 0; lm_key < ARRAY_SIZE(bool_vals); lm_key++) { + add_test(suite, DCERPC_PUSH_BIGENDIAN, bool_vals[keyexchange], bool_vals[ntlm2], + bool_vals[lm_key]); + add_test(suite, DCERPC_SEAL, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]); + add_test(suite, 0, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]); + } + } + } + + return suite; +} diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c new file mode 100644 index 0000000..05a0aef --- /dev/null +++ b/source4/torture/rpc/spoolss.c @@ -0,0 +1,11705 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2009-2011,2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "lib/registry/registry.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/resolve/resolve.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/cmdline/cmdline.h" +#include "system/filesys.h" +#include "torture/ndr/ndr.h" +#include "torture/smb2/proto.h" + +#define TORTURE_WELLKNOWN_PRINTER "torture_wkn_printer" +#define TORTURE_PRINTER "torture_printer" +#define TORTURE_WELLKNOWN_PRINTER_EX "torture_wkn_printer_ex" +#define TORTURE_PRINTER_EX "torture_printer_ex" +#define TORTURE_DRIVER "torture_driver" +#define TORTURE_DRIVER_ADD "torture_driver_add" +#define TORTURE_DRIVER_EX "torture_driver_ex" +#define TORTURE_DRIVER_ADOBE "torture_driver_adobe" +#define TORTURE_DRIVER_EX_ADOBE "torture_driver_ex_adobe" +#define TORTURE_DRIVER_ADOBE_CUPSADDSMB "torture_driver_adobe_cupsaddsmb" +#define TORTURE_DRIVER_TIMESTAMPS "torture_driver_timestamps" +#define TORTURE_DRIVER_DELETER "torture_driver_deleter" +#define TORTURE_DRIVER_COPY_DIR "torture_driver_copy_from_directory" +#define TORTURE_DRIVER_DELETERIN "torture_driver_deleterin" +#define TORTURE_PRINTER_STATIC1 "print1" + +#define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print" +#define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers" +#define TOP_LEVEL_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print" +#define TOP_LEVEL_CONTROL_FORMS_KEY TOP_LEVEL_CONTROL_KEY "\\Forms" +#define TOP_LEVEL_CONTROL_PRINTERS_KEY TOP_LEVEL_CONTROL_KEY "\\Printers" +#define TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY TOP_LEVEL_CONTROL_KEY "\\Environments" + +struct test_spoolss_context { + struct dcerpc_pipe *spoolss_pipe; + + /* server environment */ + const char *environment; + + /* print server handle */ + struct policy_handle server_handle; + + /* for EnumPorts */ + uint32_t port_count[3]; + union spoolss_PortInfo *ports[3]; + + /* for EnumPrinterDrivers */ + uint32_t driver_count[9]; + union spoolss_DriverInfo *drivers[9]; + + /* for EnumMonitors */ + uint32_t monitor_count[3]; + union spoolss_MonitorInfo *monitors[3]; + + /* for EnumPrintProcessors */ + uint32_t print_processor_count[2]; + union spoolss_PrintProcessorInfo *print_processors[2]; + + /* for EnumPrinters */ + uint32_t printer_count[6]; + union spoolss_PrinterInfo *printers[6]; +}; + +struct torture_driver_context { + struct { + const char *driver_directory; + const char *environment; + } local; + struct { + const char *driver_directory; + const char *driver_upload_directory; + const char *environment; + } remote; + struct spoolss_AddDriverInfo8 info8; + bool ex; +}; + +struct torture_printer_context { + struct dcerpc_pipe *spoolss_pipe; + struct spoolss_SetPrinterInfo2 info2; + struct torture_driver_context driver; + bool ex; + bool wellknown; + bool added_driver; + bool have_driver; + struct spoolss_DeviceMode *devmode; + struct policy_handle handle; +}; + +static bool upload_printer_driver(struct torture_context *tctx, + const char *server_name, + struct torture_driver_context *d); +static bool remove_printer_driver(struct torture_context *tctx, + const char *server_name, + struct torture_driver_context *d); +static bool fillup_printserver_info(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct torture_driver_context *d); +static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir); + +#define COMPARE_STRING(tctx, c,r,e) \ + torture_assert_str_equal(tctx, c.e, r.e, "invalid value") + +/* not every compiler supports __typeof__() */ +#if (__GNUC__ >= 3) +#define _CHECK_FIELD_SIZE(c,r,e,type) do {\ + if (sizeof(__typeof__(c.e)) != sizeof(type)) { \ + torture_fail(tctx, #c "." #e "field is not " #type "\n"); \ + }\ + if (sizeof(__typeof__(r.e)) != sizeof(type)) { \ + torture_fail(tctx, #r "." #e "field is not " #type "\n"); \ + }\ +} while(0) +#else +#define _CHECK_FIELD_SIZE(c,r,e,type) do {} while(0) +#endif + +#define COMPARE_UINT32(tctx, c, r, e) do {\ + _CHECK_FIELD_SIZE(c, r, e, uint32_t); \ + torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \ +} while(0) + +#define COMPARE_UINT64(tctx, c, r, e) do {\ + _CHECK_FIELD_SIZE(c, r, e, uint64_t); \ + torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \ +} while(0) + + +#define COMPARE_NTTIME(tctx, c, r, e) do {\ + _CHECK_FIELD_SIZE(c, r, e, NTTIME); \ + torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \ +} while(0) + +#define COMPARE_STRING_ARRAY(tctx, c,r,e) do {\ + int __i; \ + if (!c.e && !r.e) { \ + break; \ + } \ + if (c.e && !r.e) { \ + torture_fail(tctx, #r "." #e " field is NULL and " #c "." #e " is not\n"); \ + } \ + if (!c.e && r.e) { \ + torture_fail(tctx, #c "." #e " field is NULL and " #r "." #e " is not\n"); \ + } \ + for (__i=0;c.e[__i] != NULL; __i++) { \ + torture_assert_str_equal(tctx, c.e[__i], r.e[__i], "invalid value"); \ + } \ +} while(0) + +#define CHECK_ALIGN(size, n) do {\ + if (size % n) {\ + torture_warning(tctx, "%d is *NOT* %d byte aligned, should be %d",\ + size, n, size + n - (size % n));\ + }\ +} while(0) + +#define DO_ROUND(size, n) (((size)+((n)-1)) & ~((n)-1)) + +#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, needed, align) do { \ + if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\ + uint32_t size = ndr_size_##fn##_info(tctx, level, count, info);\ + uint32_t round_size = DO_ROUND(size, align);\ + if (round_size != needed) {\ + torture_warning(tctx, __location__": "#fn" level %d (count: %d) got unexpected needed size: %d, we calculated: %d", level, count, needed, round_size);\ + CHECK_ALIGN(size, align);\ + }\ + }\ +} while(0) + +#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, needed, align) do { \ + if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\ + uint32_t size = ndr_size_##fn##_info(tctx, count, info);\ + uint32_t round_size = DO_ROUND(size, align);\ + if (round_size != needed) {\ + torture_warning(tctx, __location__": "#fn" (count: %d) got unexpected needed size: %d, we calculated: %d", count, needed, round_size);\ + CHECK_ALIGN(size, align);\ + }\ + }\ +} while(0) + +#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, needed, align) do { \ + if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\ + uint32_t size = ndr_size_##fn(info, level, 0);\ + uint32_t round_size = DO_ROUND(size, align);\ + if (round_size != needed) {\ + torture_warning(tctx, __location__": "#fn" level %d got unexpected needed size: %d, we calculated: %d", level, needed, round_size);\ + CHECK_ALIGN(size, align);\ + }\ + }\ +} while(0) + +static bool PrinterInfo_to_SetPrinterInfo(struct torture_context *tctx, + const union spoolss_PrinterInfo *i, + uint32_t level, + union spoolss_SetPrinterInfo *s) +{ + switch (level) { + case 0: + s->info0 = talloc(tctx, struct spoolss_SetPrinterInfo0); + break; + case 2: + s->info2 = talloc(tctx, struct spoolss_SetPrinterInfo2); + s->info2->servername = i->info2.servername; + s->info2->printername = i->info2.printername; + s->info2->sharename = i->info2.sharename; + s->info2->portname = i->info2.portname; + s->info2->drivername = i->info2.drivername; + s->info2->comment = i->info2.comment; + s->info2->location = i->info2.location; + s->info2->devmode_ptr = 0; + s->info2->sepfile = i->info2.sepfile; + s->info2->printprocessor = i->info2.printprocessor; + s->info2->datatype = i->info2.datatype; + s->info2->parameters = i->info2.parameters; + s->info2->secdesc_ptr = 0; + s->info2->attributes = i->info2.attributes; + s->info2->priority = i->info2.priority; + s->info2->defaultpriority = i->info2.defaultpriority; + s->info2->starttime = i->info2.starttime; + s->info2->untiltime = i->info2.untiltime; + s->info2->status = i->info2.status; + s->info2->cjobs = i->info2.cjobs; + s->info2->averageppm = i->info2.averageppm; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + default: + return false; + } + + return true; +} + +static bool test_OpenPrinter_server(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *server_handle) +{ + NTSTATUS status; + struct spoolss_OpenPrinter op; + struct dcerpc_binding_handle *b = p->binding_handle; + + op.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode= NULL; + op.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + op.out.handle = server_handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername); + + status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &op); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_OpenPrinter failed"); + torture_assert_werr_ok(tctx, op.out.result, "dcerpc_spoolss_OpenPrinter failed"); + + return true; +} + +static bool test_EnumPorts(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + struct spoolss_EnumPorts r; + uint16_t levels[] = { 1, 2 }; + int i, j; + + for (i=0;iport_count[level] = count; + ctx->ports[level] = info; + } + + for (i=1;iport_count[level], ctx->port_count[old_level], + "EnumPorts invalid value"); + } + /* if the array sizes are not the same we would maybe segfault in the following code */ + + for (i=0;iport_count[level];j++) { + union spoolss_PortInfo *cur = &ctx->ports[level][j]; + union spoolss_PortInfo *ref = &ctx->ports[2][j]; + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info2, port_name); + break; + case 2: + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_GetPrintProcessorDirectory(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + NTSTATUS status; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_GetPrintProcessorDirectory r; + struct { + uint16_t level; + const char *server; + } levels[] = {{ + .level = 1, + .server = NULL + },{ + .level = 1, + .server = "" + },{ + .level = 78, + .server = "" + },{ + .level = 1, + .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)) + },{ + .level = 1024, + .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)) + } + }; + int i; + uint32_t needed; + + for (i=0;ienvironment; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_comment(tctx, "Testing GetPrintProcessorDirectory level %u\n", r.in.level); + + status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_GetPrintProcessorDirectory failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "GetPrintProcessorDirectory unexpected return code"); + + blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrintProcessorDirectory failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, needed, 2); + } + + return true; +} + + +static bool test_GetPrinterDriverDirectory(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + NTSTATUS status; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_GetPrinterDriverDirectory r; + struct { + uint16_t level; + const char *server; + } levels[] = {{ + .level = 1, + .server = NULL + },{ + .level = 1, + .server = "" + },{ + .level = 78, + .server = "" + },{ + .level = 1, + .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)) + },{ + .level = 1024, + .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)) + } + }; + int i; + uint32_t needed; + + for (i=0;ienvironment; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level); + + status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_GetPrinterDriverDirectory failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER, + "GetPrinterDriverDirectory unexpected return code"); + + blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, needed, 2); + } + + return true; +} + +static bool test_EnumPrinterDrivers_buffers(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + const char *environment, + uint32_t level, + uint32_t offered, + uint32_t *count_p, + union spoolss_DriverInfo **info_p) +{ + struct spoolss_EnumPrinterDrivers r; + uint32_t needed; + uint32_t count; + union spoolss_DriverInfo *info; + DATA_BLOB buffer; + + if (offered > 0) { + buffer = data_blob_talloc_zero(tctx, offered); + } + + r.in.server = server_name; + r.in.environment = environment; + r.in.level = level; + r.in.buffer = offered ? &buffer : NULL; + r.in.offered = offered; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPrinterDrivers(%s) level %u, offered: %u\n", + r.in.environment, r.in.level, r.in.offered); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r), + "EnumPrinterDrivers failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r), + "EnumPrinterDrivers failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "EnumPrinterDrivers failed"); + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, needed, 4); + + return true; + +} + + +static bool test_EnumPrinterDrivers_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + const char *environment, + uint32_t level, + uint32_t *count_p, + union spoolss_DriverInfo **info_p) +{ + return test_EnumPrinterDrivers_buffers(tctx, b, server_name, + environment, level, 0, + count_p, info_p); +} + +static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + const char *environment, + uint32_t level, + const char *driver_name, + union spoolss_DriverInfo *info_p) +{ + uint32_t count; + union spoolss_DriverInfo *info; + int i; + const char *environment_ret = NULL; + + torture_assert(tctx, + test_EnumPrinterDrivers_args(tctx, b, server_name, environment, level, &count, &info), + "failed to enumerate printer drivers"); + + for (i=0; i < count; i++) { + const char *driver_name_ret = ""; + switch (level) { + case 1: + driver_name_ret = info[i].info1.driver_name; + break; + case 2: + driver_name_ret = info[i].info2.driver_name; + environment_ret = info[i].info2.architecture; + break; + case 3: + driver_name_ret = info[i].info3.driver_name; + environment_ret = info[i].info3.architecture; + break; + case 4: + driver_name_ret = info[i].info4.driver_name; + environment_ret = info[i].info4.architecture; + break; + case 5: + driver_name_ret = info[i].info5.driver_name; + environment_ret = info[i].info5.architecture; + break; + case 6: + driver_name_ret = info[i].info6.driver_name; + environment_ret = info[i].info6.architecture; + break; + case 7: + driver_name_ret = info[i].info7.driver_name; + break; + case 8: + driver_name_ret = info[i].info8.driver_name; + environment_ret = info[i].info8.architecture; + break; + default: + break; + } + if (environment_ret) { + torture_assert_str_equal(tctx, environment, environment_ret, "architecture mismatch"); + } + if (strequal(driver_name, driver_name_ret)) { + if (info_p) { + *info_p = info[i]; + } + return true; + } + } + + return false; +} + +static bool test_EnumPrinterDrivers(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + uint16_t levels[] = { 1, 2, 3, 4, 5, 6, 8 }; + uint16_t buffer_sizes[] = { 0, 1024, 6040, 0xffff }; + int i, j, a; + + /* FIXME: gd, come back and fix "" as server, and handle + * priority of returned error codes in torture test and samba 3 + * server */ + const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + const char *environments[2]; + + environments[0] = SPOOLSS_ARCHITECTURE_ALL; + environments[1] = ctx->environment; + + for (a=0;adriver_count[level] = count; + ctx->drivers[level] = info; + } + + for (i=1;idriver_count[level], ctx->driver_count[old_level], + "EnumPrinterDrivers invalid value"); + } + + for (i=0;idriver_count[level - 1];j++) { + union spoolss_DriverInfo *cur = &ctx->drivers[level - 1][j]; + union spoolss_DriverInfo *ref = &ctx->drivers[8][j]; + + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info8, driver_name); + break; + case 2: + COMPARE_UINT32(tctx, cur->info2, ref->info8, version); + COMPARE_STRING(tctx, cur->info2, ref->info8, driver_name); + COMPARE_STRING(tctx, cur->info2, ref->info8, architecture); + COMPARE_STRING(tctx, cur->info2, ref->info8, driver_path); + COMPARE_STRING(tctx, cur->info2, ref->info8, data_file); + COMPARE_STRING(tctx, cur->info2, ref->info8, config_file); + break; + case 3: + COMPARE_UINT32(tctx, cur->info3, ref->info8, version); + COMPARE_STRING(tctx, cur->info3, ref->info8, driver_name); + COMPARE_STRING(tctx, cur->info3, ref->info8, architecture); + COMPARE_STRING(tctx, cur->info3, ref->info8, driver_path); + COMPARE_STRING(tctx, cur->info3, ref->info8, data_file); + COMPARE_STRING(tctx, cur->info3, ref->info8, config_file); + COMPARE_STRING(tctx, cur->info3, ref->info8, help_file); + COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info8, dependent_files); + COMPARE_STRING(tctx, cur->info3, ref->info8, monitor_name); + COMPARE_STRING(tctx, cur->info3, ref->info8, default_datatype); + break; + case 4: + COMPARE_UINT32(tctx, cur->info4, ref->info8, version); + COMPARE_STRING(tctx, cur->info4, ref->info8, driver_name); + COMPARE_STRING(tctx, cur->info4, ref->info8, architecture); + COMPARE_STRING(tctx, cur->info4, ref->info8, driver_path); + COMPARE_STRING(tctx, cur->info4, ref->info8, data_file); + COMPARE_STRING(tctx, cur->info4, ref->info8, config_file); + COMPARE_STRING(tctx, cur->info4, ref->info8, help_file); + COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, dependent_files); + COMPARE_STRING(tctx, cur->info4, ref->info8, monitor_name); + COMPARE_STRING(tctx, cur->info4, ref->info8, default_datatype); + COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, previous_names); + break; + case 5: + COMPARE_UINT32(tctx, cur->info5, ref->info8, version); + COMPARE_STRING(tctx, cur->info5, ref->info8, driver_name); + COMPARE_STRING(tctx, cur->info5, ref->info8, architecture); + COMPARE_STRING(tctx, cur->info5, ref->info8, driver_path); + COMPARE_STRING(tctx, cur->info5, ref->info8, data_file); + COMPARE_STRING(tctx, cur->info5, ref->info8, config_file); + /*COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_attributes);*/ + /*COMPARE_UINT32(tctx, cur->info5, ref->info8, config_version);*/ + /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_version); */ + break; + case 6: + COMPARE_UINT32(tctx, cur->info6, ref->info8, version); + COMPARE_STRING(tctx, cur->info6, ref->info8, driver_name); + COMPARE_STRING(tctx, cur->info6, ref->info8, architecture); + COMPARE_STRING(tctx, cur->info6, ref->info8, driver_path); + COMPARE_STRING(tctx, cur->info6, ref->info8, data_file); + COMPARE_STRING(tctx, cur->info6, ref->info8, config_file); + COMPARE_STRING(tctx, cur->info6, ref->info8, help_file); + COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, dependent_files); + COMPARE_STRING(tctx, cur->info6, ref->info8, monitor_name); + COMPARE_STRING(tctx, cur->info6, ref->info8, default_datatype); + COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, previous_names); + COMPARE_NTTIME(tctx, cur->info6, ref->info8, driver_date); + COMPARE_UINT64(tctx, cur->info6, ref->info8, driver_version); + COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_name); + COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_url); + COMPARE_STRING(tctx, cur->info6, ref->info8, hardware_id); + COMPARE_STRING(tctx, cur->info6, ref->info8, provider); + break; + case 8: + /* level 8 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + } + + return true; +} + +static bool test_EnumMonitors(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + struct spoolss_EnumMonitors r; + uint16_t levels[] = { 1, 2 }; + int i, j; + + for (i=0;imonitor_count[level] = count; + ctx->monitors[level] = info; + } + + for (i=1;imonitor_count[level], ctx->monitor_count[old_level], + "EnumMonitors invalid value"); + } + + for (i=0;imonitor_count[level];j++) { + union spoolss_MonitorInfo *cur = &ctx->monitors[level][j]; + union spoolss_MonitorInfo *ref = &ctx->monitors[2][j]; + switch (level) { + case 1: + COMPARE_STRING(tctx, cur->info1, ref->info2, monitor_name); + break; + case 2: + torture_assert_str_equal(tctx, ref->info2.environment, ctx->environment, "invalid environment"); + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + } + } + } + + return true; +} + +static bool test_EnumPrintProcessors_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *environment, + uint32_t level, + uint32_t *count_p, + union spoolss_PrintProcessorInfo **info_p, + WERROR expected_result) +{ + struct spoolss_EnumPrintProcessors r; + DATA_BLOB blob; + uint32_t needed; + uint32_t count; + union spoolss_PrintProcessorInfo *info; + + r.in.servername = ""; + r.in.environment = environment; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPrintProcessors(%s) level %u\n", + r.in.environment, r.in.level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r), + "EnumPrintProcessors failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r), + "EnumPrintProcessors failed"); + } + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "EnumPrintProcessors failed"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, level, count, needed, 4); + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + return true; +} + +static bool test_EnumPrintProcessors(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + uint16_t levels[] = {0, 1, 2, 3, 32, 256 }; + uint16_t ok[] = {0, 1, 0, 0, 0, 0 }; + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_EnumPrintProcessors_level(tctx, b, "phantasy", 1, NULL, NULL, WERR_INVALID_ENVIRONMENT), + "test_EnumPrintProcessors_level failed"); + + for (i=0;ienvironment, levels[i], &count, &info, expected_result), + "test_EnumPrintProcessors_level failed"); + } + + return true; +} + +static bool test_EnumPrintProcessorDataTypes_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *print_processor_name, + uint32_t level, + uint32_t *count_p, + union spoolss_PrintProcDataTypesInfo **info_p, + WERROR expected_result) +{ + struct spoolss_EnumPrintProcessorDataTypes r; + DATA_BLOB blob; + uint32_t needed; + uint32_t count; + union spoolss_PrintProcDataTypesInfo *info; + + r.in.servername = ""; + r.in.print_processor_name = print_processor_name; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPrintProcessorDataTypes(%s) level %u\n", + r.in.print_processor_name, r.in.level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r), + "EnumPrintProcessorDataTypes failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r), + "EnumPrintProcessorDataTypes failed"); + } + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "EnumPrintProcessorDataTypes failed"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessorDataTypes, info, level, count, needed, 4); + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + return true; +} + +static bool test_EnumPrintProcessorDataTypes(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + uint16_t levels[] = {0, 1, 2, 3, 32, 256 }; + uint16_t ok[] = {0, 1, 0, 0, 0, 0 }; + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_EnumPrintProcessorDataTypes_level(tctx, b, NULL, 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR), + "test_EnumPrintProcessorDataTypes_level failed"); + + torture_assert(tctx, + test_EnumPrintProcessorDataTypes_level(tctx, b, "nonexisting", 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR), + "test_EnumPrintProcessorDataTypes_level failed"); + + for (i=0;ienvironment, 1, &count, &info, WERR_OK), + "test_EnumPrintProcessors_level failed"); + + for (i=0; i < count; i++) { + torture_assert(tctx, + test_EnumPrintProcessorDataTypes_level(tctx, b, info[i].info1.print_processor_name, 1, NULL, NULL, WERR_OK), + "test_EnumPrintProcessorDataTypes_level failed"); + } + } + + + return true; +} + +static bool test_EnumPrinters(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_EnumPrinters r; + NTSTATUS status; + uint16_t levels[] = { 0, 1, 2, 4, 5 }; + int i, j; + + for (i=0;iprinter_count[level] = count; + ctx->printers[level] = info; + } + + for (i=1;iprinter_count[level], ctx->printer_count[old_level], + "EnumPrinters invalid value"); + } + + for (i=0;iprinter_count[level];j++) { + union spoolss_PrinterInfo *cur = &ctx->printers[level][j]; + union spoolss_PrinterInfo *ref = &ctx->printers[2][j]; + switch (level) { + case 0: + COMPARE_STRING(tctx, cur->info0, ref->info2, printername); + COMPARE_STRING(tctx, cur->info0, ref->info2, servername); + COMPARE_UINT32(tctx, cur->info0, ref->info2, cjobs); + /*COMPARE_UINT32(tctx, cur->info0, ref->info2, total_jobs); + COMPARE_UINT32(tctx, cur->info0, ref->info2, total_bytes); + COMPARE_SPOOLSS_TIME(cur->info0, ref->info2, spoolss_Time time); + COMPARE_UINT32(tctx, cur->info0, ref->info2, global_counter); + COMPARE_UINT32(tctx, cur->info0, ref->info2, total_pages); + COMPARE_UINT32(tctx, cur->info0, ref->info2, version); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown10); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown11); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown12); + COMPARE_UINT32(tctx, cur->info0, ref->info2, session_counter); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown14); + COMPARE_UINT32(tctx, cur->info0, ref->info2, printer_errors); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown16); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown17); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown18); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown19); + COMPARE_UINT32(tctx, cur->info0, ref->info2, change_id); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown21);*/ + COMPARE_UINT32(tctx, cur->info0, ref->info2, status); + /*COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown23); + COMPARE_UINT32(tctx, cur->info0, ref->info2, c_setprinter); + COMPARE_UINT16(cur->info0, ref->info2, unknown25); + COMPARE_UINT16(cur->info0, ref->info2, unknown26); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown27); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown28); + COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown29);*/ + break; + case 1: + /*COMPARE_UINT32(tctx, cur->info1, ref->info2, flags);*/ + /*COMPARE_STRING(tctx, cur->info1, ref->info2, name);*/ + /*COMPARE_STRING(tctx, cur->info1, ref->info2, description);*/ + COMPARE_STRING(tctx, cur->info1, ref->info2, comment); + break; + case 2: + /* level 2 is our reference, and it makes no sense to compare it to itself */ + break; + case 4: + COMPARE_STRING(tctx, cur->info4, ref->info2, printername); + COMPARE_STRING(tctx, cur->info4, ref->info2, servername); + COMPARE_UINT32(tctx, cur->info4, ref->info2, attributes); + break; + case 5: + COMPARE_STRING(tctx, cur->info5, ref->info2, printername); + COMPARE_STRING(tctx, cur->info5, ref->info2, portname); + COMPARE_UINT32(tctx, cur->info5, ref->info2, attributes); + /*COMPARE_UINT32(tctx, cur->info5, ref->info2, device_not_selected_timeout); + COMPARE_UINT32(tctx, cur->info5, ref->info2, transmission_retry_timeout);*/ + break; + } + } + } + + /* TODO: + * - verify that the port of a printer was in the list returned by EnumPorts + */ + + return true; +} + +static bool test_GetPrinterDriver2(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *driver_name, + const char *environment); + +bool test_GetPrinter_level_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t level, + WERROR expected_werror, + union spoolss_PrinterInfo *info) +{ + struct spoolss_GetPrinter r; + uint32_t needed; + + r.in.handle = handle; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r), + "GetPrinter failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r), + "GetPrinter failed"); + } + + torture_assert_werr_equal(tctx, + r.out.result, expected_werror, + "GetPrinter failed"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, needed, 4); + + if (info && r.out.info) { + *info = *r.out.info; + } + + return true; +} + +bool test_GetPrinter_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t level, + union spoolss_PrinterInfo *info) +{ + return test_GetPrinter_level_exp(tctx, b, handle, level, WERR_OK, info); +} + +static bool test_GetPrinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *environment) +{ + uint32_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + int i; + + for (i=0;ilevel); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), + "failed to call SetPrinter"); + torture_assert(tctx, (W_ERROR_EQUAL(r.out.result, WERR_OK) + || W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)), + "SetPrinter failed"); + + return true; +} + +static bool test_SetPrinter_errors(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct spoolss_SetPrinter r; + uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int i; + + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + + info_ctr.level = 0; + info_ctr.info.info0 = NULL; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + r.in.handle = handle; + r.in.info_ctr = &info_ctr; + r.in.devmode_ctr = &devmode_ctr; + r.in.secdesc_ctr = &secdesc_ctr; + r.in.command = 0; + + torture_comment(tctx, "Testing SetPrinter all zero\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), + "failed to call SetPrinter"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "failed to call SetPrinter"); + + again: + for (i=0; i < ARRAY_SIZE(levels); i++) { + + struct spoolss_SetPrinterInfo0 info0; + struct spoolss_SetPrinterInfo1 info1; + struct spoolss_SetPrinterInfo2 info2; + struct spoolss_SetPrinterInfo3 info3; + struct spoolss_SetPrinterInfo4 info4; + struct spoolss_SetPrinterInfo5 info5; + struct spoolss_SetPrinterInfo6 info6; + struct spoolss_SetPrinterInfo7 info7; + struct spoolss_SetPrinterInfo8 info8; + struct spoolss_SetPrinterInfo9 info9; + + + info_ctr.level = levels[i]; + switch (levels[i]) { + case 0: + ZERO_STRUCT(info0); + info_ctr.info.info0 = &info0; + break; + case 1: + ZERO_STRUCT(info1); + info_ctr.info.info1 = &info1; + break; + case 2: + ZERO_STRUCT(info2); + info_ctr.info.info2 = &info2; + break; + case 3: + ZERO_STRUCT(info3); + info_ctr.info.info3 = &info3; + break; + case 4: + ZERO_STRUCT(info4); + info_ctr.info.info4 = &info4; + break; + case 5: + ZERO_STRUCT(info5); + info_ctr.info.info5 = &info5; + break; + case 6: + ZERO_STRUCT(info6); + info_ctr.info.info6 = &info6; + break; + case 7: + ZERO_STRUCT(info7); + info_ctr.info.info7 = &info7; + break; + case 8: + ZERO_STRUCT(info8); + info_ctr.info.info8 = &info8; + break; + case 9: + ZERO_STRUCT(info9); + info_ctr.info.info9 = &info9; + break; + } + + torture_comment(tctx, "Testing SetPrinter level %d, command %d\n", + info_ctr.level, r.in.command); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), + "failed to call SetPrinter"); + + switch (r.in.command) { + case SPOOLSS_PRINTER_CONTROL_UNPAUSE: /* 0 */ + /* is ignored for all levels other then 0 */ + if (info_ctr.level > 0) { + /* ignored then */ + break; + } + + FALL_THROUGH; + case SPOOLSS_PRINTER_CONTROL_PAUSE: /* 1 */ + case SPOOLSS_PRINTER_CONTROL_RESUME: /* 2 */ + case SPOOLSS_PRINTER_CONTROL_PURGE: /* 3 */ + if (info_ctr.level > 0) { + /* is invalid for all levels other then 0 */ + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND, + "unexpected error code returned"); + continue; + } else { + torture_assert_werr_ok(tctx, r.out.result, + "failed to call SetPrinter with non 0 command"); + continue; + } + break; + + case SPOOLSS_PRINTER_CONTROL_SET_STATUS: /* 4 */ + /* FIXME: gd needs further investigation */ + default: + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND, + "unexpected error code returned"); + continue; + } + + switch (info_ctr.level) { + case 1: + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, + "unexpected error code returned"); + break; + case 2: + torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_PRINTER_DRIVER, + "unexpected error code returned"); + break; + case 3: + case 4: + case 5: + case 7: + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "unexpected error code returned"); + break; + case 9: + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "unexpected error code returned"); + break; + default: + torture_assert_werr_ok(tctx, r.out.result, + "failed to call SetPrinter"); + break; + } + } + + if (r.in.command < 5) { + r.in.command++; + goto again; + } + + return true; +} + +static void clear_info2(struct spoolss_SetPrinterInfoCtr *r) +{ + if ((r->level == 2) && (r->info.info2)) { + r->info.info2->secdesc_ptr = 0; + r->info.info2->devmode_ptr = 0; + } +} + +static bool test_PrinterInfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_SetPrinter s; + struct spoolss_GetPrinter q; + struct spoolss_GetPrinter q0; + struct spoolss_SetPrinterInfoCtr info_ctr; + union spoolss_PrinterInfo info; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + uint32_t needed = 0; + DATA_BLOB blob = data_blob_null; + bool ret = true; + int i; + + uint32_t status_list[] = { + /* these do not stick + PRINTER_STATUS_PAUSED, + PRINTER_STATUS_ERROR, + PRINTER_STATUS_PENDING_DELETION, */ + PRINTER_STATUS_PAPER_JAM, + PRINTER_STATUS_PAPER_OUT, + PRINTER_STATUS_MANUAL_FEED, + PRINTER_STATUS_PAPER_PROBLEM, + PRINTER_STATUS_OFFLINE, + PRINTER_STATUS_IO_ACTIVE, + PRINTER_STATUS_BUSY, + PRINTER_STATUS_PRINTING, + PRINTER_STATUS_OUTPUT_BIN_FULL, + PRINTER_STATUS_NOT_AVAILABLE, + PRINTER_STATUS_WAITING, + PRINTER_STATUS_PROCESSING, + PRINTER_STATUS_INITIALIZING, + PRINTER_STATUS_WARMING_UP, + PRINTER_STATUS_TONER_LOW, + PRINTER_STATUS_NO_TONER, + PRINTER_STATUS_PAGE_PUNT, + PRINTER_STATUS_USER_INTERVENTION, + PRINTER_STATUS_OUT_OF_MEMORY, + PRINTER_STATUS_DOOR_OPEN, + PRINTER_STATUS_SERVER_UNKNOWN, + PRINTER_STATUS_POWER_SAVE, + /* these do not stick + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000 */ + }; + uint32_t default_attribute = PRINTER_ATTRIBUTE_LOCAL; + uint32_t attribute_list[] = { + PRINTER_ATTRIBUTE_QUEUED, + /* fails with WERR_INVALID_DATATYPE: + PRINTER_ATTRIBUTE_DIRECT, */ + /* does not stick + PRINTER_ATTRIBUTE_DEFAULT, */ + PRINTER_ATTRIBUTE_SHARED, + /* does not stick + PRINTER_ATTRIBUTE_NETWORK, */ + PRINTER_ATTRIBUTE_HIDDEN, + PRINTER_ATTRIBUTE_LOCAL, + PRINTER_ATTRIBUTE_ENABLE_DEVQ, + PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS, + PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST, + PRINTER_ATTRIBUTE_WORK_OFFLINE, + /* does not stick + PRINTER_ATTRIBUTE_ENABLE_BIDI, */ + /* fails with WERR_INVALID_DATATYPE: + PRINTER_ATTRIBUTE_RAW_ONLY, */ + /* these do not stick + PRINTER_ATTRIBUTE_PUBLISHED, + PRINTER_ATTRIBUTE_FAX, + PRINTER_ATTRIBUTE_TS, + 0x00010000, + 0x00020000, + 0x00040000, + 0x00080000, + 0x00100000, + 0x00200000, + 0x00400000, + 0x00800000, + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000 */ + }; + + torture_skip(tctx, "Printer Info test is currently broken, skipping"); + + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + s.in.handle = handle; + s.in.command = 0; + s.in.info_ctr = &info_ctr; + s.in.devmode_ctr = &devmode_ctr; + s.in.secdesc_ctr = &secdesc_ctr; + + q.in.handle = handle; + q.out.info = &info; + q0 = q; + +#define TESTGETCALL(call, r, needed, blob) \ + r.in.buffer = NULL; \ + r.in.offered = 0;\ + r.out.needed = &needed; \ + status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, #call " level %u failed - %s (%s)\n", \ + r.in.level, nt_errstr(status), __location__); \ + ret = false; \ + break; \ + }\ + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {\ + blob = data_blob_talloc_zero(tctx, needed); \ + r.in.buffer = &blob; \ + r.in.offered = needed; \ + }\ + status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, #call " level %u failed - %s (%s)\n", \ + r.in.level, nt_errstr(status), __location__); \ + ret = false; \ + break; \ + } \ + if (!W_ERROR_IS_OK(r.out.result)) { \ + torture_comment(tctx, #call " level %u failed - %s (%s)\n", \ + r.in.level, win_errstr(r.out.result), __location__); \ + ret = false; \ + break; \ + } + + +#define TESTSETCALL_EXP(call, r, err) \ + clear_info2(&info_ctr);\ + status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, #call " level %u failed - %s (%s)\n", \ + r.in.info_ctr->level, nt_errstr(status), __location__); \ + ret = false; \ + break; \ + } \ + if (!W_ERROR_IS_OK(err)) { \ + if (!W_ERROR_EQUAL(err, r.out.result)) { \ + torture_comment(tctx, #call " level %u failed - %s, expected %s (%s)\n", \ + r.in.info_ctr->level, win_errstr(r.out.result), win_errstr(err), __location__); \ + ret = false; \ + } \ + break; \ + } \ + if (!W_ERROR_IS_OK(r.out.result)) { \ + torture_comment(tctx, #call " level %u failed - %s (%s)\n", \ + r.in.info_ctr->level, win_errstr(r.out.result), __location__); \ + ret = false; \ + break; \ + } + +#define TESTSETCALL(call, r) \ + TESTSETCALL_EXP(call, r, WERR_OK) + +#define STRING_EQUAL(s1, s2, field) \ + if ((s1 && !s2) || (s2 && !s1) || strcmp(s1, s2)) { \ + torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \ + #field, s2, __location__); \ + ret = false; \ + break; \ + } + +#define MEM_EQUAL(s1, s2, length, field) \ + if ((s1 && !s2) || (s2 && !s1) || memcmp(s1, s2, length)) { \ + torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \ + #field, (const char *)s2, __location__); \ + ret = false; \ + break; \ + } + +#define INT_EQUAL(i1, i2, field) \ + if (i1 != i2) { \ + torture_comment(tctx, "Failed to set %s to 0x%llx - got 0x%llx (%s)\n", \ + #field, (unsigned long long)i2, (unsigned long long)i1, __location__); \ + ret = false; \ + break; \ + } + +#define SD_EQUAL(sd1, sd2, field) \ + if (!security_descriptor_equal(sd1, sd2)) { \ + torture_comment(tctx, "Failed to set %s (%s)\n", \ + #field, __location__); \ + ret = false; \ + break; \ + } + +#define TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, lvl1, field1, lvl2, field2, value, err) do { \ + void *p; \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + info_ctr.level = lvl1; \ + p = (void *)&q.out.info->info ## lvl1; \ + info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \ + info_ctr.info.info ## lvl1->field1 = value;\ + TESTSETCALL_EXP(SetPrinter, s, err) \ + info_ctr.info.info ## lvl1->field1 = ""; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + info_ctr.info.info ## lvl1->field1 = value; \ + STRING_EQUAL(info_ctr.info.info ## lvl1->field1, value, field1); \ + q.in.level = lvl2; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + p = (void *)&q.out.info->info ## lvl2; \ + info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \ + STRING_EQUAL(info_ctr.info.info ## lvl2->field2, value, field2); \ + } while (0) + +#define TEST_PRINTERINFO_STRING(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \ + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, lvl1, field1, lvl2, field2, value, WERR_OK); \ + } while (0); + +#define TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, exp_value) do { \ + void *p; \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + info_ctr.level = lvl1; \ + p = (void *)&q.out.info->info ## lvl1; \ + info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \ + info_ctr.info.info ## lvl1->field1 = value; \ + TESTSETCALL(SetPrinter, s) \ + info_ctr.info.info ## lvl1->field1 = 0; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + p = (void *)&q.out.info->info ## lvl1; \ + info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \ + INT_EQUAL(info_ctr.info.info ## lvl1->field1, exp_value, field1); \ + q.in.level = lvl2; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + p = (void *)&q.out.info->info ## lvl2; \ + info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \ + INT_EQUAL(info_ctr.info.info ## lvl2->field2, exp_value, field1); \ + } while (0) + +#define TEST_PRINTERINFO_INT(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \ + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value); \ + } while (0) + + q0.in.level = 0; + do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0); + + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, comment, 1, comment, "xx2-1 comment"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, comment, 2, comment, "xx2-2 comment"); + + /* level 0 printername does not stick */ +/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 0, printername, "xx2-0 printer"); */ + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 1, name, "xx2-1 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 2, printername, "xx2-2 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 4, printername, "xx2-4 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 5, printername, "xx2-5 printer"); +/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 0, printername, "xx4-0 printer"); */ + TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 1, name, "xx4-1 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 2, printername, "xx4-2 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 4, printername, "xx4-4 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 5, printername, "xx4-5 printer"); +/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 0, printername, "xx5-0 printer"); */ + TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 1, name, "xx5-1 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 2, printername, "xx5-2 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 4, printername, "xx5-4 printer"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 5, printername, "xx5-5 printer"); + + /* servername can be set but does not stick + TEST_PRINTERINFO_STRING(q, 2, servername, 0, servername, "xx2-0 servername"); + TEST_PRINTERINFO_STRING(q, 2, servername, 2, servername, "xx2-2 servername"); + TEST_PRINTERINFO_STRING(q, 2, servername, 4, servername, "xx2-4 servername"); + */ + + /* passing an invalid port will result in WERR_UNKNOWN_PORT */ + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, portname, 2, portname, "xx2-2 portname", WERR_UNKNOWN_PORT); + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, portname, 5, portname, "xx2-5 portname", WERR_UNKNOWN_PORT); + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 5, portname, 2, portname, "xx5-2 portname", WERR_UNKNOWN_PORT); + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 5, portname, 5, portname, "xx5-5 portname", WERR_UNKNOWN_PORT); + + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, sharename, 2, sharename, "xx2-2 sharename"); + /* passing an invalid driver will result in WERR_UNKNOWN_PRINTER_DRIVER */ + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, drivername, 2, drivername, "xx2-2 drivername", WERR_UNKNOWN_PRINTER_DRIVER); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, location, 2, location, "xx2-2 location"); + /* passing an invalid sepfile will result in WERR_INVALID_SEPARATOR_FILE */ + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, sepfile, 2, sepfile, "xx2-2 sepfile", WERR_INVALID_SEPARATOR_FILE); + /* passing an invalid printprocessor will result in WERR_UNKNOWN_PRINTPROCESSOR */ + TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, printprocessor, 2, printprocessor, "xx2-2 printprocessor", WERR_UNKNOWN_PRINTPROCESSOR); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, datatype, 2, datatype, "xx2-2 datatype"); + TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, parameters, 2, parameters, "xx2-2 parameters"); + + for (i=0; i < ARRAY_SIZE(attribute_list); i++) { +/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 1, flags, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); */ + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 2, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 4, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 5, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); +/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 1, flags, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); */ + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 2, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 4, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 5, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); +/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 1, flags, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); */ + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 2, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 4, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 5, attributes, + attribute_list[i], + (attribute_list[i] | default_attribute) + ); + } + + for (i=0; i < ARRAY_SIZE(status_list); i++) { + /* level 2 sets do not stick + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 0, status, status_list[i]); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 2, status, status_list[i]); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 6, status, status_list[i]); */ + TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 0, status, status_list[i]); + TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 2, status, status_list[i]); + TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 6, status, status_list[i]); + } + + /* priorities need to be between 0 and 99 + passing an invalid priority will result in WERR_INVALID_PRIORITY */ + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 0); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 1); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 99); + /* TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 100); */ + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 0); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 1); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 99); + /* TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 100); */ + + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, starttime, 2, starttime, __LINE__); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, untiltime, 2, untiltime, __LINE__); + + /* does not stick + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, cjobs, 2, cjobs, __LINE__); + TEST_PRINTERINFO_INT(q, s, needed, blob, 2, averageppm, 2, averageppm, __LINE__); */ + + /* does not stick + TEST_PRINTERINFO_INT(q, s, needed, blob, 5, device_not_selected_timeout, 5, device_not_selected_timeout, __LINE__); + TEST_PRINTERINFO_INT(q, s, needed, blob, 5, transmission_retry_timeout, 5, transmission_retry_timeout, __LINE__); */ + + /* FIXME: gd also test devmode and secdesc behavior */ + + { + /* verify composition of level 1 description field */ + const char *description; + const char *tmp; + + q0.in.level = 1; + do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0); + + description = talloc_strdup(tctx, q0.out.info->info1.description); + + q0.in.level = 2; + do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0); + + tmp = talloc_asprintf(tctx, "%s,%s,%s", + q0.out.info->info2.printername, + q0.out.info->info2.drivername, + q0.out.info->info2.location); + + do { STRING_EQUAL(description, tmp, "description")} while (0); + } + + return ret; +} + +static bool test_security_descriptor_equal(struct torture_context *tctx, + const struct security_descriptor *sd1, + const struct security_descriptor *sd2) +{ + if (sd1 == sd2) { + return true; + } + + if (!sd1 || !sd2) { + torture_comment(tctx, "%s\n", __location__); + return false; + } + + torture_assert_int_equal(tctx, sd1->revision, sd2->revision, "revision mismatch"); + torture_assert_int_equal(tctx, sd1->type, sd2->type, "type mismatch"); + + torture_assert_sid_equal(tctx, sd1->owner_sid, sd2->owner_sid, "owner mismatch"); + torture_assert_sid_equal(tctx, sd1->group_sid, sd2->group_sid, "group mismatch"); + + if (!security_acl_equal(sd1->sacl, sd2->sacl)) { + torture_comment(tctx, "%s: sacl mismatch\n", __location__); + NDR_PRINT_DEBUG(security_acl, sd1->sacl); + NDR_PRINT_DEBUG(security_acl, sd2->sacl); + return false; + } + if (!security_acl_equal(sd1->dacl, sd2->dacl)) { + torture_comment(tctx, "%s: dacl mismatch\n", __location__); + NDR_PRINT_DEBUG(security_acl, sd1->dacl); + NDR_PRINT_DEBUG(security_acl, sd2->dacl); + return false; + } + + return true; +} + +static bool test_sd_set_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t level, + struct security_descriptor *sd) +{ + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + union spoolss_SetPrinterInfo sinfo; + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfo3 info3; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + switch (level) { + case 2: { + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), ""); + + info_ctr.level = 2; + info_ctr.info = sinfo; + + break; + } + case 3: { + + info3.sec_desc_ptr = 0; + + info_ctr.level = 3; + info_ctr.info.info3 = &info3; + + break; + } + default: + return false; + } + + secdesc_ctr.sd = sd; + + torture_assert(tctx, + test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), ""); + + return true; +} + +static bool test_PrinterInfo_SDs(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + union spoolss_PrinterInfo info; + struct security_descriptor *sd1, *sd2; + int i; + + /* just compare level 2 and level 3 */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + sd1 = info.info2.secdesc; + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 3, &info), ""); + + sd2 = info.info3.secdesc; + + torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2), + "SD level 2 != SD level 3"); + + + /* query level 2, set level 2, query level 2 */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + sd1 = info.info2.secdesc; + + torture_assert(tctx, test_sd_set_level(tctx, b, handle, 2, sd1), ""); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + sd2 = info.info2.secdesc; + if (sd1->type & SEC_DESC_DACL_DEFAULTED) { + torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n"); + sd1->type &= ~SEC_DESC_DACL_DEFAULTED; + } + + torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2), + "SD level 2 != SD level 2 after SD has been set via level 2"); + + + /* query level 2, set level 3, query level 2 */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + sd1 = info.info2.secdesc; + + torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), ""); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + sd2 = info.info2.secdesc; + + torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2), + "SD level 2 != SD level 2 after SD has been set via level 3"); + + /* set modified sd level 3, query level 2 */ + + for (i=0; i < 93; i++) { + struct security_ace a = {}; + const char *sid_string = talloc_asprintf(tctx, "S-1-5-32-9999%i", i); + a.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + a.flags = 0; + a.size = 0; /* autogenerated */ + a.access_mask = 0; + a.trustee = *dom_sid_parse_talloc(tctx, sid_string); + torture_assert_ntstatus_ok(tctx, security_descriptor_dacl_add(sd1, &a), ""); + } + + torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), ""); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + sd2 = info.info2.secdesc; + + if (sd1->type & SEC_DESC_DACL_DEFAULTED) { + torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n"); + sd1->type &= ~SEC_DESC_DACL_DEFAULTED; + } + + torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2), + "modified SD level 2 != SD level 2 after SD has been set via level 3"); + + + return true; +} + +/* + * wrapper call that saves original sd, runs tests, and restores sd + */ + +static bool test_PrinterInfo_SD(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + union spoolss_PrinterInfo info; + struct security_descriptor *sd; + bool ret = true; + + torture_comment(tctx, "Testing Printer Security Descriptors\n"); + + /* save original sd */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to get initial security descriptor"); + + sd = security_descriptor_copy(tctx, info.info2.secdesc); + + /* run tests */ + + ret = test_PrinterInfo_SDs(tctx, b, handle); + + /* restore original sd */ + + torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd), + "failed to restore initial security descriptor"); + + torture_comment(tctx, "Printer Security Descriptors test %s\n\n", + ret ? "succeeded" : "failed"); + + + return ret; +} + +static bool test_devmode_set_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t level, + struct spoolss_DeviceMode *devmode) +{ + struct spoolss_SetPrinterInfo8 info8; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + union spoolss_SetPrinterInfo sinfo; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + switch (level) { + case 2: { + union spoolss_PrinterInfo info; + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), ""); + + info_ctr.level = 2; + info_ctr.info = sinfo; + + break; + } + case 8: { + info8.devmode_ptr = 0; + + info_ctr.level = 8; + info_ctr.info.info8 = &info8; + + break; + } + default: + return false; + } + + devmode_ctr.devmode = devmode; + + torture_assert(tctx, + test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), ""); + + return true; +} + + +static bool test_devicemode_equal(struct torture_context *tctx, + const struct spoolss_DeviceMode *d1, + const struct spoolss_DeviceMode *d2) +{ + if (d1 == d2) { + return true; + } + + if (!d1 || !d2) { + torture_comment(tctx, "%s\n", __location__); + return false; + } + torture_assert_str_equal(tctx, d1->devicename, d2->devicename, "devicename mismatch"); + torture_assert_int_equal(tctx, d1->specversion, d2->specversion, "specversion mismatch"); + torture_assert_int_equal(tctx, d1->driverversion, d2->driverversion, "driverversion mismatch"); + torture_assert_int_equal(tctx, d1->size, d2->size, "size mismatch"); + torture_assert_int_equal(tctx, d1->__driverextra_length, d2->__driverextra_length, "__driverextra_length mismatch"); + torture_assert_int_equal(tctx, d1->fields, d2->fields, "fields mismatch"); + torture_assert_int_equal(tctx, d1->orientation, d2->orientation, "orientation mismatch"); + torture_assert_int_equal(tctx, d1->papersize, d2->papersize, "papersize mismatch"); + torture_assert_int_equal(tctx, d1->paperlength, d2->paperlength, "paperlength mismatch"); + torture_assert_int_equal(tctx, d1->paperwidth, d2->paperwidth, "paperwidth mismatch"); + torture_assert_int_equal(tctx, d1->scale, d2->scale, "scale mismatch"); + torture_assert_int_equal(tctx, d1->copies, d2->copies, "copies mismatch"); + torture_assert_int_equal(tctx, d1->defaultsource, d2->defaultsource, "defaultsource mismatch"); + torture_assert_int_equal(tctx, d1->printquality, d2->printquality, "printquality mismatch"); + torture_assert_int_equal(tctx, d1->color, d2->color, "color mismatch"); + torture_assert_int_equal(tctx, d1->duplex, d2->duplex, "duplex mismatch"); + torture_assert_int_equal(tctx, d1->yresolution, d2->yresolution, "yresolution mismatch"); + torture_assert_int_equal(tctx, d1->ttoption, d2->ttoption, "ttoption mismatch"); + torture_assert_int_equal(tctx, d1->collate, d2->collate, "collate mismatch"); + torture_assert_str_equal(tctx, d1->formname, d2->formname, "formname mismatch"); + torture_assert_int_equal(tctx, d1->logpixels, d2->logpixels, "logpixels mismatch"); + torture_assert_int_equal(tctx, d1->bitsperpel, d2->bitsperpel, "bitsperpel mismatch"); + torture_assert_int_equal(tctx, d1->pelswidth, d2->pelswidth, "pelswidth mismatch"); + torture_assert_int_equal(tctx, d1->pelsheight, d2->pelsheight, "pelsheight mismatch"); + torture_assert_int_equal(tctx, d1->displayflags, d2->displayflags, "displayflags mismatch"); + torture_assert_int_equal(tctx, d1->displayfrequency, d2->displayfrequency, "displayfrequency mismatch"); + torture_assert_int_equal(tctx, d1->icmmethod, d2->icmmethod, "icmmethod mismatch"); + torture_assert_int_equal(tctx, d1->icmintent, d2->icmintent, "icmintent mismatch"); + torture_assert_int_equal(tctx, d1->mediatype, d2->mediatype, "mediatype mismatch"); + torture_assert_int_equal(tctx, d1->dithertype, d2->dithertype, "dithertype mismatch"); + torture_assert_int_equal(tctx, d1->reserved1, d2->reserved1, "reserved1 mismatch"); + torture_assert_int_equal(tctx, d1->reserved2, d2->reserved2, "reserved2 mismatch"); + torture_assert_int_equal(tctx, d1->panningwidth, d2->panningwidth, "panningwidth mismatch"); + torture_assert_int_equal(tctx, d1->panningheight, d2->panningheight, "panningheight mismatch"); + torture_assert_data_blob_equal(tctx, d1->driverextra_data, d2->driverextra_data, "driverextra_data mismatch"); + + return true; +} + +static bool test_devicemode_full(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct spoolss_SetPrinter s; + struct spoolss_GetPrinter q; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo8 info8; + union spoolss_PrinterInfo info; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + uint32_t needed = 0; + DATA_BLOB blob = data_blob_null; + bool ret = true; + NTSTATUS status; + +#define TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, exp_value, expected_result) do { \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + info_ctr.level = lvl1; \ + if (lvl1 == 2) {\ + void *p = (void *)&q.out.info->info ## lvl1; \ + info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \ + } else if (lvl1 == 8) {\ + info_ctr.info.info ## lvl1 = &info8; \ + }\ + devmode_ctr.devmode = q.out.info->info ## lvl1.devmode; \ + devmode_ctr.devmode->field1 = value; \ + TESTSETCALL_EXP(SetPrinter, s, expected_result) \ + if (W_ERROR_IS_OK(expected_result)) { \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \ + q.in.level = lvl2; \ + TESTGETCALL(GetPrinter, q, needed, blob) \ + INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \ + }\ + } while (0) + +#define TEST_DEVMODE_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, expected_result) do { \ + TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value, expected_result); \ + } while (0) + +#define TEST_DEVMODE_INT(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \ + TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value, WERR_OK); \ + } while (0) + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + ZERO_STRUCT(info8); + + s.in.handle = handle; + s.in.command = 0; + s.in.info_ctr = &info_ctr; + s.in.devmode_ctr = &devmode_ctr; + s.in.secdesc_ctr = &secdesc_ctr; + + q.in.handle = handle; + q.out.info = &info; + +#if 0 + const char *devicename;/* [charset(UTF16)] */ + enum spoolss_DeviceModeSpecVersion specversion; + uint16_t driverversion; + uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */ + uint32_t fields; +#endif + TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, __LINE__, WERR_INVALID_PARAMETER); + TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, 0, WERR_INVALID_PARAMETER); + TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, 0xffff, WERR_INVALID_PARAMETER); + TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK); + TEST_DEVMODE_INT(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length); + + devmode_ctr.devmode->driverextra_data = data_blob_string_const("foobar"); + torture_assert(tctx, + test_devmode_set_level(tctx, b, handle, 8, devmode_ctr.devmode), + "failed to set devmode"); + + TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK); + TEST_DEVMODE_INT(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length); + + TEST_DEVMODE_INT(q, s, needed, blob, 8, orientation, 8, orientation, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, papersize, 8, papersize, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, paperlength, 8, paperlength, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, paperwidth, 8, paperwidth, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, scale, 8, scale, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, copies, 8, copies, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, defaultsource, 8, defaultsource, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, printquality, 8, printquality, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, color, 8, color, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, duplex, 8, duplex, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, yresolution, 8, yresolution, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, ttoption, 8, ttoption, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, collate, 8, collate, __LINE__); +#if 0 + const char *formname;/* [charset(UTF16)] */ +#endif + TEST_DEVMODE_INT(q, s, needed, blob, 8, logpixels, 8, logpixels, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, bitsperpel, 8, bitsperpel, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, pelswidth, 8, pelswidth, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, pelsheight, 8, pelsheight, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, displayflags, 8, displayflags, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, displayfrequency, 8, displayfrequency, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, icmmethod, 8, icmmethod, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, icmintent, 8, icmintent, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, mediatype, 8, mediatype, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, dithertype, 8, dithertype, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, reserved1, 8, reserved1, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, reserved2, 8, reserved2, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, panningwidth, 8, panningwidth, __LINE__); + TEST_DEVMODE_INT(q, s, needed, blob, 8, panningheight, 8, panningheight, __LINE__); + + return ret; +} + +static bool call_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + struct spoolss_DeviceMode *devmode, + struct policy_handle *handle); + +static bool test_PrinterInfo_DevModes(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *name) +{ + union spoolss_PrinterInfo info; + struct spoolss_DeviceMode *devmode; + struct spoolss_DeviceMode *devmode2; + struct policy_handle handle_devmode; + struct dcerpc_binding_handle *b = p->binding_handle; + + /* simply compare level8 and level2 devmode */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), ""); + + devmode = info.info8.devmode; + + if (devmode && devmode->size == 0) { + torture_fail(tctx, + "devmode of zero size!"); + } + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + devmode2 = info.info2.devmode; + + if (devmode2 && devmode2->size == 0) { + torture_fail(tctx, + "devmode of zero size!"); + } + + torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2), + "DM level 8 != DM level 2"); + + + /* set devicemode level 8 and see if it persists */ + + devmode->copies = 93; + devmode->formname = talloc_strdup(tctx, "Legal"); + + torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode), ""); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), ""); + + devmode2 = info.info8.devmode; + + torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2), + "modified DM level 8 != DM level 8 after DM has been set via level 8"); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + devmode2 = info.info2.devmode; + + torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2), + "modified DM level 8 != DM level 2"); + + + /* set devicemode level 2 and see if it persists */ + + devmode->copies = 39; + devmode->formname = talloc_strdup(tctx, "Executive"); + + torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 2, devmode), ""); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), ""); + + devmode2 = info.info8.devmode; + + torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2), + "modified DM level 8 != DM level 8 after DM has been set via level 2"); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + devmode2 = info.info2.devmode; + + torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2), + "modified DM level 8 != DM level 2"); + + + /* check every single bit in public part of devicemode */ + + torture_assert(tctx, test_devicemode_full(tctx, b, handle), + "failed to set every single devicemode component"); + + + /* change formname upon open and see if it persists in getprinter calls */ + + devmode->formname = talloc_strdup(tctx, "A4"); + devmode->copies = 42; + + torture_assert(tctx, call_OpenPrinterEx(tctx, p, name, devmode, &handle_devmode), + "failed to open printer handle"); + + torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 8, &info), ""); + + devmode2 = info.info8.devmode; + + if (strequal(devmode->devicename, devmode2->devicename)) { + torture_warning(tctx, "devicenames are the same\n"); + } else { + torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename); + torture_comment(tctx, "devicename after level 8 get: %s\n", devmode2->devicename); + } + + if (strequal(devmode->formname, devmode2->formname)) { + torture_warning(tctx, "formname are the same\n"); + } else { + torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname); + torture_comment(tctx, "formname after level 8 get: %s\n", devmode2->formname); + } + + if (devmode->copies == devmode2->copies) { + torture_warning(tctx, "copies are the same\n"); + } else { + torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies); + torture_comment(tctx, "copies after level 8 get: %d\n", devmode2->copies); + } + + torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 2, &info), ""); + + devmode2 = info.info2.devmode; + + if (strequal(devmode->devicename, devmode2->devicename)) { + torture_warning(tctx, "devicenames are the same\n"); + } else { + torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename); + torture_comment(tctx, "devicename after level 2 get: %s\n", devmode2->devicename); + } + + if (strequal(devmode->formname, devmode2->formname)) { + torture_warning(tctx, "formname is the same\n"); + } else { + torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname); + torture_comment(tctx, "formname after level 2 get: %s\n", devmode2->formname); + } + + if (devmode->copies == devmode2->copies) { + torture_warning(tctx, "copies are the same\n"); + } else { + torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies); + torture_comment(tctx, "copies after level 2 get: %d\n", devmode2->copies); + } + + test_ClosePrinter(tctx, b, &handle_devmode); + + return true; +} + +/* + * wrapper call that saves original devmode, runs tests, and restores devmode + */ + +static bool test_PrinterInfo_DevMode(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *name, + struct spoolss_DeviceMode *addprinter_devmode) +{ + union spoolss_PrinterInfo info; + struct spoolss_DeviceMode *devmode; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing Printer Devicemodes\n"); + + /* save original devmode */ + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), + "failed to get initial global devicemode"); + + devmode = info.info8.devmode; + + if (devmode && devmode->size == 0) { + torture_fail(tctx, + "devmode of zero size!"); + } + + if (addprinter_devmode) { + if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) { + torture_warning(tctx, "current global DM is != DM provided in addprinter"); + } + } + + /* run tests */ + + ret = test_PrinterInfo_DevModes(tctx, p, handle, name); + + /* restore original devmode */ + + torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode), + "failed to restore initial global device mode"); + + torture_comment(tctx, "Printer Devicemodes test %s\n\n", + ret ? "succeeded" : "failed"); + + + return ret; +} + +bool test_ClosePrinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_ClosePrinter r; + + r.in.handle = handle; + r.out.handle = handle; + + torture_comment(tctx, "Testing ClosePrinter\n"); + + status = dcerpc_spoolss_ClosePrinter_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + torture_assert_werr_ok(tctx, r.out.result, "ClosePrinter failed"); + + return true; +} + +static bool test_GetForm_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *form_name, + uint32_t level, + union spoolss_FormInfo *info_p) +{ + NTSTATUS status; + struct spoolss_GetForm r; + uint32_t needed; + + r.in.handle = handle; + r.in.form_name = form_name; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_comment(tctx, "Testing GetForm(%s) level %d\n", form_name, r.in.level); + + status = dcerpc_spoolss_GetForm_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetForm failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + status = dcerpc_spoolss_GetForm_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetForm failed"); + + torture_assert(tctx, r.out.info, "No form info returned"); + } + + torture_assert_werr_ok(tctx, r.out.result, "GetForm failed"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, needed, 4); + + if (info_p) { + *info_p = *r.out.info; + } + + return true; +} + +static bool test_GetForm(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *form_name, + uint32_t level) +{ + return test_GetForm_args(tctx, b, handle, form_name, level, NULL); +} + +static bool test_EnumForms(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + bool print_server, + uint32_t level, + uint32_t *count_p, + union spoolss_FormInfo **info_p) +{ + struct spoolss_EnumForms r; + uint32_t needed; + uint32_t count; + union spoolss_FormInfo *info; + + r.in.handle = handle; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumForms level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumForms_r(b, tctx, &r), + "EnumForms failed"); + + if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL))) { + torture_skip(tctx, "EnumForms level 2 not supported"); + } + + if (print_server && W_ERROR_EQUAL(r.out.result, WERR_INVALID_HANDLE)) { + torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)"); + } + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumForms_r(b, tctx, &r), + "EnumForms failed"); + + torture_assert(tctx, info, "No forms returned"); + } + + torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, needed, 4); + + if (info_p) { + *info_p = info; + } + if (count_p) { + *count_p = count; + } + + return true; +} + +static bool test_EnumForms_all(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + bool print_server) +{ + uint32_t levels[] = { 1, 2 }; + int i, j; + + for (i=0; iinfo.info1->form_name, level, + r.in.info_ctr->info.info1->flags); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r), + "AddForm failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "AddForm gave unexpected result"); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r), + "2nd AddForm failed"); + if (W_ERROR_EQUAL(expected_result, WERR_INVALID_PARAMETER)) { + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "2nd AddForm gave unexpected result"); + } else { + torture_assert_werr_equal(tctx, r.out.result, WERR_FILE_EXISTS, + "2nd AddForm gave unexpected result"); + } + + return true; +} + +static bool test_SetForm(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *form_name, + uint32_t level, + union spoolss_AddFormInfo *info) +{ + struct spoolss_SetForm r; + struct spoolss_AddFormInfoCtr info_ctr; + + info_ctr.level = level; + info_ctr.info = *info; + + r.in.handle = handle; + r.in.form_name = form_name; + r.in.info_ctr = &info_ctr; + + torture_comment(tctx, "Testing SetForm(%s) level %d\n", + form_name, level); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r), + "SetForm failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "SetForm failed"); + + return true; +} + +static bool test_GetForm_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char *form_name, + enum winreg_Type *w_type, + uint32_t *w_size, + uint32_t *w_length, + uint8_t **w_data); + +static bool test_Forms_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + bool print_server, + const char *printer_name, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle, + const char *form_name, + struct spoolss_AddFormInfo1 *info1, + WERROR expected_add_result, + WERROR expected_delete_result) +{ + union spoolss_FormInfo info; + union spoolss_AddFormInfo add_info; + + enum winreg_Type w_type; + uint32_t w_size; + uint32_t w_length; + uint8_t *w_data; + + add_info.info1 = info1; + + torture_assert(tctx, + test_AddForm(tctx, b, handle, 1, &add_info, expected_add_result), + "failed to add form"); + + if (winreg_handle && hive_handle && W_ERROR_IS_OK(expected_add_result)) { + + struct spoolss_FormInfo1 i1; + + torture_assert(tctx, + test_GetForm_winreg(tctx, winreg_handle, hive_handle, TOP_LEVEL_CONTROL_FORMS_KEY, form_name, &w_type, &w_size, &w_length, &w_data), + "failed to get form via winreg"); + + i1.size.width = IVAL(w_data, 0); + i1.size.height = IVAL(w_data, 4); + i1.area.left = IVAL(w_data, 8); + i1.area.top = IVAL(w_data, 12); + i1.area.right = IVAL(w_data, 16); + i1.area.bottom = IVAL(w_data, 20); + /* skip index here */ + i1.flags = IVAL(w_data, 28); + + torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type"); + torture_assert_int_equal(tctx, w_size, 0x20, "unexpected size"); + torture_assert_int_equal(tctx, w_length, 0x20, "unexpected length"); + torture_assert_int_equal(tctx, i1.size.width, add_info.info1->size.width, "width mismatch"); + torture_assert_int_equal(tctx, i1.size.height, add_info.info1->size.height, "height mismatch"); + torture_assert_int_equal(tctx, i1.area.left, add_info.info1->area.left, "left mismatch"); + torture_assert_int_equal(tctx, i1.area.top, add_info.info1->area.top, "top mismatch"); + torture_assert_int_equal(tctx, i1.area.right, add_info.info1->area.right, "right mismatch"); + torture_assert_int_equal(tctx, i1.area.bottom, add_info.info1->area.bottom, "bottom mismatch"); + torture_assert_int_equal(tctx, i1.flags, add_info.info1->flags, "flags mismatch"); + } + + if (!print_server && W_ERROR_IS_OK(expected_add_result)) { + torture_assert(tctx, + test_GetForm_args(tctx, b, handle, form_name, 1, &info), + "failed to get added form"); + + torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch"); + torture_assert_int_equal(tctx, info.info1.size.height, add_info.info1->size.height, "height mismatch"); + torture_assert_int_equal(tctx, info.info1.area.left, add_info.info1->area.left, "left mismatch"); + torture_assert_int_equal(tctx, info.info1.area.top, add_info.info1->area.top, "top mismatch"); + torture_assert_int_equal(tctx, info.info1.area.right, add_info.info1->area.right, "right mismatch"); + torture_assert_int_equal(tctx, info.info1.area.bottom, add_info.info1->area.bottom, "bottom mismatch"); + torture_assert_int_equal(tctx, info.info1.flags, add_info.info1->flags, "flags mismatch"); + + if (winreg_handle && hive_handle) { + + struct spoolss_FormInfo1 i1; + + i1.size.width = IVAL(w_data, 0); + i1.size.height = IVAL(w_data, 4); + i1.area.left = IVAL(w_data, 8); + i1.area.top = IVAL(w_data, 12); + i1.area.right = IVAL(w_data, 16); + i1.area.bottom = IVAL(w_data, 20); + /* skip index here */ + i1.flags = IVAL(w_data, 28); + + torture_assert_int_equal(tctx, i1.size.width, info.info1.size.width, "width mismatch"); + torture_assert_int_equal(tctx, i1.size.height, info.info1.size.height, "height mismatch"); + torture_assert_int_equal(tctx, i1.area.left, info.info1.area.left, "left mismatch"); + torture_assert_int_equal(tctx, i1.area.top, info.info1.area.top, "top mismatch"); + torture_assert_int_equal(tctx, i1.area.right, info.info1.area.right, "right mismatch"); + torture_assert_int_equal(tctx, i1.area.bottom, info.info1.area.bottom, "bottom mismatch"); + torture_assert_int_equal(tctx, i1.flags, info.info1.flags, "flags mismatch"); + } + + add_info.info1->size.width = 1234; + + torture_assert(tctx, + test_SetForm(tctx, b, handle, form_name, 1, &add_info), + "failed to set form"); + torture_assert(tctx, + test_GetForm_args(tctx, b, handle, form_name, 1, &info), + "failed to get set form"); + + torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch"); + } + + if (!W_ERROR_EQUAL(expected_add_result, WERR_INVALID_PARAMETER)) { + torture_assert(tctx, + test_EnumForms_find_one(tctx, b, handle, print_server, form_name), + "Newly added form not found in enum call"); + } + + torture_assert(tctx, + test_DeleteForm(tctx, b, handle, form_name, expected_delete_result), + "failed to delete form"); + + return true; +} + +static bool test_Forms(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + bool print_server, + const char *printer_name, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + const struct spoolss_FormSize size = { + .width = 50, + .height = 25 + }; + const struct spoolss_FormArea area = { + .left = 5, + .top = 10, + .right = 45, + .bottom = 15 + }; + int i; + + struct { + struct spoolss_AddFormInfo1 info1; + WERROR expected_add_result; + WERROR expected_delete_result; + } forms[] = { + { + .info1 = { + .flags = SPOOLSS_FORM_USER, + .form_name = "testform_user", + .size = size, + .area = area, + }, + .expected_add_result = WERR_OK, + .expected_delete_result = WERR_OK + }, +/* + weird, we can add a builtin form but we can never remove it + again - gd + + { + .info1 = { + .flags = SPOOLSS_FORM_BUILTIN, + .form_name = "testform_builtin", + .size = size, + .area = area, + }, + .expected_add_result = WERR_OK, + .expected_delete_result = WERR_INVALID_PARAMETER, + }, +*/ + { + .info1 = { + .flags = SPOOLSS_FORM_PRINTER, + .form_name = "testform_printer", + .size = size, + .area = area, + }, + .expected_add_result = WERR_OK, + .expected_delete_result = WERR_OK + }, + { + .info1 = { + .flags = SPOOLSS_FORM_USER, + .form_name = "Letter", + .size = size, + .area = area, + }, + .expected_add_result = WERR_FILE_EXISTS, + .expected_delete_result = WERR_INVALID_PARAMETER + }, + { + .info1 = { + .flags = SPOOLSS_FORM_BUILTIN, + .form_name = "Letter", + .size = size, + .area = area, + }, + .expected_add_result = WERR_FILE_EXISTS, + .expected_delete_result = WERR_INVALID_PARAMETER + }, + { + .info1 = { + .flags = SPOOLSS_FORM_PRINTER, + .form_name = "Letter", + .size = size, + .area = area, + }, + .expected_add_result = WERR_FILE_EXISTS, + .expected_delete_result = WERR_INVALID_PARAMETER + }, + { + .info1 = { + .flags = 12345, + .form_name = "invalid_flags", + .size = size, + .area = area, + }, + .expected_add_result = WERR_INVALID_PARAMETER, + .expected_delete_result = WERR_INVALID_FORM_NAME + } + + }; + + for (i=0; i < ARRAY_SIZE(forms); i++) { + torture_assert(tctx, + test_Forms_args(tctx, b, handle, print_server, printer_name, + winreg_handle, hive_handle, + forms[i].info1.form_name, + &forms[i].info1, + forms[i].expected_add_result, + forms[i].expected_delete_result), + talloc_asprintf(tctx, "failed to test form '%s'", forms[i].info1.form_name)); + } + + return true; +} + +static bool test_EnumPorts_old(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + NTSTATUS status; + struct spoolss_EnumPorts r; + uint32_t needed; + uint32_t count; + union spoolss_PortInfo *info; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.servername = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.level = 2; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPorts\n"); + + status = dcerpc_spoolss_EnumPorts_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + status = dcerpc_spoolss_EnumPorts_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed"); + torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed"); + + torture_assert(tctx, info, "No ports returned"); + } + + torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, needed, 4); + + return true; +} + +static bool test_AddPort(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + NTSTATUS status; + struct spoolss_AddPort r; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + r.in.unknown = 0; + r.in.monitor_name = "foo"; + + torture_comment(tctx, "Testing AddPort\n"); + + status = dcerpc_spoolss_AddPort_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "AddPort failed"); + + /* win2k3 returns WERR_NOT_SUPPORTED */ + +#if 0 + + if (!W_ERROR_IS_OK(r.out.result)) { + printf("AddPort failed - %s\n", win_errstr(r.out.result)); + return false; + } + +#endif + + return true; +} + +static bool test_GetJob_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id, + uint32_t level, + union spoolss_JobInfo *info_p) +{ + NTSTATUS status; + struct spoolss_GetJob r; + union spoolss_JobInfo info; + uint32_t needed; + + r.in.handle = handle; + r.in.job_id = job_id; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.info = &info; + + torture_comment(tctx, "Testing GetJob(%d), level %d\n", job_id, r.in.level); + + status = dcerpc_spoolss_GetJob_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetJob failed"); + if (level == 0) { + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "Unexpected return code"); + } + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + status = dcerpc_spoolss_GetJob_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetJob failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, "GetJob failed"); + torture_assert(tctx, r.out.info, "No job info returned"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, needed, 4); + + if (info_p) { + *info_p = *r.out.info; + } + + return true; +} + +#if 0 +static bool test_GetJob(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id) +{ + uint32_t levels[] = {0, 1, 2 /* 3, 4 */}; + uint32_t i; + + for (i=0; i < ARRAY_SIZE(levels); i++) { + torture_assert(tctx, + test_GetJob_args(tctx, b, handle, job_id, levels[i], NULL), + "GetJob failed"); + } + + return true; +} +#endif + +static bool test_SetJob(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id, + struct spoolss_JobInfoContainer *ctr, + enum spoolss_JobControl command) +{ + NTSTATUS status; + struct spoolss_SetJob r; + + r.in.handle = handle; + r.in.job_id = job_id; + r.in.ctr = ctr; + r.in.command = command; + + switch (command) { + case SPOOLSS_JOB_CONTROL_PAUSE: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_PAUSE\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_RESUME: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESUME\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_CANCEL: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_CANCEL\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_RESTART: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESTART\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_DELETE: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_DELETE\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_RETAIN: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RETAIN\n", job_id); + break; + case SPOOLSS_JOB_CONTROL_RELEASE: + torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RELEASE\n", job_id); + break; + default: + torture_comment(tctx, "Testing SetJob(%d)\n", job_id); + break; + } + + status = dcerpc_spoolss_SetJob_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "SetJob failed"); + torture_assert_werr_ok(tctx, r.out.result, "SetJob failed"); + + return true; +} + +static bool test_AddJob(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_AddJob r; + uint32_t needed; + + r.in.level = 0; + r.in.handle = handle; + r.in.offered = 0; + r.out.needed = &needed; + r.in.buffer = r.out.buffer = NULL; + + torture_comment(tctx, "Testing AddJob\n"); + + status = dcerpc_spoolss_AddJob_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "AddJob failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "AddJob failed"); + + r.in.level = 1; + + status = dcerpc_spoolss_AddJob_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "AddJob failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, "AddJob failed"); + + return true; +} + + +static bool test_EnumJobs_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t level, + WERROR werr_expected, + uint32_t *count_p, + union spoolss_JobInfo **info_p) +{ + NTSTATUS status; + struct spoolss_EnumJobs r; + uint32_t needed; + uint32_t count; + union spoolss_JobInfo *info; + + r.in.handle = handle; + r.in.firstjob = 0; + r.in.numjobs = 0xffffffff; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumJobs level %d\n", level); + + status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + torture_assert_werr_equal(tctx, r.out.result, werr_expected, + "EnumJobs failed"); + torture_assert(tctx, info, "No jobs returned"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, needed, 4); + + } else { + torture_assert_werr_equal(tctx, r.out.result, werr_expected, + "EnumJobs failed"); + } + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + return true; +} + +static bool test_JobPropertiesEnum(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id) +{ + struct spoolss_EnumJobNamedProperties r; + uint32_t pcProperties = 0; + struct spoolss_PrintNamedProperty *ppProperties = NULL; + + r.in.hPrinter = handle; + r.in.JobId = job_id; + r.out.pcProperties = &pcProperties; + r.out.ppProperties = &ppProperties; + + torture_comment(tctx, "Testing EnumJobNamedProperties(%d)\n", job_id); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumJobNamedProperties_r(b, tctx, &r), + "spoolss_EnumJobNamedProperties failed"); + torture_assert_werr_ok(tctx, r.out.result, + "spoolss_EnumJobNamedProperties failed"); + + return true; +} + +static bool test_JobPropertySet(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id, + struct spoolss_PrintNamedProperty *property) +{ + struct spoolss_SetJobNamedProperty r; + + r.in.hPrinter = handle; + r.in.JobId = job_id; + r.in.pProperty = property; + + torture_comment(tctx, "Testing SetJobNamedProperty(%d) %s - %d\n", + job_id, property->propertyName, + property->propertyValue.ePropertyType); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_SetJobNamedProperty_r(b, tctx, &r), + "spoolss_SetJobNamedProperty failed"); + torture_assert_werr_ok(tctx, r.out.result, + "spoolss_SetJobNamedProperty failed"); + + return true; +} + +static bool test_JobPropertyGetValue(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id, + const char *property_name, + struct spoolss_PrintPropertyValue *value) +{ + struct spoolss_GetJobNamedPropertyValue r; + + r.in.hPrinter = handle; + r.in.JobId = job_id; + r.in.pszName = property_name; + r.out.pValue = value; + + torture_comment(tctx, "Testing GetJobNamedPropertyValue(%d) %s\n", + job_id, property_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetJobNamedPropertyValue_r(b, tctx, &r), + "spoolss_GetJobNamedPropertyValue failed"); + torture_assert_werr_ok(tctx, r.out.result, + "spoolss_GetJobNamedPropertyValue failed"); + + return true; +} + +static bool test_JobPropertyDelete(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id, + const char *property_name) +{ + struct spoolss_DeleteJobNamedProperty r; + + r.in.hPrinter = handle; + r.in.JobId = job_id; + r.in.pszName = property_name; + + torture_comment(tctx, "Testing DeleteJobNamedProperty(%d) %s\n", + job_id, property_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeleteJobNamedProperty_r(b, tctx, &r), + "spoolss_DeleteJobNamedProperty failed"); + torture_assert_werr_ok(tctx, r.out.result, + "spoolss_DeleteJobNamedProperty failed"); + + return true; +} + +static bool test_DoPrintTest_add_one_job_common(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *document_name, + const char *datatype, + uint32_t *job_id) +{ + NTSTATUS status; + struct spoolss_StartDocPrinter s; + struct spoolss_DocumentInfoCtr info_ctr; + struct spoolss_DocumentInfo1 info1; + struct spoolss_StartPagePrinter sp; + struct spoolss_WritePrinter w; + struct spoolss_EndPagePrinter ep; + struct spoolss_EndDocPrinter e; + int i; + uint32_t num_written; + + torture_comment(tctx, "Testing StartDocPrinter\n"); + + s.in.handle = handle; + s.in.info_ctr = &info_ctr; + s.out.job_id = job_id; + + info1.document_name = document_name; + info1.output_file = NULL; + info1.datatype = datatype; + + info_ctr.level = 1; + info_ctr.info.info1 = &info1; + + status = dcerpc_spoolss_StartDocPrinter_r(b, tctx, &s); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed"); + torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed"); + + for (i=1; i < 4; i++) { + union spoolss_JobInfo ginfo; + bool ok; + + torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id); + + sp.in.handle = handle; + + status = dcerpc_spoolss_StartPagePrinter_r(b, tctx, &sp); + torture_assert_ntstatus_ok(tctx, status, + "dcerpc_spoolss_StartPagePrinter failed"); + torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed"); + + ok = test_GetJob_args(tctx, b, handle, *job_id, 1, &ginfo); + if (!ok) { + torture_comment(tctx, "test_GetJob failed for JobId[%d]\n", *job_id); + } + + torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id); + + w.in.handle = handle; + w.in.data = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i)); + w.out.num_written = &num_written; + + status = dcerpc_spoolss_WritePrinter_r(b, tctx, &w); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_WritePrinter failed"); + torture_assert_werr_ok(tctx, w.out.result, "WritePrinter failed"); + + torture_comment(tctx, "Testing EndPagePrinter: Page[%d], JobId[%d]\n", i, *job_id); + + ep.in.handle = handle; + + status = dcerpc_spoolss_EndPagePrinter_r(b, tctx, &ep); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndPagePrinter failed"); + torture_assert_werr_ok(tctx, ep.out.result, "EndPagePrinter failed"); + } + + torture_comment(tctx, "Testing EndDocPrinter: JobId[%d]\n", *job_id); + + e.in.handle = handle; + + status = dcerpc_spoolss_EndDocPrinter_r(b, tctx, &e); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed"); + torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed"); + + return true; +} + +static bool test_DoPrintTest_add_one_job(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *document_name, + uint32_t *job_id) +{ + test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "RAW", job_id); + + return true; +} + +static bool test_DoPrintTest_add_one_job_v4(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *document_name, + uint32_t *job_id) +{ + test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "XPS_PASS", job_id); + + return true; +} + + +static bool test_DoPrintTest_check_jobs(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t num_jobs, + uint32_t *job_ids) +{ + uint32_t count; + union spoolss_JobInfo *info = NULL; + int i; + + torture_assert(tctx, + test_AddJob(tctx, b, handle), + "AddJob failed"); + + torture_assert(tctx, + test_EnumJobs_args(tctx, b, handle, 1, WERR_OK, &count, &info), + "EnumJobs level 1 failed"); + + torture_assert_int_equal(tctx, count, num_jobs, "unexpected number of jobs in queue"); + + for (i=0; i < num_jobs; i++) { + union spoolss_JobInfo ginfo; + const char *document_name; + const char *new_document_name = "any_other_docname"; + struct spoolss_JobInfoContainer ctr; + struct spoolss_SetJobInfo1 info1; + + torture_assert_int_equal(tctx, info[i].info1.job_id, job_ids[i], "job id mismatch"); + + torture_assert(tctx, + test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo), + "failed to call test_GetJob"); + + torture_assert_int_equal(tctx, ginfo.info1.job_id, info[i].info1.job_id, "job id mismatch"); + + document_name = ginfo.info1.document_name; + + info1.job_id = ginfo.info1.job_id; + info1.printer_name = ginfo.info1.printer_name; + info1.server_name = ginfo.info1.server_name; + info1.user_name = ginfo.info1.user_name; + info1.document_name = new_document_name; + info1.data_type = ginfo.info1.data_type; + info1.text_status = ginfo.info1.text_status; + info1.status = ginfo.info1.status; + info1.priority = ginfo.info1.priority; + info1.position = ginfo.info1.position; + info1.total_pages = ginfo.info1.total_pages; + info1.pages_printed = ginfo.info1.pages_printed; + info1.submitted = ginfo.info1.submitted; + + ctr.level = 1; + ctr.info.info1 = &info1; + + torture_assert(tctx, + test_SetJob(tctx, b, handle, info[i].info1.job_id, &ctr, 0), + "failed to call test_SetJob level 1"); + + torture_assert(tctx, + test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo), + "failed to call test_GetJob"); + + if (strequal(ginfo.info1.document_name, document_name)) { + torture_warning(tctx, + "document_name did *NOT* change from '%s' to '%s'\n", + document_name, new_document_name); + } + } + + for (i=0; i < num_jobs; i++) { + if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_PAUSE)) { + torture_warning(tctx, "failed to pause printjob\n"); + } + if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_RESUME)) { + torture_warning(tctx, "failed to resume printjob\n"); + } + } + + return true; +} + +static bool test_DoPrintTest(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + bool ret = true; + uint32_t num_jobs = 8; + uint32_t *job_ids; + int i; + + torture_comment(tctx, "Testing real print operations\n"); + + job_ids = talloc_zero_array(tctx, uint32_t, num_jobs); + + for (i=0; i < num_jobs; i++) { + ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]); + } + + for (i=0; i < num_jobs; i++) { + ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE); + } + + for (i=0; i < num_jobs; i++) { + ret &= test_DoPrintTest_add_one_job_v4(tctx, b, handle, "TorturePrintJob v4", &job_ids[i]); + } + + for (i=0; i < num_jobs; i++) { + ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE); + } + + if (ret == true) { + torture_comment(tctx, "real print operations test succeeded\n\n"); + } + + return ret; +} + +static bool test_DoPrintTest_extended(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + bool ret = true; + uint32_t num_jobs = 8; + uint32_t *job_ids; + int i; + torture_comment(tctx, "Testing real print operations (extended)\n"); + + job_ids = talloc_zero_array(tctx, uint32_t, num_jobs); + + for (i=0; i < num_jobs; i++) { + ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]); + } + + ret &= test_DoPrintTest_check_jobs(tctx, b, handle, num_jobs, job_ids); + + for (i=0; i < num_jobs; i++) { + ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE); + } + + if (ret == true) { + torture_comment(tctx, "real print operations (extended) test succeeded\n\n"); + } + + return ret; +} + +static bool test_JobPrintProperties_equal(struct torture_context *tctx, + struct spoolss_PrintPropertyValue *got, + struct spoolss_PrintNamedProperty *exp) +{ + torture_assert_int_equal(tctx, + got->ePropertyType, + exp->propertyValue.ePropertyType, + "ePropertyType"); + + switch (exp->propertyValue.ePropertyType) { + case kRpcPropertyTypeString: + torture_assert_str_equal(tctx, + got->value.propertyString, + exp->propertyValue.value.propertyString, + "propertyString"); + break; + case kRpcPropertyTypeInt32: + torture_assert_int_equal(tctx, + got->value.propertyInt32, + exp->propertyValue.value.propertyInt32, + "propertyInt32"); + break; + case kRpcPropertyTypeInt64: + torture_assert_u64_equal(tctx, + got->value.propertyInt64, + exp->propertyValue.value.propertyInt64, + "propertyInt64"); + break; + case kRpcPropertyTypeByte: + torture_assert_int_equal(tctx, + got->value.propertyByte, + exp->propertyValue.value.propertyByte, + "propertyByte"); + break; + case kRpcPropertyTypeBuffer: + torture_assert_int_equal(tctx, + got->value.propertyBlob.cbBuf, + exp->propertyValue.value.propertyBlob.cbBuf, + "propertyBlob.cbBuf"); + torture_assert_mem_equal(tctx, + got->value.propertyBlob.pBuf, + exp->propertyValue.value.propertyBlob.pBuf, + exp->propertyValue.value.propertyBlob.cbBuf, + "propertyBlob.pBuf"); + + break; + + } + + return true; +} + +static bool test_JobPrintProperties(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t job_id) +{ + struct spoolss_PrintNamedProperty in; + struct spoolss_PrintPropertyValue out; + int i; + DATA_BLOB blob = data_blob_string_const("blob"); + struct { + const char *property_name; + enum spoolss_EPrintPropertyType type; + union spoolss_PrintPropertyValueUnion value; + WERROR expected_result; + } tests[] = { + { + .property_name = "torture_property_string", + .type = kRpcPropertyTypeString, + .value.propertyString = "torture_property_value_string", + },{ + .property_name = "torture_property_int32", + .type = kRpcPropertyTypeInt32, + .value.propertyInt32 = 42, + },{ + .property_name = "torture_property_int64", + .type = kRpcPropertyTypeInt64, + .value.propertyInt64 = 0xaffe, + },{ + .property_name = "torture_property_byte", + .type = kRpcPropertyTypeByte, + .value.propertyByte = 0xab, + },{ + .property_name = "torture_property_buffer", + .type = kRpcPropertyTypeBuffer, + .value.propertyBlob.cbBuf = blob.length, + .value.propertyBlob.pBuf = blob.data, + } + }; + + torture_assert(tctx, + test_JobPropertiesEnum(tctx, b, handle, job_id), + "failed to enum properties"); + + for (i=0; i binding_handle; + + r.in.handle = handle; + r.in.key_name = key_name; + r.in.value_name = value_name; + r.in.offered = 0; + r.out.type = &type; + r.out.needed = &needed; + r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered); + + torture_comment(tctx, "Testing GetPrinterDataEx(%s - %s)\n", + r.in.key_name, r.in.value_name); + + status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + torture_skip(tctx, "GetPrinterDataEx not supported by server\n"); + } + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed"); + } + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + if (expected_type) { + torture_assert_int_equal(tctx, type, *expected_type, "unexpected type"); + } + r.in.offered = needed; + r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered); + status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + talloc_asprintf(tctx, "GetPrinterDataEx(%s - %s) failed", r.in.key_name, r.in.value_name)); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1); + + if (type_p) { + *type_p = type; + } + + if (data_p) { + *data_p = r.out.data; + } + + if (needed_p) { + *needed_p = needed; + } + + return true; +} + +static bool test_GetPrinterDataEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *key_name, + const char *value_name, + enum winreg_Type *type_p, + uint8_t **data_p, + uint32_t *needed_p) +{ + return test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name, + NULL, type_p, data_p, needed_p); +} + +static bool test_get_environment(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char **architecture) +{ + DATA_BLOB blob; + enum winreg_Type type; + uint8_t *data; + uint32_t needed; + + torture_assert(tctx, + test_GetPrinterData(tctx, b, handle, "Architecture", &type, &data, &needed), + "failed to get Architecture"); + + torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type"); + + blob = data_blob_const(data, needed); + *architecture = reg_val_data_string(tctx, REG_SZ, blob); + + return true; +} + +static bool test_GetPrinterData_list(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *list[] = { + "W3SvcInstalled", + "BeepEnabled", + "EventLog", + /* "NetPopup", not on w2k8 */ + /* "NetPopupToComputer", not on w2k8 */ + "MajorVersion", + "MinorVersion", + "DefaultSpoolDirectory", + "Architecture", + "DsPresent", + "OSVersion", + /* "OSVersionEx", not on s3 */ + "DNSMachineName" + }; + int i; + + for (i=0; i < ARRAY_SIZE(list); i++) { + enum winreg_Type type = REG_NONE; + enum winreg_Type type_ex1 = REG_NONE; + enum winreg_Type type_ex2 = REG_NONE; + uint8_t *data; + uint8_t *data_ex1 = NULL; + uint8_t *data_ex2 = NULL; + uint32_t needed; + uint32_t needed_ex1 = 0; + uint32_t needed_ex2 = 0; + + torture_assert(tctx, test_GetPrinterData(tctx, b, &ctx->server_handle, list[i], &type, &data, &needed), + talloc_asprintf(tctx, "GetPrinterData failed on %s\n", list[i])); + torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "random_string", list[i], &type_ex1, &data_ex1, &needed_ex1), + talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i])); + torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "", list[i], &type_ex2, &data_ex2, &needed_ex2), + talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i])); + torture_assert_int_equal(tctx, type, type_ex1, "type mismatch"); + torture_assert_int_equal(tctx, type, type_ex2, "type mismatch"); + torture_assert_int_equal(tctx, needed, needed_ex1, "needed mismatch"); + torture_assert_int_equal(tctx, needed, needed_ex2, "needed mismatch"); + torture_assert_mem_equal(tctx, data, data_ex1, needed, "data mismatch"); + torture_assert_mem_equal(tctx, data, data_ex2, needed, "data mismatch"); + } + + return true; +} + +static bool test_EnumPrinterData(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + uint32_t enum_index, + uint32_t value_offered, + uint32_t data_offered, + enum winreg_Type *type_p, + uint32_t *value_needed_p, + uint32_t *data_needed_p, + const char **value_name_p, + uint8_t **data_p, + WERROR *result_p) +{ + struct spoolss_EnumPrinterData r; + uint32_t data_needed; + uint32_t value_needed; + enum winreg_Type type; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.handle = handle; + r.in.enum_index = enum_index; + r.in.value_offered = value_offered; + r.in.data_offered = data_offered; + r.out.data_needed = &data_needed; + r.out.value_needed = &value_needed; + r.out.type = &type; + r.out.data = talloc_zero_array(tctx, uint8_t, r.in.data_offered); + r.out.value_name = talloc_zero_array(tctx, const char, r.in.value_offered); + + torture_comment(tctx, "Testing EnumPrinterData(%d)\n", enum_index); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinterData_r(b, tctx, &r), + "EnumPrinterData failed"); + + if (type_p) { + *type_p = type; + } + if (value_needed_p) { + *value_needed_p = value_needed; + } + if (data_needed_p) { + *data_needed_p = data_needed; + } + if (value_name_p) { + *value_name_p = r.out.value_name; + } + if (data_p) { + *data_p = r.out.data; + } + if (result_p) { + *result_p = r.out.result; + } + + return true; +} + + +static bool test_EnumPrinterData_all(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + uint32_t enum_index = 0; + enum winreg_Type type; + uint32_t value_needed; + uint32_t data_needed; + uint8_t *data; + const char *value_name; + WERROR result; + + torture_comment(tctx, "Testing EnumPrinterData\n"); + + do { + torture_assert(tctx, + test_EnumPrinterData(tctx, p, handle, enum_index, 0, 0, + &type, &value_needed, &data_needed, + &value_name, &data, &result), + "EnumPrinterData failed"); + + if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) { + break; + } + + torture_assert(tctx, + test_EnumPrinterData(tctx, p, handle, enum_index, value_needed, data_needed, + &type, &value_needed, &data_needed, + &value_name, &data, &result), + "EnumPrinterData failed"); + + if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) { + break; + } + + enum_index++; + + } while (W_ERROR_IS_OK(result)); + + torture_comment(tctx, "EnumPrinterData test succeeded\n"); + + return true; +} + +static bool test_EnumPrinterDataEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + uint32_t *count_p, + struct spoolss_PrinterEnumValues **info_p) +{ + struct spoolss_EnumPrinterDataEx r; + struct spoolss_PrinterEnumValues *info; + uint32_t needed; + uint32_t count; + + r.in.handle = handle; + r.in.key_name = key_name; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key_name); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r), + "EnumPrinterDataEx failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r), + "EnumPrinterDataEx failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDataEx failed"); + + CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, needed, 1); + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + return true; +} + +static bool test_SetPrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type type, + uint8_t *data, + uint32_t offered); +static bool test_DeletePrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name); + +static bool test_EnumPrinterData_consistency(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + uint32_t count; + struct spoolss_PrinterEnumValues *info; + int i; + uint32_t value_needed, data_needed; + uint32_t value_offered, data_offered; + WERROR result; + struct dcerpc_binding_handle *b = p->binding_handle; + + enum winreg_Type type; + DATA_BLOB blob; + + torture_comment(tctx, "Testing EnumPrinterData vs EnumPrinterDataEx consistency\n"); + + torture_assert(tctx, push_reg_sz(tctx, &blob, "torture_data1"), ""); + type = REG_SZ; + + torture_assert(tctx, + test_SetPrinterData(tctx, b, handle, "torture_value1", type, blob.data, blob.length), + "SetPrinterData failed"); + + blob = data_blob_string_const("torture_data2"); + + torture_assert(tctx, + test_SetPrinterData(tctx, b, handle, "torture_value2", REG_BINARY, blob.data, blob.length), + "SetPrinterData failed"); + + blob = data_blob_talloc(tctx, NULL, 4); + SIVAL(blob.data, 0, 0x11223344); + + torture_assert(tctx, + test_SetPrinterData(tctx, b, handle, "torture_value3", type, blob.data, blob.length), + "SetPrinterData failed"); + + torture_assert(tctx, + test_EnumPrinterDataEx(tctx, b, handle, "PrinterDriverData", &count, &info), + "failed to call EnumPrinterDataEx"); + + /* get the max sizes for value and data */ + + torture_assert(tctx, + test_EnumPrinterData(tctx, p, handle, 0, 0, 0, + NULL, &value_needed, &data_needed, + NULL, NULL, &result), + "EnumPrinterData failed"); + torture_assert_werr_ok(tctx, result, "unexpected result"); + + /* check if the reply from the EnumPrinterData really matches max values */ + + for (i=0; i < count; i++) { + if (info[i].value_name_len > value_needed) { + torture_fail(tctx, + talloc_asprintf(tctx, + "EnumPrinterDataEx gave a reply with value length %d which is larger then expected max value length %d from EnumPrinterData", + info[i].value_name_len, value_needed)); + } + if (info[i].data_length > data_needed) { + torture_fail(tctx, + talloc_asprintf(tctx, + "EnumPrinterDataEx gave a reply with data length %d which is larger then expected max data length %d from EnumPrinterData", + info[i].data_length, data_needed)); + } + } + + /* assuming that both EnumPrinterData and EnumPrinterDataEx do either + * sort or not sort the replies by value name, we should be able to do + * the following entry comparison */ + + data_offered = data_needed; + value_offered = value_needed; + + for (i=0; i < count; i++) { + + const char *value_name; + uint8_t *data; + + torture_assert(tctx, + test_EnumPrinterData(tctx, p, handle, i, value_offered, data_offered, + &type, &value_needed, &data_needed, + &value_name, &data, &result), + "EnumPrinterData failed"); + + if (i -1 == count) { + torture_assert_werr_equal(tctx, result, WERR_NO_MORE_ITEMS, + "unexpected result"); + break; + } else { + torture_assert_werr_ok(tctx, result, "unexpected result"); + } + + torture_assert_int_equal(tctx, type, info[i].type, "type mismatch"); + torture_assert_int_equal(tctx, value_needed, info[i].value_name_len, "value name length mismatch"); + torture_assert_str_equal(tctx, value_name, info[i].value_name, "value name mismatch"); + torture_assert_int_equal(tctx, data_needed, info[i].data_length, "data length mismatch"); + torture_assert_mem_equal(tctx, data, info[i].data->data, info[i].data_length, "data mismatch"); + } + + torture_assert(tctx, + test_DeletePrinterData(tctx, b, handle, "torture_value1"), + "DeletePrinterData failed"); + torture_assert(tctx, + test_DeletePrinterData(tctx, b, handle, "torture_value2"), + "DeletePrinterData failed"); + torture_assert(tctx, + test_DeletePrinterData(tctx, b, handle, "torture_value3"), + "DeletePrinterData failed"); + + torture_comment(tctx, "EnumPrinterData vs EnumPrinterDataEx consistency test succeeded\n\n"); + + return true; +} + +static bool test_DeletePrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name) +{ + NTSTATUS status; + struct spoolss_DeletePrinterData r; + + r.in.handle = handle; + r.in.value_name = value_name; + + torture_comment(tctx, "Testing DeletePrinterData(%s)\n", + r.in.value_name); + + status = dcerpc_spoolss_DeletePrinterData_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed"); + torture_assert_werr_ok(tctx, r.out.result, "DeletePrinterData failed"); + + return true; +} + +static bool test_DeletePrinterDataEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char *value_name) +{ + struct spoolss_DeletePrinterDataEx r; + + r.in.handle = handle; + r.in.key_name = key_name; + r.in.value_name = value_name; + + torture_comment(tctx, "Testing DeletePrinterDataEx(%s - %s)\n", + r.in.key_name, r.in.value_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrinterDataEx_r(b, tctx, &r), + "DeletePrinterDataEx failed"); + torture_assert_werr_ok(tctx, r.out.result, + "DeletePrinterDataEx failed"); + + return true; +} + +static bool test_DeletePrinterKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name) +{ + struct spoolss_DeletePrinterKey r; + + r.in.handle = handle; + r.in.key_name = key_name; + + torture_comment(tctx, "Testing DeletePrinterKey(%s)\n", r.in.key_name); + + if (strequal(key_name, "") && !torture_setting_bool(tctx, "dangerous", false)) { + torture_skip(tctx, "not wiping out printer registry - enable dangerous tests to use\n"); + return true; + } + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrinterKey_r(b, tctx, &r), + "DeletePrinterKey failed"); + torture_assert_werr_ok(tctx, r.out.result, + "DeletePrinterKey failed"); + + return true; +} + +static bool test_winreg_OpenHKLM(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct winreg_OpenHKLM r; + + r.in.system_name = NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = handle; + + torture_comment(tctx, "Testing winreg_OpenHKLM\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenHKLM_r(b, tctx, &r), "OpenHKLM failed"); + torture_assert_werr_ok(tctx, r.out.result, "OpenHKLM failed"); + + return true; +} + +static void init_winreg_String(struct winreg_String *name, const char *s) +{ + name->name = s; + if (s) { + name->name_len = 2 * (strlen_m(s) + 1); + name->name_size = name->name_len; + } else { + name->name_len = 0; + name->name_size = 0; + } +} + +static bool test_winreg_OpenKey_opts(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *hive_handle, + const char *keyname, + uint32_t options, + struct policy_handle *key_handle) +{ + struct winreg_OpenKey r; + + r.in.parent_handle = hive_handle; + init_winreg_String(&r.in.keyname, keyname); + r.in.options = options; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = key_handle; + + torture_comment(tctx, "Testing winreg_OpenKey(%s)\n", keyname); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey_r(b, tctx, &r), "OpenKey failed"); + torture_assert_werr_ok(tctx, r.out.result, "OpenKey failed"); + + return true; +} + +static bool test_winreg_OpenKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *hive_handle, + const char *keyname, + struct policy_handle *key_handle) +{ + return test_winreg_OpenKey_opts(tctx, b, hive_handle, keyname, + REG_OPTION_NON_VOLATILE, key_handle); +} + +static bool test_winreg_CloseKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct winreg_CloseKey r; + + r.in.handle = handle; + r.out.handle = handle; + + torture_comment(tctx, "Testing winreg_CloseKey\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r), "CloseKey failed"); + torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed"); + + return true; +} + +bool test_winreg_QueryValue(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type *type_p, + uint32_t *data_size_p, + uint32_t *data_length_p, + uint8_t **data_p) +{ + struct winreg_QueryValue r; + enum winreg_Type type = REG_NONE; + uint32_t data_size = 0; + uint32_t data_length = 0; + struct winreg_String valuename; + uint8_t *data = NULL; + + init_winreg_String(&valuename, value_name); + + data = talloc_zero_array(tctx, uint8_t, 0); + + r.in.handle = handle; + r.in.value_name = &valuename; + r.in.type = &type; + r.in.data_size = &data_size; + r.in.data_length = &data_length; + r.in.data = data; + r.out.type = &type; + r.out.data = data; + r.out.data_size = &data_size; + r.out.data_length = &data_length; + + torture_comment(tctx, "Testing winreg_QueryValue(%s)\n", value_name); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + *r.in.data_size = *r.out.data_size; + data = talloc_zero_array(tctx, uint8_t, *r.in.data_size); + r.in.data = data; + r.out.data = data; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed"); + } + torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed"); + + if (type_p) { + *type_p = *r.out.type; + } + if (data_size_p) { + *data_size_p = *r.out.data_size; + } + if (data_length_p) { + *data_length_p = *r.out.data_length; + } + if (data_p) { + *data_p = r.out.data; + } + + return true; +} + +static bool test_winreg_query_printerdata(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *printer_name, + const char *key_name, + const char *value_name, + enum winreg_Type *w_type, + uint32_t *w_size, + uint32_t *w_length, + uint8_t **w_data) +{ + const char *printer_key; + struct policy_handle key_handle; + + printer_key = talloc_asprintf(tctx, "%s\\%s\\%s", + TOP_LEVEL_PRINT_PRINTERS_KEY, printer_name, key_name); + + torture_assert(tctx, + test_winreg_OpenKey(tctx, b, handle, printer_key, &key_handle), ""); + + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, &key_handle, value_name, w_type, w_size, w_length, w_data), ""); + + torture_assert(tctx, + test_winreg_CloseKey(tctx, b, &key_handle), ""); + + return true; +} + +static bool test_GetForm_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char *form_name, + enum winreg_Type *w_type, + uint32_t *w_size, + uint32_t *w_length, + uint8_t **w_data) +{ + struct policy_handle key_handle; + + torture_assert(tctx, + test_winreg_OpenKey(tctx, b, handle, key_name, &key_handle), ""); + + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, &key_handle, form_name, w_type, w_size, w_length, w_data), ""); + + torture_assert(tctx, + test_winreg_CloseKey(tctx, b, &key_handle), ""); + + return true; +} + +static bool test_winreg_symbolic_link(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *symlink_keyname, + const char *symlink_destination) +{ + /* check if the first key is a symlink to the second key */ + + enum winreg_Type w_type; + uint32_t w_size; + uint32_t w_length; + uint8_t *w_data; + struct policy_handle key_handle; + DATA_BLOB blob; + const char *str; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skip winreg symlink test against samba"); + } + + torture_assert(tctx, + test_winreg_OpenKey_opts(tctx, b, handle, symlink_keyname, REG_OPTION_OPEN_LINK, &key_handle), + "failed to open key link"); + + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, &key_handle, + "SymbolicLinkValue", + &w_type, &w_size, &w_length, &w_data), + "failed to query for 'SymbolicLinkValue' attribute"); + + torture_assert_int_equal(tctx, w_type, REG_LINK, "unexpected type"); + + blob = data_blob(w_data, w_size); + str = reg_val_data_string(tctx, REG_SZ, blob); + + torture_assert_str_equal(tctx, str, symlink_destination, "unexpected symlink target string"); + + torture_assert(tctx, + test_winreg_CloseKey(tctx, b, &key_handle), + "failed to close key link"); + + return true; +} + +static const char *strip_unc(const char *unc) +{ + char *name; + + if (!unc) { + return NULL; + } + + if (unc[0] == '\\' && unc[1] == '\\') { + unc +=2; + } + + name = strchr(unc, '\\'); + if (name) { + return name+1; + } + + return unc; +} + +static bool test_GetPrinterInfo_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *printer_name, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + union spoolss_PrinterInfo info; + const char *keys[] = { + TOP_LEVEL_CONTROL_PRINTERS_KEY, + TOP_LEVEL_PRINT_PRINTERS_KEY + }; + int i; + const char *printername, *sharename; + + torture_comment(tctx, "Testing Printer Info and winreg consistency\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to get printer info level 2"); + + printername = strip_unc(info.info2.printername); + sharename = strip_unc(info.info2.sharename); + +#define test_sz(wname, iname) \ +do {\ + DATA_BLOB blob;\ + const char *str;\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_SZ, "unexpected type");\ + blob = data_blob(w_data, w_size);\ + str = reg_val_data_string(tctx, REG_SZ, blob);\ + if (w_size == 2 && iname == NULL) {\ + /*torture_comment(tctx, "%s: \"\", %s: (null)\n", #wname, #iname);\ */\ + } else {\ + torture_assert_str_equal(tctx, str, iname,\ + talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\ + }\ +} while(0); + +#define test_dword(wname, iname) \ +do {\ + uint32_t value;\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_DWORD, "unexpected type");\ + torture_assert_int_equal(tctx, w_size, 4, "unexpected size");\ + torture_assert_int_equal(tctx, w_length, 4, "unexpected length");\ + value = IVAL(w_data, 0);\ + torture_assert_int_equal(tctx, value, iname,\ + talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\ +} while(0); + +#define test_binary(wname, iname) \ +do {\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\ + torture_assert_int_equal(tctx, w_size, iname.length, "unexpected length");\ + torture_assert_mem_equal(tctx, w_data, iname.data, w_size, \ + "binary unequal");\ +} while(0); + + +#define test_dm(wname, iname) \ +do {\ + DATA_BLOB blob;\ + struct spoolss_DeviceMode dm;\ + enum ndr_err_code ndr_err;\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\ + blob = data_blob(w_data, w_size);\ + ndr_err = ndr_pull_struct_blob(&blob, tctx, &dm,\ + (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);\ + torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall dm");\ + torture_assert(tctx, test_devicemode_equal(tctx, &dm, iname),\ + "dm unequal");\ +} while(0); + +#define test_sd(wname, iname) \ +do {\ + DATA_BLOB blob;\ + struct security_descriptor sd;\ + enum ndr_err_code ndr_err;\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\ + blob = data_blob(w_data, w_size);\ + ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd,\ + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);\ + torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall sd");\ + torture_assert(tctx, test_security_descriptor_equal(tctx, &sd, iname),\ + "sd unequal");\ +} while(0); + +#define test_multi_sz(wname, iname) \ +do {\ + DATA_BLOB blob;\ + const char **array;\ + enum winreg_Type w_type;\ + uint32_t w_size;\ + uint32_t w_length;\ + uint8_t *w_data;\ + int i;\ + torture_assert(tctx,\ + test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\ + &w_type, &w_size, &w_length, &w_data),\ + "failed to query winreg");\ + torture_assert_int_equal(tctx, w_type, REG_MULTI_SZ, "unexpected type");\ + blob = data_blob(w_data, w_size);\ + torture_assert(tctx, \ + pull_reg_multi_sz(tctx, &blob, &array),\ + "failed to pull multi sz");\ + for (i=0; array[i] != NULL; i++) {\ + torture_assert_str_equal(tctx, array[i], iname[i],\ + talloc_asprintf(tctx, "%s - %s mismatch", #wname, iname[i]));\ + }\ +} while(0); + + if (!test_winreg_symbolic_link(tctx, winreg_handle, hive_handle, + TOP_LEVEL_CONTROL_PRINTERS_KEY, + "\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers")) + { + torture_warning(tctx, "failed to check for winreg symlink"); + } + + for (i=0; i < ARRAY_SIZE(keys); i++) { + + const char *printer_key; + struct policy_handle key_handle; + + printer_key = talloc_asprintf(tctx, "%s\\%s", + keys[i], printer_name); + + torture_assert(tctx, + test_winreg_OpenKey(tctx, winreg_handle, hive_handle, printer_key, &key_handle), ""); + + test_sz("Name", printername); + test_sz("Share Name", sharename); + test_sz("Port", info.info2.portname); + test_sz("Printer Driver", info.info2.drivername); + test_sz("Description", info.info2.comment); + test_sz("Location", info.info2.location); + test_sz("Separator File", info.info2.sepfile); + test_sz("Print Processor", info.info2.printprocessor); + test_sz("Datatype", info.info2.datatype); + test_sz("Parameters", info.info2.parameters); + /* winreg: 0, spoolss not */ +/* test_dword("Attributes", info.info2.attributes); */ + test_dword("Priority", info.info2.priority); + test_dword("Default Priority", info.info2.defaultpriority); + /* winreg: 60, spoolss: 0 */ +/* test_dword("StartTime", info.info2.starttime); */ +/* test_dword("UntilTime", info.info2.untiltime); */ + /* winreg != spoolss */ +/* test_dword("Status", info.info2.status); */ + test_dm("Default DevMode", info.info2.devmode); + test_sd("Security", info.info2.secdesc); + + torture_assert(tctx, + test_winreg_CloseKey(tctx, winreg_handle, &key_handle), ""); + } + +#undef test_dm + + torture_comment(tctx, "Printer Info and winreg consistency test succeeded\n\n"); + + return true; +} + +static bool test_GetPrintserverInfo_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + union spoolss_PrinterInfo info; + struct policy_handle key_handle; + + torture_comment(tctx, + "Testing Printserver Info and winreg consistency\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 3, &info), + "failed to get printer info level 2"); + + torture_assert(tctx, + test_winreg_OpenKey(tctx, winreg_handle, hive_handle, + TOP_LEVEL_CONTROL_KEY, &key_handle), ""); + + test_sd("ServerSecurityDescriptor", info.info3.secdesc); + + torture_assert(tctx, + test_winreg_CloseKey(tctx, winreg_handle, &key_handle), ""); + +#undef test_sd + + torture_comment(tctx, + "Printserver Info and winreg consistency test succeeded\n\n"); + + return true; +} + + +static bool test_PrintProcessors(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *environment, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + union spoolss_PrintProcessorInfo *info; + uint32_t count; + int i; + + torture_comment(tctx, "Testing Print Processor Info and winreg consistency\n"); + + torture_assert(tctx, + test_EnumPrintProcessors_level(tctx, b, environment, 1, &count, &info, WERR_OK), + "failed to enum print processors level 1"); + + for (i=0; i < count; i++) { + + const char *processor_key; + struct policy_handle key_handle; + + processor_key = talloc_asprintf(tctx, "%s\\%s\\Print Processors\\%s", + TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY, + environment, + info[i].info1.print_processor_name); + + torture_assert(tctx, + test_winreg_OpenKey(tctx, winreg_handle, hive_handle, processor_key, &key_handle), ""); + + /* nothing to check in there so far */ + + torture_assert(tctx, + test_winreg_CloseKey(tctx, winreg_handle, &key_handle), ""); + } + + torture_comment(tctx, "Print Processor Info and winreg consistency test succeeded\n\n"); + + return true; +} + +static bool test_GetPrinterDriver2_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *driver_name, + const char *architecture, + uint32_t level, + uint32_t client_major_version, + uint32_t client_minor_version, + union spoolss_DriverInfo *info_p, + WERROR *result); + +static const char *strip_path(const char *path) +{ + char *p; + + if (path == NULL) { + return NULL; + } + + p = strrchr(path, '\\'); + if (p) { + return p+1; + } + + return path; +} + +static const char **strip_paths(const char **path_array) +{ + int i; + + if (path_array == NULL) { + return NULL; + } + + for (i=0; path_array[i] != NULL; i++) { + path_array[i] = strip_path(path_array[i]); + } + + return path_array; +} + +static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt) +{ + time_t t; + struct tm *tm; + + if (nt == 0) { + return talloc_strdup(mem_ctx, "01/01/1601"); + } + + t = nt_time_to_unix(nt); + tm = localtime(&t); + + return talloc_asprintf(mem_ctx, "%02d/%02d/%04d", + tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900); +} + +static const char *driver_winreg_version(TALLOC_CTX *mem_ctx, uint64_t v) +{ + return talloc_asprintf(mem_ctx, "%u.%u.%u.%u", + (unsigned)((v >> 48) & 0xFFFF), + (unsigned)((v >> 32) & 0xFFFF), + (unsigned)((v >> 16) & 0xFFFF), + (unsigned)(v & 0xFFFF)); +} + +static bool test_GetDriverInfo_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *printer_name, + const char *driver_name, + const char *environment, + enum spoolss_DriverOSVersion version, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle, + const char *server_name_slash) +{ + WERROR result = WERR_OK; + union spoolss_DriverInfo info; + const char *driver_key; + struct policy_handle key_handle; + + const char *driver_path; + const char *data_file; + const char *config_file; + const char *help_file; + const char **dependent_files; + + const char *driver_date; + const char *inbox_driver_date; + + const char *driver_version; + const char *inbox_driver_version; + + ZERO_STRUCT(key_handle); + + torture_comment(tctx, "Testing Driver Info and winreg consistency\n"); + + driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s", + TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY, + environment, + version, + driver_name); + + torture_assert(tctx, + test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle), + "failed to open driver key"); + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "w2k3", false)) { + goto try_level6; + } + + if (handle) { + torture_assert(tctx, + test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, version, 0, &info, &result), + "failed to get driver info level 8"); + } else { + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 8, driver_name, &info), + "failed to get driver info level 8"); + } + + if (W_ERROR_EQUAL(result, WERR_INVALID_LEVEL)) { + goto try_level6; + } + + driver_path = strip_path(info.info8.driver_path); + data_file = strip_path(info.info8.data_file); + config_file = strip_path(info.info8.config_file); + help_file = strip_path(info.info8.help_file); + dependent_files = strip_paths(info.info8.dependent_files); + + driver_date = driver_winreg_date(tctx, info.info8.driver_date); + inbox_driver_date = driver_winreg_date(tctx, info.info8.min_inbox_driver_ver_date); + + driver_version = driver_winreg_version(tctx, info.info8.driver_version); + inbox_driver_version = driver_winreg_version(tctx, info.info8.min_inbox_driver_ver_version); + + test_sz("Configuration File", config_file); + test_sz("Data File", data_file); + test_sz("Datatype", info.info8.default_datatype); + test_sz("Driver", driver_path); + test_sz("DriverDate", driver_date); + test_sz("DriverVersion", driver_version); + test_sz("HardwareID", info.info8.hardware_id); + test_sz("Help File", help_file); + test_sz("InfPath", info.info8.inf_path); + test_sz("Manufacturer", info.info8.manufacturer_name); + test_sz("MinInboxDriverVerDate", inbox_driver_date); + test_sz("MinInboxDriverVerVersion", inbox_driver_version); + test_sz("Monitor", info.info8.monitor_name); + test_sz("OEM URL", info.info8.manufacturer_url); + test_sz("Print Processor", info.info8.print_processor); + test_sz("Provider", info.info8.provider); + test_sz("VendorSetup", info.info8.vendor_setup); + test_multi_sz("ColorProfiles", info.info8.color_profiles); + test_multi_sz("Dependent Files", dependent_files); + test_multi_sz("CoreDependencies", info.info8.core_driver_dependencies); + test_multi_sz("Previous Names", info.info8.previous_names); +/* test_dword("Attributes", ?); */ + test_dword("PrinterDriverAttributes", info.info8.printer_driver_attributes); + test_dword("Version", info.info8.version); +/* test_dword("TempDir", ?); */ + + try_level6: + + if (handle) { + torture_assert(tctx, + test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, version, 0, &info, &result), + "failed to get driver info level 6"); + } else { + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 6, driver_name, &info), + "failed to get driver info level 6"); + } + + driver_path = strip_path(info.info6.driver_path); + data_file = strip_path(info.info6.data_file); + config_file = strip_path(info.info6.config_file); + help_file = strip_path(info.info6.help_file); + dependent_files = strip_paths(info.info6.dependent_files); + + driver_date = driver_winreg_date(tctx, info.info6.driver_date); + + driver_version = driver_winreg_version(tctx, info.info6.driver_version); + + test_sz("Configuration File", config_file); + test_sz("Data File", data_file); + test_sz("Datatype", info.info6.default_datatype); + test_sz("Driver", driver_path); + if (torture_setting_bool(tctx, "w2k3", false)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, 8); + push_nttime(blob.data, 0, info.info6.driver_date); + test_binary("DriverDate", blob); + SBVAL(blob.data, 0, info.info6.driver_version); + test_binary("DriverVersion", blob); + } else { + test_sz("DriverDate", driver_date); + test_sz("DriverVersion", driver_version); + } + test_sz("HardwareID", info.info6.hardware_id); + test_sz("Help File", help_file); + test_sz("Manufacturer", info.info6.manufacturer_name); + test_sz("Monitor", info.info6.monitor_name); + test_sz("OEM URL", info.info6.manufacturer_url); + test_sz("Provider", info.info6.provider); + test_multi_sz("Dependent Files", dependent_files); + test_multi_sz("Previous Names", info.info6.previous_names); +/* test_dword("Attributes", ?); */ + test_dword("Version", info.info6.version); +/* test_dword("TempDir", ?); */ + + if (handle) { + torture_assert(tctx, + test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, version, 0, &info, &result), + "failed to get driver info level 3"); + } else { + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 3, driver_name, &info), + "failed to get driver info level 3"); + } + + driver_path = strip_path(info.info3.driver_path); + data_file = strip_path(info.info3.data_file); + config_file = strip_path(info.info3.config_file); + help_file = strip_path(info.info3.help_file); + dependent_files = strip_paths(info.info3.dependent_files); + + test_sz("Configuration File", config_file); + test_sz("Data File", data_file); + test_sz("Datatype", info.info3.default_datatype); + test_sz("Driver", driver_path); + test_sz("Help File", help_file); + test_sz("Monitor", info.info3.monitor_name); + test_multi_sz("Dependent Files", dependent_files); +/* test_dword("Attributes", ?); */ + test_dword("Version", info.info3.version); +/* test_dword("TempDir", ?); */ + + + torture_assert(tctx, + test_winreg_CloseKey(tctx, winreg_handle, &key_handle), ""); + + torture_comment(tctx, "Driver Info and winreg consistency test succeeded\n\n"); + + return true; +} + +#undef test_sz +#undef test_dword + +static bool test_SetPrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type type, + uint8_t *data, + uint32_t offered) +{ + struct spoolss_SetPrinterData r; + + r.in.handle = handle; + r.in.value_name = value_name; + r.in.type = type; + r.in.data = data; + r.in.offered = offered; + + torture_comment(tctx, "Testing SetPrinterData(%s)\n", + r.in.value_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_SetPrinterData_r(b, tctx, &r), + "SetPrinterData failed"); + torture_assert_werr_ok(tctx, r.out.result, + "SetPrinterData failed"); + + return true; +} + +static bool test_SetPrinterData_matrix(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *printer_name, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + const char *values[] = { + "spootyfoot", + "spooty\\foot", +#if 0 + /* FIXME: not working with s3 atm. */ + "spooty,foot", + "spooty,fo,ot", +#endif + "spooty foot", +#if 0 + /* FIXME: not working with s3 atm. */ + "spooty\\fo,ot", + "spooty,fo\\ot" +#endif + }; + int i; + + for (i=0; i < ARRAY_SIZE(values); i++) { + + enum winreg_Type type, expected_type = REG_SZ; + DATA_BLOB blob; + uint8_t *data; + uint32_t needed; + + torture_assert(tctx, push_reg_sz(tctx, &blob, "dog"), ""); + type = REG_SZ; + + torture_assert(tctx, + test_SetPrinterData(tctx, b, handle, values[i], REG_SZ, blob.data, blob.length), + "SetPrinterData failed"); + + torture_assert(tctx, + test_GetPrinterData_checktype(tctx, b, handle, values[i], &expected_type, &type, &data, &needed), + "GetPrinterData failed"); + + torture_assert_int_equal(tctx, type, REG_SZ, "type mismatch"); + torture_assert_int_equal(tctx, needed, blob.length, "size mismatch"); + torture_assert_mem_equal(tctx, data, blob.data, blob.length, "buffer mismatch"); + + if (winreg_handle && hive_handle) { + + enum winreg_Type w_type; + uint32_t w_size; + uint32_t w_length; + uint8_t *w_data; + + torture_assert(tctx, + test_winreg_query_printerdata(tctx, winreg_handle, hive_handle, + printer_name, "PrinterDriverData", values[i], + &w_type, &w_size, &w_length, &w_data), ""); + + torture_assert_int_equal(tctx, w_type, REG_SZ, "winreg type mismatch"); + torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch"); + torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch"); + torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch"); + } + + torture_assert(tctx, + test_DeletePrinterData(tctx, b, handle, values[i]), + "DeletePrinterData failed"); + } + + return true; +} + + +static bool test_EnumPrinterKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char ***array); + +static bool test_SetPrinterDataEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char *value_name, + enum winreg_Type type, + uint8_t *data, + uint32_t offered) +{ + NTSTATUS status; + struct spoolss_SetPrinterDataEx r; + + r.in.handle = handle; + r.in.key_name = key_name; + r.in.value_name = value_name; + r.in.type = type; + r.in.data = data; + r.in.offered = offered; + + torture_comment(tctx, "Testing SetPrinterDataEx(%s - %s) type: %s, offered: 0x%08x\n", + r.in.key_name, r.in.value_name, str_regtype(r.in.type), r.in.offered); + + status = dcerpc_spoolss_SetPrinterDataEx_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "SetPrinterDataEx failed"); + torture_assert_werr_ok(tctx, r.out.result, "SetPrinterDataEx failed"); + + return true; +} + +static bool test_SetPrinterDataEx_keys(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + const char *value_name = "dog"; + const char *keys[] = { + "torturedataex", + "torture data ex", + "torturedataex_with_subkey\\subkey", + "torturedataex_with_subkey\\subkey:0", + "torturedataex_with_subkey\\subkey:1", + "torturedataex_with_subkey\\subkey\\subsubkey", + "torturedataex_with_subkey\\subkey\\subsubkey:0", + "torturedataex_with_subkey\\subkey\\subsubkey:1", + "torture,data", + "torture,data,ex", + "torture,data\\ex", + "torture\\data,ex", + "torture/data", + "torture/data ex", + "torture/data ex/sub", + "torture//data", + "torture//data ex", + "torture//data ex/sub", + "torture//data ex//sub", + }; + int i; + + for (i=0; i < ARRAY_SIZE(keys); i++) { + + char *c; + const char *key; + enum winreg_Type type; + DATA_BLOB blob_in, blob_out; + const char **subkeys; + uint32_t ecount; + struct spoolss_PrinterEnumValues *einfo; + uint32_t needed; + + blob_in = data_blob_talloc(tctx, NULL, 42); + + generate_random_buffer(blob_in.data, blob_in.length); + + torture_assert(tctx, + test_SetPrinterDataEx(tctx, b, handle, keys[i], value_name, REG_BINARY, blob_in.data, blob_in.length), + "failed to call SetPrinterDataEx"); + + torture_assert(tctx, + test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &blob_out.data, &needed), + "failed to call GetPrinterDataEx"); + + blob_out.length = needed; + torture_assert(tctx, + test_EnumPrinterDataEx(tctx, b, handle, keys[i], &ecount, &einfo), + "failed to call EnumPrinterDataEx"); + + torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch"); + torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch"); + torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch"); + + torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count"); + torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch"); + torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len"); + torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch"); + torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch"); + if (einfo[0].data_length > 0) { + torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch"); + } + + key = talloc_strdup(tctx, keys[i]); + + if (!test_DeletePrinterDataEx(tctx, b, handle, keys[i], value_name)) { + return false; + } + + c = strchr(key, '\\'); + if (c) { + int k; + + /* we have subkeys */ + + *c = 0; + + if (!test_EnumPrinterKey(tctx, b, handle, key, &subkeys)) { + return false; + } + + for (k=0; subkeys && subkeys[k]; k++) { + + const char *current_key = talloc_asprintf(tctx, "%s\\%s", key, subkeys[k]); + + if (!test_DeletePrinterKey(tctx, b, handle, current_key)) { + return false; + } + } + + if (!test_DeletePrinterKey(tctx, b, handle, key)) { + return false; + } + + } else { + if (!test_DeletePrinterKey(tctx, b, handle, key)) { + return false; + } + } + } + + return true; +} + +static bool test_SetPrinterDataEx_values(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + const char *key = "torturedataex"; + const char *values[] = { + "torture_value", + "torture value", + "torture,value", + "torture/value", + "torture\\value", + "torture\\\\value" + }; + int i; + + for (i=0; i < ARRAY_SIZE(values); i++) { + + enum winreg_Type type = REG_NONE; + DATA_BLOB blob_in = data_blob_null; + DATA_BLOB blob_out = data_blob_null; + uint32_t ecount; + struct spoolss_PrinterEnumValues *einfo; + uint32_t needed = 0; + + if (torture_setting_bool(tctx, "samba3", false)) { + char *q; + q = strrchr(values[i], ','); + if (q) { + torture_comment(tctx, "skipping valuename '%s' including ',' character against Samba3\n", + values[i]); + continue; + } + } + + blob_in = data_blob_talloc(tctx, NULL, 42); + + generate_random_buffer(blob_in.data, blob_in.length); + + torture_assert(tctx, + test_SetPrinterDataEx(tctx, b, handle, key, values[i], REG_BINARY, blob_in.data, blob_in.length), + "failed to call SetPrinterDataEx"); + + torture_assert(tctx, + test_GetPrinterDataEx(tctx, p, handle, key, values[i], &type, &blob_out.data, &needed), + "failed to call GetPrinterDataEx"); + + blob_out.length = needed; + torture_assert(tctx, + test_EnumPrinterDataEx(tctx, b, handle, key, &ecount, &einfo), + "failed to call EnumPrinterDataEx"); + + torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch"); + torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch"); + torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch"); + + torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count"); + torture_assert_str_equal(tctx, einfo[0].value_name, values[i], "value_name mismatch"); + torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(values[i])*2, "unexpected value_name_len"); + torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch"); + torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch"); + if (einfo[0].data_length > 0) { + torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch"); + } + + torture_assert(tctx, + test_DeletePrinterDataEx(tctx, b, handle, key, values[i]), + "failed to call DeletePrinterDataEx"); + } + + return true; +} + + +static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printername, + struct dcerpc_binding_handle *winreg_handle, + struct policy_handle *hive_handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + const char *value_name = "dog"; + const char *key_name = "torturedataex"; + enum winreg_Type types[] = { + REG_SZ, + REG_MULTI_SZ, + REG_DWORD, + REG_BINARY + }; + const char *str = "abcdefghi"; + size_t t, s; + + for (t=0; t < ARRAY_SIZE(types); t++) { + for (s=0; s < strlen(str); s++) { + + enum winreg_Type type; + const char *string = talloc_strndup(tctx, str, s); + const char *array[2]; + DATA_BLOB blob = data_blob_string_const(string); + DATA_BLOB data; + uint8_t *data_out; + uint32_t needed, offered = 0; + uint32_t ecount; + struct spoolss_PrinterEnumValues *einfo; + + array[0] = talloc_strdup(tctx, string); + array[1] = NULL; + + if (types[t] == REG_DWORD) { + s = 0xffff; + } + + switch (types[t]) { + case REG_BINARY: + data = blob; + offered = blob.length; + break; + case REG_DWORD: + data = data_blob_talloc(tctx, NULL, 4); + SIVAL(data.data, 0, 0x12345678); + offered = 4; + break; + case REG_SZ: + torture_assert(tctx, push_reg_sz(tctx, &data, string), ""); + type = REG_SZ; + offered = data.length; + /*strlen_m_term(data.string)*2;*/ + break; + case REG_MULTI_SZ: + torture_assert(tctx, push_reg_multi_sz(tctx, &data, array), ""); + type = REG_MULTI_SZ; + offered = data.length; + break; + default: + torture_fail(tctx, talloc_asprintf(tctx, "type %d untested\n", types[t])); + } + + torture_assert(tctx, + test_SetPrinterDataEx(tctx, b, handle, key_name, value_name, types[t], data.data, offered), + "failed to call SetPrinterDataEx"); + + torture_assert(tctx, + test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name, &types[t], &type, &data_out, &needed), + "failed to call GetPrinterDataEx"); + + torture_assert(tctx, + test_EnumPrinterDataEx(tctx, b, handle, key_name, &ecount, &einfo), + "failed to call EnumPrinterDataEx"); + + torture_assert_int_equal(tctx, types[t], type, "type mismatch"); + torture_assert_int_equal(tctx, needed, offered, "size mismatch"); + torture_assert_mem_equal(tctx, data_out, data.data, offered, "buffer mismatch"); + + torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count"); + torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch"); + torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len"); + torture_assert_int_equal(tctx, einfo[0].type, types[t], "type mismatch"); + torture_assert_int_equal(tctx, einfo[0].data_length, offered, "size mismatch"); + if (einfo[0].data_length > 0) { + torture_assert_mem_equal(tctx, einfo[0].data->data, data.data, offered, "buffer mismatch"); + } + + if (winreg_handle && hive_handle) { + enum winreg_Type w_type; + uint32_t w_size; + uint32_t w_length; + uint8_t *w_data; + + torture_assert(tctx, + test_winreg_query_printerdata(tctx, winreg_handle, hive_handle, + printername, key_name, value_name, + &w_type, &w_size, &w_length, &w_data), ""); + + torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch"); + torture_assert_int_equal(tctx, w_size, offered, "winreg size mismatch"); + torture_assert_int_equal(tctx, w_length, offered, "winreg length mismatch"); + torture_assert_mem_equal(tctx, w_data, data.data, offered, "winreg buffer mismatch"); + } + + torture_assert(tctx, + test_DeletePrinterDataEx(tctx, b, handle, key_name, value_name), + "failed to call DeletePrinterDataEx"); + } + } + + return true; +} + +static bool test_PrinterData_winreg(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printer_name) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret &= test_SetPrinterData_matrix(tctx, b, handle, printer_name, b2, &hive_handle); + ret &= test_SetPrinterDataEx_matrix(tctx, p, handle, printer_name, b2, &hive_handle); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + +static bool test_Forms_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + bool print_server, + const char *printer_name) +{ + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + +static bool test_PrinterInfo_winreg(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printer_name) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_GetPrinterInfo_winreg(tctx, b, handle, printer_name, b2, &hive_handle); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + +static bool test_PrintserverInfo_winreg(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_GetPrintserverInfo_winreg(tctx, b, handle, b2, &hive_handle); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + + +static bool test_DriverInfo_winreg(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printer_name, + const char *driver_name, + const char *environment, + enum spoolss_DriverOSVersion version) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, version, b2, &hive_handle, NULL); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + +static bool test_PrintProcessors_winreg(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *environment) +{ + struct dcerpc_pipe *p2; + bool ret = true; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_PrintProcessors(tctx, b, environment, b2, &hive_handle); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + + return ret; +} + +static bool test_PrinterData_DsSpooler(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printer_name) +{ + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + union spoolss_SetPrinterInfo sinfo; + union spoolss_PrinterInfo info; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *pname; + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + torture_comment(tctx, "Testing DsSpooler <-> SetPrinter relations\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to query Printer level 2"); + + torture_assert(tctx, + PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), + "failed to convert"); + + info_ctr.level = 2; + info_ctr.info = sinfo; + +#define TEST_SZ(wname, iname) \ +do {\ + enum winreg_Type type;\ + uint8_t *data;\ + uint32_t needed;\ + DATA_BLOB blob;\ + const char *str;\ + torture_assert(tctx,\ + test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\ + "failed to query");\ + torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\ + blob = data_blob_const(data, needed);\ + torture_assert(tctx,\ + pull_reg_sz(tctx, &blob, &str),\ + "failed to pull REG_SZ");\ + torture_assert_str_equal(tctx, str, iname, "unexpected result");\ +} while(0); + + +#define TEST_SET_SZ(wname, iname, val) \ +do {\ + enum winreg_Type type;\ + uint8_t *data;\ + uint32_t needed;\ + DATA_BLOB blob;\ + const char *str;\ + sinfo.info2->iname = val;\ + torture_assert(tctx,\ + test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\ + "failed to call SetPrinter");\ + torture_assert(tctx,\ + test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\ + "failed to query");\ + torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\ + blob = data_blob_const(data, needed);\ + torture_assert(tctx,\ + pull_reg_sz(tctx, &blob, &str),\ + "failed to pull REG_SZ");\ + torture_assert_str_equal(tctx, str, val, "unexpected result");\ +} while(0); + +#define TEST_SET_DWORD(wname, iname, val) \ +do {\ + enum winreg_Type type;\ + uint8_t *data;\ + uint32_t needed;\ + uint32_t value;\ + sinfo.info2->iname = val;\ + torture_assert(tctx,\ + test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\ + "failed to call SetPrinter");\ + torture_assert(tctx,\ + test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\ + "failed to query");\ + torture_assert_int_equal(tctx, type, REG_DWORD, "unexpected type");\ + torture_assert_int_equal(tctx, needed, 4, "unexpected length");\ + value = IVAL(data, 0); \ + torture_assert_int_equal(tctx, value, val, "unexpected result");\ +} while(0); + + TEST_SET_SZ("description", comment, "newval"); + TEST_SET_SZ("location", location, "newval"); + TEST_SET_SZ("driverName", drivername, "newval"); +/* TEST_SET_DWORD("priority", priority, 25); */ + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to query Printer level 2"); + + TEST_SZ("description", info.info2.comment); + TEST_SZ("driverName", info.info2.drivername); + TEST_SZ("location", info.info2.location); + + pname = strrchr(info.info2.printername, '\\'); + if (pname == NULL) { + pname = info.info2.printername; + } else { + pname++; + } + TEST_SZ("printerName", pname); + /* TEST_SZ("printSeparatorFile", info.info2.sepfile); */ + /* TEST_SZ("printShareName", info.info2.sharename); */ + + /* FIXME gd: complete the list */ + +#undef TEST_SZ +#undef TEST_SET_SZ +#undef TEST_DWORD + + torture_comment(tctx, "DsSpooler <-> SetPrinter relations test succeeded\n\n"); + + return true; +} + +static bool test_print_processors_winreg(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_PrintProcessors_winreg(tctx, b, ctx->environment); +} + +static bool test_AddPrintProcessor(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *environment, + const char *path_name, + const char *print_processor_name, + WERROR expected_error) +{ + struct spoolss_AddPrintProcessor r; + + r.in.server = NULL; + r.in.architecture = environment; + r.in.path_name = path_name; + r.in.print_processor_name = print_processor_name; + + torture_comment(tctx, "Testing AddPrintProcessor(%s)\n", + print_processor_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_AddPrintProcessor_r(b, tctx, &r), + "spoolss_AddPrintProcessor failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_error, + "spoolss_AddPrintProcessor failed"); + + return true; +} + +static bool test_DeletePrintProcessor(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *environment, + const char *print_processor_name, + WERROR expected_error) +{ + struct spoolss_DeletePrintProcessor r; + + r.in.server = NULL; + r.in.architecture = environment; + r.in.print_processor_name = print_processor_name; + + torture_comment(tctx, "Testing DeletePrintProcessor(%s)\n", + print_processor_name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrintProcessor_r(b, tctx, &r), + "spoolss_DeletePrintProcessor failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_error, + "spoolss_DeletePrintProcessor failed"); + + return true; +} + +static bool test_add_print_processor(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + int i; + + struct { + const char *environment; + const char *path_name; + const char *print_processor_name; + WERROR expected_add_result; + WERROR expected_del_result; + } tests[] = { + { + .environment = ctx->environment, + .path_name = "", + .print_processor_name = "winprint", + .expected_add_result = WERR_PRINT_PROCESSOR_ALREADY_INSTALLED, + .expected_del_result = WERR_CAN_NOT_COMPLETE + },{ + .environment = ctx->environment, + .path_name = "", + .print_processor_name = "unknown", + .expected_add_result = WERR_MOD_NOT_FOUND, + .expected_del_result = WERR_UNKNOWN_PRINTPROCESSOR + } + }; + + for (i=0; i < ARRAY_SIZE(tests); i++) { + torture_assert(tctx, + test_AddPrintProcessor(tctx, b, + tests[i].environment, + tests[i].path_name, + tests[i].print_processor_name, + tests[i].expected_add_result), + "add print processor failed"); + torture_assert(tctx, + test_DeletePrintProcessor(tctx, b, + tests[i].environment, + tests[i].print_processor_name, + tests[i].expected_del_result), + "delete print processor failed"); + } + + return true; +} + +static bool test_AddPerMachineConnection(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, + const char *printername, + const char *printserver, + const char *provider, + WERROR expected_error) +{ + struct spoolss_AddPerMachineConnection r; + const char *composed_printername = printername; + + if (servername != NULL) { + composed_printername = talloc_asprintf(tctx, "%s\\%s", + servername, + printername); + } + r.in.server = servername; + r.in.printername = composed_printername; + r.in.printserver = printserver; + r.in.provider = provider; + + torture_comment(tctx, "Testing AddPerMachineConnection(%s|%s|%s)\n", + printername, printserver, provider); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_AddPerMachineConnection_r(b, tctx, &r), + "spoolss_AddPerMachineConnection failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_error, + "spoolss_AddPerMachineConnection failed"); + + return true; +} + +static bool test_DeletePerMachineConnection(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, + const char *printername, + WERROR expected_error) +{ + struct spoolss_DeletePerMachineConnection r; + const char *composed_printername = printername; + + if (servername != NULL) { + composed_printername = talloc_asprintf(tctx, "%s\\%s", + servername, + printername); + } + + r.in.server = servername; + r.in.printername = composed_printername; + + torture_comment(tctx, "Testing DeletePerMachineConnection(%s)\n", + printername); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePerMachineConnection_r(b, tctx, &r), + "spoolss_DeletePerMachineConnection failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_error, + "spoolss_DeletePerMachineConnection failed"); + + return true; +} + +static bool test_EnumPerMachineConnections(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername) +{ + struct spoolss_EnumPerMachineConnections r; + DATA_BLOB blob = data_blob_null; + struct spoolss_PrinterInfo4 *info; + uint32_t needed; + uint32_t count; + + r.in.server = servername; + r.in.buffer = &blob; + r.in.offered = 0; + + r.out.info = &info; + r.out.needed = &needed; + r.out.count = &count; + + torture_comment(tctx, "Testing EnumPerMachineConnections(%s)\n", + servername); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r), + "spoolss_EnumPerMachineConnections failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r), + "spoolss_EnumPerMachineConnections failed"); + } + torture_assert_werr_ok(tctx, r.out.result, + "spoolss_EnumPerMachineConnections failed"); + + return true; +} + +static bool test_addpermachineconnection(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + int i; + + struct { + const char *servername; + const char *printername; + const char *printserver; + const char *provider; + WERROR expected_add_result; + WERROR expected_del_result; + } tests[] = { + { + .servername = NULL, + .printername = "foo", + .printserver = "", + .provider = "unknown", + .expected_add_result = WERR_INVALID_PRINTER_NAME, + .expected_del_result = WERR_INVALID_PRINTER_NAME + },{ + .servername = NULL, + .printername = "Microsoft Print to PDF", + .printserver = "samba.org", + .provider = "unknown", + .expected_add_result = WERR_INVALID_PRINTER_NAME, + .expected_del_result = WERR_INVALID_PRINTER_NAME + },{ + .servername = NULL, + .printername = "Microsoft Print to PDF", + .printserver = "samba.org", + .provider = "", + .expected_add_result = WERR_INVALID_PRINTER_NAME, + .expected_del_result = WERR_INVALID_PRINTER_NAME + },{ + .servername = server_name_slash, + .printername = "foo", + .printserver = "", + .provider = "unknown", + .expected_add_result = WERR_FILE_NOT_FOUND, + .expected_del_result = WERR_INVALID_PRINTER_NAME + },{ + .servername = server_name_slash, + .printername = "foo", + .printserver = "", + .provider = "", + .expected_add_result = WERR_OK, + .expected_del_result = WERR_OK + },{ + .servername = server_name_slash, + .printername = "Microsoft Print to PDF", + .printserver = "samba.org", + .provider = "unknown", + .expected_add_result = WERR_FILE_NOT_FOUND, + .expected_del_result = WERR_INVALID_PRINTER_NAME + },{ + .servername = server_name_slash, + .printername = "Microsoft Print to PDF", + .printserver = "samba.org", + .provider = "", + .expected_add_result = WERR_OK, + .expected_del_result = WERR_OK + } + }; + + for (i=0; i < ARRAY_SIZE(tests); i++) { + torture_assert(tctx, + test_AddPerMachineConnection(tctx, b, + tests[i].servername, + tests[i].printername, + tests[i].printserver, + tests[i].provider, + tests[i].expected_add_result), + "add per machine connection failed"); + torture_assert(tctx, + test_EnumPerMachineConnections(tctx, b, + tests[i].servername), + "enum per machine connections failed"); + torture_assert(tctx, + test_DeletePerMachineConnection(tctx, b, + tests[i].servername, + tests[i].printername, + tests[i].expected_del_result), + "delete per machine connection failed"); + torture_assert(tctx, + test_EnumPerMachineConnections(tctx, b, + tests[i].servername), + "enum per machine connections failed"); + } + + return true; +} + +static bool test_GetChangeID_PrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t *change_id) +{ + enum winreg_Type type; + uint8_t *data; + uint32_t needed; + + torture_assert(tctx, + test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed), + "failed to call GetPrinterData"); + + torture_assert(tctx, type == REG_DWORD, "unexpected type"); + torture_assert_int_equal(tctx, needed, 4, "unexpected size"); + + *change_id = IVAL(data, 0); + + return true; +} + +static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + uint32_t *change_id) +{ + enum winreg_Type type = REG_NONE; + uint8_t *data = NULL; + uint32_t needed = 0; + + torture_assert(tctx, + test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data, &needed), + "failed to call GetPrinterData"); + + torture_assert(tctx, type == REG_DWORD, "unexpected type"); + torture_assert_int_equal(tctx, needed, 4, "unexpected size"); + + *change_id = IVAL(data, 0); + + return true; +} + +static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t *change_id) +{ + union spoolss_PrinterInfo info; + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 0, &info), + "failed to query Printer level 0"); + + *change_id = info.info0.change_id; + + return true; +} + +static bool test_ChangeID(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + uint32_t change_id, change_id_ex, change_id_info; + uint32_t change_id2, change_id_ex2, change_id_info2; + union spoolss_PrinterInfo info; + const char *comment; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing ChangeID: id change test #1\n"); + + torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info), + "failed to query for ChangeID"); + + torture_assert_int_equal(tctx, change_id, change_id_ex, + "change_ids should all be equal"); + torture_assert_int_equal(tctx, change_id_ex, change_id_info, + "change_ids should all be equal"); + + + torture_comment(tctx, "Testing ChangeID: id change test #2\n"); + + torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to query Printer level 2"); + torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info), + "failed to query for ChangeID"); + torture_assert_int_equal(tctx, change_id, change_id_ex, + "change_id should not have changed"); + torture_assert_int_equal(tctx, change_id_ex, change_id_info, + "change_id should not have changed"); + + + torture_comment(tctx, "Testing ChangeID: id change test #3\n"); + + torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), + "failed to query Printer level 2"); + comment = talloc_strdup(tctx, info.info2.comment); + + { + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + union spoolss_SetPrinterInfo sinfo; + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + + torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), ""); + sinfo.info2->comment = "torture_comment"; + + info_ctr.level = 2; + info_ctr.info = sinfo; + + torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), + "failed to call SetPrinter"); + + sinfo.info2->comment = comment; + + torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), + "failed to call SetPrinter"); + + } + + torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id2), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex2), + "failed to query for ChangeID"); + torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info2), + "failed to query for ChangeID"); + + torture_assert_int_equal(tctx, change_id2, change_id_ex2, + "change_ids should all be equal"); + torture_assert_int_equal(tctx, change_id_ex2, change_id_info2, + "change_ids should all be equal"); + + torture_assert(tctx, (change_id < change_id2), + talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d", + change_id2, change_id)); + torture_assert(tctx, (change_id_ex < change_id_ex2), + talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d", + change_id_ex2, change_id_ex)); + torture_assert(tctx, (change_id_info < change_id_info2), + talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d", + change_id_info2, change_id_info)); + + torture_comment(tctx, "ChangeID tests succeeded\n\n"); + + return true; +} + +static bool test_SecondaryClosePrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + NTSTATUS status; + struct cli_credentials *anon_creds; + const struct dcerpc_binding *binding2; + struct dcerpc_pipe *p2; + struct spoolss_ClosePrinter cp; + + /* only makes sense on SMB */ + if (p->conn->transport.transport != NCACN_NP) { + return true; + } + + torture_comment(tctx, "Testing close on secondary pipe\n"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed"); + + binding2 = p->binding; + status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_spoolss, + anon_creds, tctx->lp_ctx, + tctx, &p2); + torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); + + cp.in.handle = handle; + cp.out.handle = handle; + + status = dcerpc_spoolss_ClosePrinter_r(p2->binding_handle, tctx, &cp); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH, + "ERROR: Allowed close on secondary connection"); + + talloc_free(p2); + + return true; +} + +static bool test_OpenPrinter_badname(struct torture_context *tctx, + struct dcerpc_binding_handle *b, const char *name) +{ + NTSTATUS status; + struct spoolss_OpenPrinter op; + struct spoolss_OpenPrinterEx opEx; + struct policy_handle handle; + bool ret = true; + + op.in.printername = name; + op.in.datatype = NULL; + op.in.devmode_ctr.devmode= NULL; + op.in.access_mask = 0; + op.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinter(%s) with bad name\n", op.in.printername); + + status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &op); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed"); + torture_assert_werr_equal(tctx, op.out.result, WERR_INVALID_PRINTER_NAME, + "unexpected result"); + + if (W_ERROR_IS_OK(op.out.result)) { + ret &=test_ClosePrinter(tctx, b, &handle); + } + + opEx.in.printername = name; + opEx.in.datatype = NULL; + opEx.in.devmode_ctr.devmode = NULL; + opEx.in.access_mask = 0; + opEx.in.userlevel_ctr.level = 1; + opEx.in.userlevel_ctr.user_info.level1 = NULL; + opEx.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername); + + status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &opEx); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed"); + torture_assert_werr_equal(tctx, opEx.out.result, WERR_INVALID_PARAMETER, + "unexpected result"); + + if (W_ERROR_IS_OK(opEx.out.result)) { + ret &=test_ClosePrinter(tctx, b, &handle); + } + + return ret; +} + +static bool test_OpenPrinter_badname_list(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + const char *badnames[] = { + "__INVALID_PRINTER__", + "\\\\__INVALID_HOST__", + "", + "\\\\\\", + "\\\\\\__INVALID_PRINTER__" + }; + const char *badname; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + const char *server_name = dcerpc_server_name(p); + struct dcerpc_binding_handle *b = p->binding_handle; + int i; + + for (i=0; i < ARRAY_SIZE(badnames); i++) { + torture_assert(tctx, + test_OpenPrinter_badname(tctx, b, badnames[i]), + ""); + } + + badname = talloc_asprintf(tctx, "\\\\%s\\", server_name); + torture_assert(tctx, + test_OpenPrinter_badname(tctx, b, badname), + ""); + + badname = talloc_asprintf(tctx, "\\\\%s\\__INVALID_PRINTER__", server_name); + torture_assert(tctx, + test_OpenPrinter_badname(tctx, b, badname), + ""); + + return true; +} + +static bool test_OpenPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + const char *environment, + bool open_only) +{ + NTSTATUS status; + struct spoolss_OpenPrinter r; + struct policy_handle handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.printername = name; + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername); + + status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed"); + + torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed"); + + if (open_only) { + goto close_printer; + } + + if (!test_GetPrinter(tctx, b, &handle, environment)) { + ret = false; + } + + if (!torture_setting_bool(tctx, "samba3", false)) { + if (!test_SecondaryClosePrinter(tctx, p, &handle)) { + ret = false; + } + } + + close_printer: + if (!test_ClosePrinter(tctx, b, &handle)) { + ret = false; + } + + return ret; +} + +static bool test_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *printername, + const char *datatype, + struct spoolss_DeviceMode *devmode, + uint32_t access_mask, + struct spoolss_UserLevelCtr *userlevel_ctr, + struct policy_handle *handle, + WERROR expected_result) +{ + struct spoolss_OpenPrinterEx r; + + r.in.printername = printername; + r.in.datatype = datatype; + r.in.devmode_ctr.devmode= devmode; + r.in.access_mask = access_mask; + r.in.userlevel_ctr = *userlevel_ctr; + r.out.handle = handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), + "OpenPrinterEx failed"); + + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "OpenPrinterEx failed"); + + return true; +} + +static bool call_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + struct spoolss_DeviceMode *devmode, + struct policy_handle *handle) +{ + struct spoolss_UserLevelCtr userlevel_ctr; + struct spoolss_UserLevel1 userlevel1; + struct dcerpc_binding_handle *b = p->binding_handle; + + userlevel1.size = 1234; + userlevel1.client = "hello"; + userlevel1.user = "spottyfoot!"; + userlevel1.build = 1; + userlevel1.major = 2; + userlevel1.minor = 3; + userlevel1.processor = 4; + + userlevel_ctr.level = 1; + userlevel_ctr.user_info.level1 = &userlevel1; + + return test_OpenPrinterEx(tctx, b, name, NULL, devmode, + SEC_FLAG_MAXIMUM_ALLOWED, + &userlevel_ctr, + handle, + WERR_OK); +} + +static bool test_printer_rename(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + bool ret = true; + union spoolss_PrinterInfo info; + union spoolss_SetPrinterInfo sinfo; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + const char *printer_name; + const char *printer_name_orig; + const char *printer_name_new = "SAMBA smbtorture Test Printer (Copy 2)"; + struct policy_handle new_handle; + const char *q; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + torture_comment(tctx, "Testing Printer rename operations\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &t->handle, 2, &info), + "failed to call GetPrinter level 2"); + + printer_name_orig = talloc_strdup(tctx, info.info2.printername); + + q = strrchr(info.info2.printername, '\\'); + if (q) { + torture_warning(tctx, + "server returns printername %s incl. servername although we did not set servername", info.info2.printername); + } + + torture_assert(tctx, + PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), ""); + + sinfo.info2->printername = printer_name_new; + + info_ctr.level = 2; + info_ctr.info = sinfo; + + torture_assert(tctx, + test_SetPrinter(tctx, b, &t->handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), + "failed to call SetPrinter level 2"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &t->handle, 2, &info), + "failed to call GetPrinter level 2"); + + printer_name = talloc_strdup(tctx, info.info2.printername); + + q = strrchr(info.info2.printername, '\\'); + if (q) { + torture_warning(tctx, + "server returns printername %s incl. servername although we did not set servername", info.info2.printername); + q++; + printer_name = q; + } + + torture_assert_str_equal(tctx, printer_name, printer_name_new, + "new printer name was not set"); + + /* samba currently cannot fully rename printers */ + if (!torture_setting_bool(tctx, "samba3", false)) { + torture_assert(tctx, + test_OpenPrinter_badname(tctx, b, printer_name_orig), + "still can open printer with oldname after rename"); + } else { + torture_warning(tctx, "*not* checking for open with oldname after rename for samba3"); + } + + torture_assert(tctx, + call_OpenPrinterEx(tctx, p, printer_name_new, NULL, &new_handle), + "failed to open printer with new name"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &new_handle, 2, &info), + "failed to call GetPrinter level 2"); + + torture_assert_str_equal(tctx, info.info2.printername, printer_name_new, + "new printer name was not set"); + + torture_assert(tctx, + test_ClosePrinter(tctx, b, &new_handle), + "failed to close printer"); + + torture_comment(tctx, "Printer rename operations test succeeded\n\n"); + + return ret; +} + +static bool test_openprinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *real_printername) +{ + struct spoolss_UserLevelCtr userlevel_ctr; + struct policy_handle handle; + struct spoolss_UserLevel1 userlevel1; + const char *printername = NULL; + int i; + + struct { + const char *suffix; + WERROR expected_result; + } tests[] = { + { + .suffix = "rubbish", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", LocalOnl", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", localOnly", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", localonl", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ",LocalOnl", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ",localOnl2", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", DrvConver2t", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", drvconvert", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ",drvconvert", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ", DrvConvert", + .expected_result = WERR_OK + },{ + .suffix = " , DrvConvert", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ",DrvConvert", + .expected_result = WERR_OK + },{ + .suffix = ", DrvConvertsadfasdf", + .expected_result = WERR_OK + },{ + .suffix = ",DrvConvertasdfasd", + .expected_result = WERR_OK + },{ + .suffix = ", LocalOnly", + .expected_result = WERR_OK + },{ + .suffix = " , LocalOnly", + .expected_result = WERR_INVALID_PRINTER_NAME + },{ + .suffix = ",LocalOnly", + .expected_result = WERR_OK + },{ + .suffix = ", LocalOnlysagi4gjfkd", + .expected_result = WERR_OK + },{ + .suffix = ",LocalOnlysagi4gjfkd", + .expected_result = WERR_OK + } + }; + + userlevel1.size = 1234; + userlevel1.client = "hello"; + userlevel1.user = "spottyfoot!"; + userlevel1.build = 1; + userlevel1.major = 2; + userlevel1.minor = 3; + userlevel1.processor = 4; + + userlevel_ctr.level = 1; + userlevel_ctr.user_info.level1 = &userlevel1; + + torture_comment(tctx, "Testing openprinterex printername pattern\n"); + + torture_assert(tctx, + test_OpenPrinterEx(tctx, b, real_printername, NULL, NULL, 0, + &userlevel_ctr, &handle, + WERR_OK), + "OpenPrinterEx failed"); + test_ClosePrinter(tctx, b, &handle); + + for (i=0; i < ARRAY_SIZE(tests); i++) { + + printername = talloc_asprintf(tctx, "%s%s", + real_printername, + tests[i].suffix); + + torture_assert(tctx, + test_OpenPrinterEx(tctx, b, printername, NULL, NULL, 0, + &userlevel_ctr, &handle, + tests[i].expected_result), + "OpenPrinterEx failed"); + if (W_ERROR_IS_OK(tests[i].expected_result)) { + test_ClosePrinter(tctx, b, &handle); + } + } + + return true; +} + + +static bool test_existing_printer_openprinterex(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + const char *environment) +{ + struct policy_handle handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_openprinter(tctx, b, name)) { + return false; + } + + if (!call_OpenPrinterEx(tctx, p, name, NULL, &handle)) { + return false; + } + + if (!test_PrinterInfo_SD(tctx, b, &handle)) { + ret = false; + } + + if (!test_GetPrinter(tctx, b, &handle, environment)) { + ret = false; + } + + if (!test_EnumForms_all(tctx, b, &handle, false)) { + ret = false; + } + + if (!test_Forms(tctx, b, &handle, false, name, NULL, NULL)) { + ret = false; + } + + if (!test_Forms_winreg(tctx, b, &handle, false, name)) { + ret = false; + } + + if (!test_EnumPrinterData_all(tctx, p, &handle)) { + ret = false; + } + + if (!test_EnumPrinterDataEx(tctx, b, &handle, "PrinterDriverData", NULL, NULL)) { + ret = false; + } + + if (!test_EnumPrinterData_consistency(tctx, p, &handle)) { + ret = false; + } + + if (!test_printer_all_keys(tctx, b, &handle)) { + ret = false; + } + + if (!test_PausePrinter(tctx, b, &handle)) { + ret = false; + } + + if (!test_DoPrintTest(tctx, b, &handle)) { + ret = false; + } + + if (!test_ResumePrinter(tctx, b, &handle)) { + ret = false; + } + + if (!test_SetPrinterData_matrix(tctx, b, &handle, name, NULL, NULL)) { + ret = false; + } + + if (!test_SetPrinterDataEx_matrix(tctx, p, &handle, name, NULL, NULL)) { + ret = false; + } + + if (!torture_setting_bool(tctx, "samba3", false)) { + if (!test_SecondaryClosePrinter(tctx, p, &handle)) { + ret = false; + } + } + + if (!test_ClosePrinter(tctx, b, &handle)) { + ret = false; + } + + return ret; +} + +static bool test_EnumPrinters_old(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct spoolss_EnumPrinters r; + NTSTATUS status; + uint16_t levels[] = {1, 2, 4, 5}; + int i; + bool ret = true; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0;ienvironment, true)) { + ret = false; + } + if (!test_OpenPrinter(tctx, p, full_name, ctx->environment, true)) { + ret = false; + } + if (!test_OpenPrinter(tctx, p, name, ctx->environment, false)) { + ret = false; + } + if (!test_existing_printer_openprinterex(tctx, p, name, ctx->environment)) { + ret = false; + } + } + } + } + + return ret; +} + +static bool test_EnumPrinters_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t flags, + const char *servername, + uint32_t level, + uint32_t *count_p, + union spoolss_PrinterInfo **info_p) +{ + struct spoolss_EnumPrinters r; + union spoolss_PrinterInfo *info; + uint32_t needed; + uint32_t count; + + r.in.flags = flags; + r.in.server = servername; + r.in.level = level; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + r.out.count = &count; + r.out.info = &info; + + torture_comment(tctx, "Testing EnumPrinters(%s) level %u\n", + r.in.server, r.in.level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "EnumPrinters failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "EnumPrinters failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed"); + + CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4); + + if (count_p) { + *count_p = count; + } + if (info_p) { + *info_p = info; + } + + return true; +} + +static const char *get_short_printername(struct torture_context *tctx, + const char *name) +{ + const char *short_name; + + if (name[0] == '\\' && name[1] == '\\') { + name += 2; + short_name = strchr(name, '\\'); + if (short_name) { + return talloc_strdup(tctx, short_name+1); + } + } + + return name; +} + +static const char *get_full_printername(struct torture_context *tctx, + const char *name) +{ + const char *full_name = talloc_strdup(tctx, name); + char *p; + + if (name && name[0] == '\\' && name[1] == '\\') { + name += 2; + p = strchr(name, '\\'); + if (p) { + return full_name; + } + } + + return NULL; +} + +static bool test_OnePrinter_servername(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct dcerpc_binding_handle *b, + const char *servername, + const char *printername) +{ + union spoolss_PrinterInfo info; + const char *short_name = get_short_printername(tctx, printername); + const char *full_name = get_full_printername(tctx, printername); + + if (short_name) { + struct policy_handle handle; + torture_assert(tctx, + call_OpenPrinterEx(tctx, p, short_name, NULL, &handle), + "failed to open printer"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &handle, 2, &info), + "failed to get printer info"); + + torture_assert_casestr_equal(tctx, info.info2.servername, NULL, + "unexpected servername"); + torture_assert_casestr_equal(tctx, info.info2.printername, short_name, + "unexpected printername"); + + if (info.info2.devmode) { + const char *expected_devicename; + expected_devicename = talloc_strndup(tctx, short_name, MIN(strlen(short_name), 31)); + torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename, + "unexpected devicemode devicename"); + } + + torture_assert(tctx, + test_ClosePrinter(tctx, b, &handle), + "failed to close printer"); + } + + if (full_name) { + struct policy_handle handle; + + torture_assert(tctx, + call_OpenPrinterEx(tctx, p, full_name, NULL, &handle), + "failed to open printer"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &handle, 2, &info), + "failed to get printer info"); + + torture_assert_casestr_equal(tctx, info.info2.servername, servername, + "unexpected servername"); + torture_assert_casestr_equal(tctx, info.info2.printername, full_name, + "unexpected printername"); + + if (info.info2.devmode) { + const char *expected_devicename; + expected_devicename = talloc_strndup(tctx, full_name, MIN(strlen(full_name), 31)); + torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename, + "unexpected devicemode devicename"); + } + + torture_assert(tctx, + test_ClosePrinter(tctx, b, &handle), + "failed to close printer"); + } + + return true; +} + +static bool test_EnumPrinters_servername(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t count; + union spoolss_PrinterInfo *info; + const char *servername; + uint32_t flags = PRINTER_ENUM_NAME|PRINTER_ENUM_LOCAL; + + torture_comment(tctx, "Testing servername behaviour in EnumPrinters and GetPrinters\n"); + + servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + torture_assert(tctx, + test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info), + "failed to enumerate printers"); + + for (i=0; i < count; i++) { + + torture_assert_casestr_equal(tctx, info[i].info2.servername, servername, + "unexpected servername"); + + torture_assert(tctx, + test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername), + "failed to check printer"); + } + + servername = ""; + + torture_assert(tctx, + test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info), + "failed to enumerate printers"); + + for (i=0; i < count; i++) { + + torture_assert_casestr_equal(tctx, info[i].info2.servername, NULL, + "unexpected servername"); + + torture_assert(tctx, + test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername), + "failed to check printer"); + } + + + return true; +} + +#if 0 +static bool test_GetPrinterDriver(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *driver_name) +{ + struct spoolss_GetPrinterDriver r; + uint32_t needed; + + r.in.handle = handle; + r.in.architecture = "W32X86"; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_comment(tctx, "Testing GetPrinterDriver level %d\n", r.in.level); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r), + "failed to call GetPrinterDriver"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r), + "failed to call GetPrinterDriver"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "failed to call GetPrinterDriver"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4); + + return true; +} +#endif + +static bool test_GetPrinterDriver2_level(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *driver_name, + const char *architecture, + uint32_t level, + uint32_t client_major_version, + uint32_t client_minor_version, + union spoolss_DriverInfo *info_p, + WERROR *result_p) + +{ + struct spoolss_GetPrinterDriver2 r; + uint32_t needed; + uint32_t server_major_version; + uint32_t server_minor_version; + + r.in.handle = handle; + r.in.architecture = architecture; + r.in.client_major_version = client_major_version; + r.in.client_minor_version = client_minor_version; + r.in.buffer = NULL; + r.in.offered = 0; + r.in.level = level; + r.out.needed = &needed; + r.out.server_major_version = &server_major_version; + r.out.server_minor_version = &server_minor_version; + + torture_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n", + driver_name, r.in.level); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r), + "failed to call GetPrinterDriver2"); + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r), + "failed to call GetPrinterDriver2"); + } + + if (result_p) { + *result_p = r.out.result; + } + + if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) { + switch (r.in.level) { + case 101: + case 8: + torture_comment(tctx, + "level %d not implemented, not considering as an error\n", + r.in.level); + return true; + default: + break; + } + } + + torture_assert_werr_ok(tctx, r.out.result, + "failed to call GetPrinterDriver2"); + + CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4); + + if (info_p) { + *info_p = *r.out.info; + } + + return true; +} + +static bool test_GetPrinterDriver2(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *driver_name, + const char *architecture) +{ + uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 }; + int i; + + + for (i=0;ispoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + for (i=0;ienvironment, levels[i], &count, &info), + "failed to enumerate drivers"); + + if (!info) { + torture_comment(tctx, "No printer drivers returned\n"); + break; + } + } + + return true; +} + +static bool test_DeletePrinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct spoolss_DeletePrinter r; + + torture_comment(tctx, "Testing DeletePrinter\n"); + + r.in.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_DeletePrinter_r(b, tctx, &r), + "failed to delete printer"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to delete printer"); + + return true; +} + +static bool test_EnumPrinters_findname(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t flags, + uint32_t level, + const char *name, + bool *found) +{ + struct spoolss_EnumPrinters e; + uint32_t count; + union spoolss_PrinterInfo *info; + uint32_t needed; + int i; + + *found = false; + + e.in.flags = flags; + e.in.server = NULL; + e.in.level = level; + e.in.buffer = NULL; + e.in.offered = 0; + e.out.count = &count; + e.out.info = &info; + e.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), + "failed to enum printers"); + + if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + e.in.buffer = &blob; + e.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), + "failed to enum printers"); + } + + torture_assert_werr_ok(tctx, e.out.result, + "failed to enum printers"); + + for (i=0; i < count; i++) { + + const char *current = NULL; + const char *q; + + switch (level) { + case 1: + current = info[i].info1.name; + break; + } + + if (strequal(current, name)) { + *found = true; + break; + } + + q = strrchr(current, '\\'); + if (q) { + if (!e.in.server) { + torture_warning(tctx, + "server returns printername %s incl. servername although we did not set servername", current); + } + q++; + if (strequal(q, name)) { + *found = true; + break; + } + } + } + + return true; +} + +static bool test_AddPrinter_wellknown(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *printername, + bool ex) +{ + WERROR result; + struct spoolss_AddPrinter r; + struct spoolss_AddPrinterEx rex; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo1 info1; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct spoolss_UserLevelCtr userlevel_ctr; + struct policy_handle handle; + bool found = false; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + ZERO_STRUCT(userlevel_ctr); + ZERO_STRUCT(info1); + + torture_comment(tctx, "Testing AddPrinter%s(%s) level 1\n", + ex ? "Ex":"", printername); + + /* try to add printer to wellknown printer list (level 1) */ + + userlevel_ctr.level = 1; + + info_ctr.info.info1 = &info1; + info_ctr.level = 1; + + rex.in.server = NULL; + rex.in.info_ctr = &info_ctr; + rex.in.devmode_ctr = &devmode_ctr; + rex.in.secdesc_ctr = &secdesc_ctr; + rex.in.userlevel_ctr = &userlevel_ctr; + rex.out.handle = &handle; + + r.in.server = NULL; + r.in.info_ctr = &info_ctr; + r.in.devmode_ctr = &devmode_ctr; + r.in.secdesc_ctr = &secdesc_ctr; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME, + "unexpected result code"); + + info1.name = printername; + info1.flags = PRINTER_ATTRIBUTE_SHARED; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS, + "unexpected result code"); + + /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here, + better do a real check to see the printer is really there */ + + torture_assert(tctx, test_EnumPrinters_findname(tctx, b, + PRINTER_ENUM_NETWORK, 1, + printername, + &found), + "failed to enum printers"); + + torture_assert(tctx, found, "failed to find newly added printer"); + + info1.flags = 0; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS, + "unexpected result code"); + + /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here, + better do a real check to see the printer has really been removed + from the well known printer list */ + + found = false; + + torture_assert(tctx, test_EnumPrinters_findname(tctx, b, + PRINTER_ENUM_NETWORK, 1, + printername, + &found), + "failed to enum printers"); +#if 0 + torture_assert(tctx, !found, "printer still in well known printer list"); +#endif + return true; +} + +static bool test_AddPrinter_normal(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle_p, + const char *printername, + const char *drivername, + const char *portname, + struct spoolss_DeviceMode *devmode, + bool ex) +{ + WERROR result; + struct spoolss_AddPrinter r; + struct spoolss_AddPrinterEx rex; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo2 info2; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct spoolss_UserLevelCtr userlevel_ctr; + struct policy_handle handle; + bool found = false; + bool existing_printer_deleted = false; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + ZERO_STRUCT(userlevel_ctr); + + torture_comment(tctx, "Testing AddPrinter%s(%s) level 2\n", + ex ? "Ex":"", printername); + + devmode_ctr.devmode = devmode; + + userlevel_ctr.level = 1; + + rex.in.server = NULL; + rex.in.info_ctr = &info_ctr; + rex.in.devmode_ctr = &devmode_ctr; + rex.in.secdesc_ctr = &secdesc_ctr; + rex.in.userlevel_ctr = &userlevel_ctr; + rex.out.handle = &handle; + + r.in.server = NULL; + r.in.info_ctr = &info_ctr; + r.in.devmode_ctr = &devmode_ctr; + r.in.secdesc_ctr = &secdesc_ctr; + r.out.handle = &handle; + + again: + + /* try to add printer to printer list (level 2) */ + + ZERO_STRUCT(info2); + + info_ctr.info.info2 = &info2; + info_ctr.level = 2; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME, + "unexpected result code"); + + info2.printername = printername; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + + if (W_ERROR_EQUAL(result, WERR_PRINTER_ALREADY_EXISTS)) { + struct policy_handle printer_handle; + + if (existing_printer_deleted) { + torture_fail(tctx, "already deleted printer still existing?"); + } + + torture_assert(tctx, call_OpenPrinterEx(tctx, p, printername, NULL, &printer_handle), + "failed to open printer handle"); + + torture_assert(tctx, test_DeletePrinter(tctx, b, &printer_handle), + "failed to delete printer"); + + torture_assert(tctx, test_ClosePrinter(tctx, b, &printer_handle), + "failed to close server handle"); + + existing_printer_deleted = true; + + goto again; + } + + torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT, + "unexpected result code"); + + info2.portname = portname; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTER_DRIVER, + "unexpected result code"); + + info2.drivername = drivername; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + + /* w2k8r2 allows one to add printer w/o defining printprocessor */ + + if (!W_ERROR_IS_OK(result)) { + torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR, + "unexpected result code"); + + info2.printprocessor = "winprint"; + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_ok(tctx, result, + "failed to add printer"); + } + + *handle_p = handle; + + /* we are paranoid, really check if the printer is there now */ + + torture_assert(tctx, test_EnumPrinters_findname(tctx, b, + PRINTER_ENUM_LOCAL, 1, + printername, + &found), + "failed to enum printers"); + torture_assert(tctx, found, "failed to find newly added printer"); + + torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) : + dcerpc_spoolss_AddPrinter_r(b, tctx, &r), + "failed to add printer"); + result = ex ? rex.out.result : r.out.result; + torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS, + "unexpected result code"); + + return true; +} + +static bool test_printer_info(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + bool ret = true; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skipping printer info cross tests against samba 3"); + } + + if (!test_PrinterInfo(tctx, b, &t->handle)) { + ret = false; + } + + if (!test_SetPrinter_errors(tctx, b, &t->handle)) { + ret = false; + } + + return ret; +} + +static bool test_EnumPrinterKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key_name, + const char ***array) +{ + struct spoolss_EnumPrinterKey r; + uint32_t needed = 0; + union spoolss_KeyNames key_buffer; + int32_t offered[] = { 0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, 256, 512, 1024, 2048 }; + uint32_t _ndr_size; + int i; + + r.in.handle = handle; + r.in.key_name = key_name; + r.out.key_buffer = &key_buffer; + r.out.needed = &needed; + r.out._ndr_size = &_ndr_size; + + for (i=0; i < ARRAY_SIZE(offered); i++) { + + if (offered[i] < 0 && needed) { + if (needed <= 4) { + continue; + } + r.in.offered = needed + offered[i]; + } else { + r.in.offered = offered[i]; + } + + ZERO_STRUCT(key_buffer); + + torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r), + "failed to call EnumPrinterKey"); + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + + torture_assert(tctx, (_ndr_size == r.in.offered/2), + talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)", + _ndr_size, r.in.offered/2)); + + r.in.offered = needed; + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r), + "failed to call EnumPrinterKey"); + } + + if (offered[i] > 0) { + torture_assert_werr_ok(tctx, r.out.result, + "failed to call EnumPrinterKey"); + } + + torture_assert(tctx, (_ndr_size == r.in.offered/2), + talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)", + _ndr_size, r.in.offered/2)); + + torture_assert(tctx, (*r.out.needed <= r.in.offered), + talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= offered %d", *r.out.needed, r.in.offered)); + + torture_assert(tctx, (*r.out.needed <= _ndr_size * 2), + talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= _ndr_size %d * 2", *r.out.needed, _ndr_size)); + + if (key_buffer.string_array) { + uint32_t calc_needed = 0; + int s; + for (s=0; key_buffer.string_array[s]; s++) { + calc_needed += strlen_m_term(key_buffer.string_array[s])*2; + } + if (!key_buffer.string_array[0]) { + calc_needed += 2; + } + calc_needed += 2; + + torture_assert_int_equal(tctx, *r.out.needed, calc_needed, + "EnumPrinterKey unexpected size"); + } + } + + if (array) { + *array = key_buffer.string_array; + } + + return true; +} + +bool test_printer_all_keys(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + const char **key_array = NULL; + int i; + + torture_comment(tctx, "Testing Printer Keys\n"); + + torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, "", &key_array), + "failed to call test_EnumPrinterKey"); + + for (i=0; key_array && key_array[i]; i++) { + torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, key_array[i], NULL), + "failed to call test_EnumPrinterKey"); + } + for (i=0; key_array && key_array[i]; i++) { + torture_assert(tctx, test_EnumPrinterDataEx(tctx, b, handle, key_array[i], NULL, NULL), + "failed to call test_EnumPrinterDataEx"); + } + + torture_comment(tctx, "Printer Keys test succeeded\n\n"); + + return true; +} + +static bool test_openprinter_wrap(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *printername = t->info2.printername; + + return test_openprinter(tctx, b, printername); +} + +static bool test_csetprinter(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + const char *printername = talloc_asprintf(tctx, "%s2", t->info2.printername); + const char *drivername = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername; + const char *portname = t->info2.portname; + + union spoolss_PrinterInfo info; + struct policy_handle new_handle, new_handle2; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "Testing c_setprinter\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &t->handle, 0, &info), + "failed to get level 0 printer info"); + torture_comment(tctx, "csetprinter on initial printer handle: %d\n", + info.info0.c_setprinter); + + /* check if c_setprinter on 1st handle increases after a printer has + * been added */ + + torture_assert(tctx, + test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, NULL, false), + "failed to add new printer"); + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &t->handle, 0, &info), + "failed to get level 0 printer info"); + torture_comment(tctx, "csetprinter on initial printer handle (after add): %d\n", + info.info0.c_setprinter); + + /* check if c_setprinter on new handle increases after a printer has + * been added */ + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &new_handle, 0, &info), + "failed to get level 0 printer info"); + torture_comment(tctx, "csetprinter on created handle: %d\n", + info.info0.c_setprinter); + + /* open the new printer and check if c_setprinter increases */ + + torture_assert(tctx, + call_OpenPrinterEx(tctx, p, printername, NULL, &new_handle2), + "failed to open created printer"); + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &new_handle2, 0, &info), + "failed to get level 0 printer info"); + torture_comment(tctx, "csetprinter on new handle (after openprinter): %d\n", + info.info0.c_setprinter); + + /* cleanup */ + + torture_assert(tctx, + test_ClosePrinter(tctx, b, &new_handle2), + "failed to close printer"); + torture_assert(tctx, + test_DeletePrinter(tctx, b, &new_handle), + "failed to delete new printer"); + + return true; +} + +static bool compose_local_driver_directory(struct torture_context *tctx, + const char *environment, + const char *local_dir, + const char **path) +{ + char *p; + + p = strrchr(local_dir, '/'); + if (!p) { + return NULL; + } + p++; + + if (strequal(environment, SPOOLSS_ARCHITECTURE_x64)) { + if (!strequal(p, "x64")) { + *path = talloc_asprintf(tctx, "%s/x64", local_dir); + } + } else if (strequal(environment, SPOOLSS_ARCHITECTURE_NT_X86)) { + if (!strequal(p, "i386")) { + *path = talloc_asprintf(tctx, "%s/i386", local_dir); + } + } else { + torture_assert(tctx, "unknown environment: '%s'\n", environment); + } + + return true; +} + +#if 0 +static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx, + const char *devicename) +{ + struct spoolss_DeviceMode *r; + + r = talloc_zero(mem_ctx, struct spoolss_DeviceMode); + if (r == NULL) { + return NULL; + } + + r->devicename = talloc_strdup(r, devicename); + r->specversion = DMSPEC_NT4_AND_ABOVE; + r->driverversion = 0x0600; + r->size = 0x00dc; + r->__driverextra_length = 0; + r->fields = DEVMODE_FORMNAME | + DEVMODE_TTOPTION | + DEVMODE_PRINTQUALITY | + DEVMODE_DEFAULTSOURCE | + DEVMODE_COPIES | + DEVMODE_SCALE | + DEVMODE_PAPERSIZE | + DEVMODE_ORIENTATION; + r->orientation = DMORIENT_PORTRAIT; + r->papersize = DMPAPER_LETTER; + r->paperlength = 0; + r->paperwidth = 0; + r->scale = 100; + r->copies = 55; + r->defaultsource = DMBIN_FORMSOURCE; + r->printquality = DMRES_HIGH; + r->color = DMRES_MONOCHROME; + r->duplex = DMDUP_SIMPLEX; + r->yresolution = 0; + r->ttoption = DMTT_SUBDEV; + r->collate = DMCOLLATE_FALSE; + r->formname = talloc_strdup(r, "Letter"); + + return r; +} +#endif + +static bool test_architecture_buffer(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + struct spoolss_OpenPrinterEx r; + struct spoolss_UserLevel1 u1; + struct policy_handle handle; + uint32_t architectures[] = { + PROCESSOR_ARCHITECTURE_INTEL, + PROCESSOR_ARCHITECTURE_IA64, + PROCESSOR_ARCHITECTURE_AMD64 + }; + uint32_t needed[3]; + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0; i < ARRAY_SIZE(architectures); i++) { + + torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]); + + u1.size = 0; + u1.client = NULL; + u1.user = NULL; + u1.build = 0; + u1.major = 3; + u1.minor = 0; + u1.processor = architectures[i]; + + r.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.userlevel_ctr.level = 1; + r.in.userlevel_ctr.user_info.level1 = &u1; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), ""); + torture_assert_werr_ok(tctx, r.out.result, ""); + + { + struct spoolss_EnumPrinters e; + uint32_t count; + union spoolss_PrinterInfo *info; + + e.in.flags = PRINTER_ENUM_LOCAL; + e.in.server = NULL; + e.in.level = 2; + e.in.buffer = NULL; + e.in.offered = 0; + e.out.count = &count; + e.out.info = &info; + e.out.needed = &needed[i]; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), ""); +#if 0 + torture_comment(tctx, "needed was %d\n", needed[i]); +#endif + } + + torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), ""); + } + + for (i=1; i < ARRAY_SIZE(architectures); i++) { + if (needed[i-1] != needed[i]) { + torture_fail(tctx, + talloc_asprintf(tctx, "needed size %d for architecture %d != needed size %d for architecture %d\n", + needed[i-1], architectures[i-1], needed[i], architectures[i])); + } + } + + return true; +} + +static bool test_get_core_printer_drivers_arch_guid(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *architecture, + const char *guid_str, + const char **package_id) +{ + struct spoolss_GetCorePrinterDrivers r; + struct spoolss_CorePrinterDriver core_printer_drivers; + DATA_BLOB blob = data_blob_talloc_zero(tctx, 2); + const char **s; + struct dcerpc_binding_handle *b = p->binding_handle; + struct GUID guid; + + s = talloc_zero_array(tctx, const char *, 2); + + r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.architecture = "foobar"; + r.in.core_driver_size = 0; + r.in.core_driver_dependencies = (uint16_t *)blob.data; + r.in.core_printer_driver_count = 0; + r.out.core_printer_drivers = &core_printer_drivers; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r), + "spoolss_GetCorePrinterDrivers failed"); + torture_assert_hresult_equal(tctx, r.out.result, HRES_E_INVALIDARG, + "spoolss_GetCorePrinterDrivers failed"); + + guid = GUID_random(); + s[0] = GUID_string2(tctx, &guid); + + torture_assert(tctx, + push_reg_multi_sz(tctx, &blob, s), + "push_reg_multi_sz failed"); + + r.in.core_driver_size = blob.length/2; + r.in.core_driver_dependencies = (uint16_t *)blob.data; + r.in.core_printer_driver_count = 1; + r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r), + "spoolss_GetCorePrinterDrivers failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT, + "spoolss_GetCorePrinterDrivers failed"); + + r.in.architecture = architecture; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r), + "spoolss_GetCorePrinterDrivers failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_NOT_FOUND, + "spoolss_GetCorePrinterDrivers failed"); + + s[0] = talloc_strdup(s, guid_str); + + torture_assert(tctx, + push_reg_multi_sz(tctx, &blob, s), + "push_reg_multi_sz failed"); + + r.in.core_driver_size = blob.length/2; + r.in.core_driver_dependencies = (uint16_t *)blob.data; + r.in.core_printer_driver_count = 1; + r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r), + "spoolss_GetCorePrinterDrivers failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "spoolss_GetCorePrinterDrivers failed"); + + if (package_id) { + *package_id = r.out.core_printer_drivers[0].szPackageID; + } + + return true; +} + +static bool test_get_core_printer_drivers(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + const char *architectures[] = { + SPOOLSS_ARCHITECTURE_NT_X86, + SPOOLSS_ARCHITECTURE_x64 + }; + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + + for (i=0; i < ARRAY_SIZE(architectures); i++) { + + torture_comment(tctx, "Testing GetCorePrinterDrivers(\"%s\",\"%s\")\n", + architectures[i], + SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV); + + torture_assert(tctx, + test_get_core_printer_drivers_arch_guid(tctx, p, + architectures[i], + SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV, + NULL), + ""); + } + + return true; +} + +static bool test_get_printer_driver_package_path(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + const char *architectures[] = { + SPOOLSS_ARCHITECTURE_NT_X86, + SPOOLSS_ARCHITECTURE_x64 + }; + int i; + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0; i < ARRAY_SIZE(architectures); i++) { + struct spoolss_GetPrinterDriverPackagePath r; + uint32_t required = 0; + const char *package_id = NULL; + + test_get_core_printer_drivers_arch_guid(tctx, p, + architectures[i], + SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV, + &package_id), + + torture_comment(tctx, "Testing GetPrinterDriverPackagePath(\"%s\",\"%s\")\n", + architectures[i], package_id); + + r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.architecture = "foobar"; + r.in.language = NULL; + r.in.package_id = ""; + r.in.driver_package_cab_size = 0; + r.in.driver_package_cab = NULL; + + r.out.required = &required; + r.out.driver_package_cab = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT, + "spoolss_GetPrinterDriverPackagePath failed"); + + r.in.architecture = architectures[i]; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND, + "spoolss_GetPrinterDriverPackagePath failed"); + + r.in.package_id = package_id; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "spoolss_GetPrinterDriverPackagePath failed"); + + r.in.driver_package_cab_size = required; + r.in.driver_package_cab = talloc_zero_array(tctx, char, required); + r.out.driver_package_cab = talloc_zero_array(tctx, char, required); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "spoolss_GetPrinterDriverPackagePath failed"); + + r.in.servername = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_werr_equal(tctx, + W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INSUFFICIENT_BUFFER, + "spoolss_GetPrinterDriverPackagePath failed"); + + r.in.driver_package_cab_size = required; + r.in.driver_package_cab = talloc_zero_array(tctx, char, required); + r.out.driver_package_cab = talloc_zero_array(tctx, char, required); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r), + "spoolss_GetPrinterDriverPackagePath failed"); + torture_assert_hresult_ok(tctx, r.out.result, + "spoolss_GetPrinterDriverPackagePath failed"); + + } + + return true; +} + +static bool test_get_printer_printserverhandle(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t levels[] = {0, 1, 2, /* 3,*/ 4, 5, 6, 7, 8}; + int i; + + for (i=0;iserver_handle, + levels[i], WERR_INVALID_LEVEL, + NULL), + "failed to call GetPrinter"); + } + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, NULL), + "failed to call GetPrinter"); + + return true; +} + +#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500" + +static bool test_set_printer_printserverhandle(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo3 info3; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct security_descriptor *sd; + struct security_ace *ace; + struct dom_sid sid; + int i; + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info), + "failed to call GetPrinter"); + + secdesc_ctr.sd = info.info3.secdesc; + secdesc_ctr.sd->owner_sid = NULL; + secdesc_ctr.sd->group_sid = NULL; + + sd = security_descriptor_copy(tctx, secdesc_ctr.sd); + if (sd == NULL) { + return false; + } + + ace = security_ace_create(tctx, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_STD_REQUIRED, + SEC_ACE_FLAG_CONTAINER_INHERIT); + torture_assert(tctx, ace, "failed to create ace"); + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_add(sd, ace), + "failed to add ace"); + + secdesc_ctr.sd = sd; + + info3.sec_desc_ptr = 0; + + info_ctr.level = 3; + info_ctr.info.info3 = &info3; + + ZERO_STRUCT(devmode_ctr); + + torture_assert(tctx, + test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr, + &devmode_ctr, &secdesc_ctr, 0), + "failed to call SetPrinter"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info), + "failed to call GetPrinter"); + + for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) { + if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) { + break; + } + } + + if (i == info.info3.secdesc->dacl->num_aces) { + torture_fail(tctx, "ace not present"); + } + + torture_assert(tctx, + dom_sid_parse(TEST_SID, &sid), + "failed to parse sid"); + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_del(info.info3.secdesc, &sid), + "failed to remove ace from sd"); + + secdesc_ctr.sd = info.info3.secdesc; + + torture_assert(tctx, + test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr, + &devmode_ctr, &secdesc_ctr, 0), + "failed to call SetPrinter"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info), + "failed to call GetPrinter"); + + for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) { + if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) { + torture_fail(tctx, "ace still present"); + } + } + + return true; +} + + +static bool test_PrintServer_Forms_Winreg(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_Forms_winreg(tctx, b, &ctx->server_handle, true, NULL); +} + +static bool test_PrintServer_Forms(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_Forms(tctx, b, &ctx->server_handle, true, NULL, NULL, NULL); +} + +static bool test_PrintServer_EnumForms(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *ctx = + talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = ctx->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + return test_EnumForms_all(tctx, b, &ctx->server_handle, true); +} + +static bool torture_rpc_spoolss_setup_common(struct torture_context *tctx, struct test_spoolss_context *t) +{ + NTSTATUS status; + + status = torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss); + + torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); + + torture_assert(tctx, + test_OpenPrinter_server(tctx, t->spoolss_pipe, &t->server_handle), + "failed to open printserver"); + torture_assert(tctx, + test_get_environment(tctx, t->spoolss_pipe->binding_handle, &t->server_handle, &t->environment), + "failed to get environment"); + + return true; +} + +static bool torture_rpc_spoolss_setup(struct torture_context *tctx, void **data) +{ + struct test_spoolss_context *t; + + *data = t = talloc_zero(tctx, struct test_spoolss_context); + + return torture_rpc_spoolss_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_teardown_common(struct torture_context *tctx, struct test_spoolss_context *t) +{ + test_ClosePrinter(tctx, t->spoolss_pipe->binding_handle, &t->server_handle); + + return true; +} + +static bool torture_rpc_spoolss_teardown(struct torture_context *tctx, void *data) +{ + struct test_spoolss_context *t = talloc_get_type(data, struct test_spoolss_context); + bool ret; + + ret = torture_rpc_spoolss_teardown_common(tctx, t); + talloc_free(t); + + return ret; +} + +static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tctx, struct torture_printer_context *t) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + const char *server_name_slash; + const char *driver_name; + const char *printer_name; + const char *port_name; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss), + "Error connecting to server"); + + p = t->spoolss_pipe; + b = p->binding_handle; + server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + t->driver.info8.version = SPOOLSS_DRIVER_VERSION_200X; + t->driver.info8.driver_name = TORTURE_DRIVER; + t->driver.info8.driver_path = "pscript5.dll"; + t->driver.info8.data_file = "cups6.ppd"; + t->driver.info8.config_file = "ps5ui.dll"; + t->driver.info8.help_file = "pscript.hlp"; + t->driver.info8.default_datatype = "RAW"; + t->driver.info8.dependent_files = talloc_zero(t, struct spoolss_StringArray); + t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1); + t->driver.info8.dependent_files->string[0] = "pscript5.dll"; + t->driver.info8.dependent_files->string[1] = "cups6.ppd"; + t->driver.info8.dependent_files->string[2] = "ps5ui.dll"; + t->driver.info8.dependent_files->string[3] = "pscript.hlp"; + t->driver.info8.dependent_files->string[4] = "pscript.ntf"; + t->driver.info8.dependent_files->string[5] = "cups6.ini"; + t->driver.info8.dependent_files->string[6] = "cupsps6.dll"; + t->driver.info8.dependent_files->string[7] = "cupsui6.dll"; + + t->driver.local.driver_directory= "/usr/share/cups/drivers"; + + t->info2.portname = "LPT1:"; + + printer_name = t->info2.printername; + port_name = t->info2.portname; + + torture_assert(tctx, + fillup_printserver_info(tctx, p, &t->driver), + "failed to fillup printserver info"); + + t->driver.info8.architecture = talloc_strdup(t, t->driver.remote.environment); + + torture_assert(tctx, + compose_local_driver_directory(tctx, t->driver.remote.environment, + t->driver.local.driver_directory, + &t->driver.local.driver_directory), + "failed to compose local driver directory"); + + t->info2.drivername = "Microsoft XPS Document Writer"; + + if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) { + torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) is present on server\n", + t->info2.drivername, t->driver.remote.environment); + t->have_driver = true; + goto try_add; + } + + torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n", + t->info2.drivername, t->driver.remote.environment); + + t->info2.drivername = "Microsoft XPS Document Writer v4"; + + if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) { + torture_comment(tctx, "driver '%s' (architecture: %s, version: 4) is present on server\n", + t->info2.drivername, t->driver.remote.environment); + t->have_driver = true; + goto try_add; + } + + torture_comment(tctx, "trying to upload own driver\n"); + + if (!directory_exist(t->driver.local.driver_directory)) { + torture_warning(tctx, "no local driver is available!"); + t->have_driver = false; + goto try_add; + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), &t->driver), + "failed to upload printer driver"); + + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &t->driver.info8, 0, false, NULL), + "failed to add driver"); + + t->added_driver = true; + t->have_driver = true; + + try_add: + driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername; + + if (t->wellknown) { + torture_assert(tctx, + test_AddPrinter_wellknown(tctx, p, printer_name, t->ex), + "failed to add wellknown printer"); + } else { + torture_assert(tctx, + test_AddPrinter_normal(tctx, p, &t->handle, printer_name, driver_name, port_name, t->devmode, t->ex), + "failed to add printer"); + } + + return true; +} + +static bool torture_rpc_spoolss_printer_setup(struct torture_context *tctx, void **data) +{ + struct torture_printer_context *t; + + *data = t = talloc_zero(tctx, struct torture_printer_context); + + t->ex = false; + t->wellknown = false; + t->info2.printername = TORTURE_PRINTER; + t->devmode = NULL; + + return torture_rpc_spoolss_printer_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_printerex_setup(struct torture_context *tctx, void **data) +{ + struct torture_printer_context *t; + + *data = t = talloc_zero(tctx, struct torture_printer_context); + + t->ex = true; + t->wellknown = false; + t->info2.printername = TORTURE_PRINTER_EX; + t->devmode = NULL; + + return torture_rpc_spoolss_printer_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_printerwkn_setup(struct torture_context *tctx, void **data) +{ + struct torture_printer_context *t; + + *data = t = talloc_zero(tctx, struct torture_printer_context); + + t->ex = false; + t->wellknown = true; + t->info2.printername = TORTURE_WELLKNOWN_PRINTER; + t->devmode = NULL; + + /* FIXME */ + if (t->wellknown) { + torture_skip(tctx, "skipping AddPrinter level 1"); + } + + return torture_rpc_spoolss_printer_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx, void **data) +{ + struct torture_printer_context *t; + + *data = t = talloc_zero(tctx, struct torture_printer_context); + + t->ex = true; + t->wellknown = true; + t->info2.printername = TORTURE_WELLKNOWN_PRINTER_EX; + t->devmode = NULL; + + /* FIXME */ + if (t->wellknown) { + torture_skip(tctx, "skipping AddPrinterEx level 1"); + } + + return torture_rpc_spoolss_printer_setup_common(tctx, t); +} + +#if 0 +static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, void **data) +{ + struct torture_printer_context *t; + + *data = t = talloc_zero(tctx, struct torture_printer_context); + + t->ex = true; + t->wellknown = false; + t->info2.printername = TORTURE_PRINTER_EX; + t->devmode = torture_devicemode(t, TORTURE_PRINTER_EX); + + return torture_rpc_spoolss_printer_setup_common(tctx, t); +} +#endif + +static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server, + const char *driver, + const char *environment, + uint32_t delete_flags, + uint32_t version, + WERROR expected_result); + +static bool torture_rpc_spoolss_printer_teardown_common(struct torture_context *tctx, struct torture_printer_context *t) +{ + bool found = false; + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = NULL; + const char *server_name_slash; + bool ok = true; + + if (p == NULL) { + return true; + } + b = p->binding_handle; + + server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + if (!t->wellknown) { + const char *printer_name = t->info2.printername; + + torture_assert_goto(tctx, + test_DeletePrinter(tctx, b, &t->handle), + ok, + remove_driver, + "failed to delete printer"); + + torture_assert_goto(tctx, + test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1, + printer_name, &found), + ok, + remove_driver, + "failed to enumerate printers"); + + torture_assert_goto(tctx, + !found, + ok, + remove_driver, + "deleted printer still there"); + } + + +remove_driver: + if (t->added_driver) { + ok = remove_printer_driver(tctx, + dcerpc_server_name(p), + &t->driver); + if (!ok) { + torture_warning(tctx, + "failed to remove printer driver\n"); + } + + ok = test_DeletePrinterDriverEx_exp(tctx, b, + server_name_slash, + t->driver.info8.driver_name, + t->driver.info8.architecture, + DPD_DELETE_ALL_FILES, + t->driver.info8.version, + WERR_OK); + if (!ok) { + torture_warning(tctx, + "failed to delete printer driver via " + "spoolss\n"); + } + } + + return ok; +} + +static bool torture_rpc_spoolss_printer_teardown(struct torture_context *tctx, void *data) +{ + struct torture_printer_context *t = talloc_get_type(data, struct torture_printer_context); + bool ret; + + ret = torture_rpc_spoolss_printer_teardown_common(tctx, t); + talloc_free(t); + + return ret; +} + +static bool test_print_test(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_PausePrinter(tctx, b, &t->handle), + "failed to pause printer"); + + torture_assert(tctx, + test_DoPrintTest(tctx, b, &t->handle), + "failed to do print test"); + + torture_assert(tctx, + test_ResumePrinter(tctx, b, &t->handle), + "failed to resume printer"); + + return true; +} + +static bool test_print_test_extended(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + bool ret = true; + + torture_assert(tctx, + test_PausePrinter(tctx, b, &t->handle), + "failed to pause printer"); + + ret = test_DoPrintTest_extended(tctx, b, &t->handle); + if (ret == false) { + torture_comment(tctx, "WARNING! failed to do extended print test\n"); + if (torture_setting_bool(tctx, "samba3", false)) { + torture_comment(tctx, "non-critical for samba3\n"); + ret = true; + tctx->last_result = TORTURE_SKIP; + } + } + + torture_assert(tctx, + test_ResumePrinter(tctx, b, &t->handle), + "failed to resume printer"); + + return ret; +} + +static bool test_print_test_properties(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skip printer job property tests against samba"); + } + + torture_assert(tctx, + test_PausePrinter(tctx, b, &t->handle), + "failed to pause printer"); + + torture_assert(tctx, + test_DoPrintTest_properties(tctx, b, &t->handle), + "failed to test print job properties"); + + torture_assert(tctx, + test_ResumePrinter(tctx, b, &t->handle), + "failed to resume printer"); + + return true; +} + +/* use smbd file IO to spool a print job */ +static bool test_print_test_smbd(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + NTSTATUS status; + uint32_t count; + union spoolss_JobInfo *info = NULL; + int i; + + struct smb2_tree *tree; + struct smb2_handle job_h; + struct smbcli_options options; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + /* + * Do not test against the dynamically added printers, printing via + * smbd means that a different spoolss process may handle the + * OpenPrinter request to the one that handled the AddPrinter request. + * This currently leads to an ugly race condition where one process + * sees the new printer and one doesn't. + */ + const char *share = TORTURE_PRINTER_STATIC1; + + torture_comment(tctx, "Testing smbd job spooling\n"); + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + status = smb2_connect(mem_ctx, + torture_setting_string(tctx, "host", NULL), + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to SMB2 printer %s - %s\n", + share, nt_errstr(status)); + return false; + } + + status = torture_smb2_testfile(tree, "smbd_spooler_job", &job_h); + torture_assert_ntstatus_ok(tctx, status, "smbd spool job create"); + + status = smb2_util_write(tree, job_h, "exciting print job data", 0, + sizeof("exciting print job data")); + torture_assert_ntstatus_ok(tctx, status, "smbd spool job write"); + + /* check back end spoolss job was created */ + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK, + &count, &info), + "EnumJobs level 1 failed"); + + for (i = 0; i < count; i++) { + if (!strcmp(info[i].info1.document_name, "smbd_spooler_job")) { + break; + } + } + torture_assert(tctx, (i != count), "smbd_spooler_job not found"); + + status = smb2_util_close(tree, job_h); + torture_assert_ntstatus_ok(tctx, status, "smbd spool job close"); + + /* disconnect from printer share */ + talloc_free(mem_ctx); + + return true; +} + +static bool test_print_test_purge(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, + struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t num_jobs = 8; + uint32_t *job_ids; + int i; + bool ret = true; + uint32_t count; + union spoolss_JobInfo *info; + + torture_assert(tctx, + test_PausePrinter(tctx, b, &t->handle), + "failed to pause printer"); + + job_ids = talloc_zero_array(tctx, uint32_t, num_jobs); + for (i=0; i < num_jobs; i++) { + ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle, + "TorturePrintJob", + &job_ids[i]); + torture_assert(tctx, ret, "failed to add print job"); + } + + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK, + &count, &info), + "EnumJobs level 1 failed"); + + torture_assert_int_equal(tctx, count, num_jobs, + "unexpected number of jobs in queue"); + + torture_assert(tctx, + test_printer_purge(tctx, b, &t->handle), + "failed to purge printer"); + + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK, + &count, &info), + "EnumJobs level 1 failed"); + + torture_assert_int_equal(tctx, count, 0, + "unexpected number of jobs in queue"); + + torture_assert(tctx, + test_ResumePrinter(tctx, b, &t->handle), + "failed to resume printer"); + + return true; +} + +static bool test_printer_sd(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_PrinterInfo_SD(tctx, b, &t->handle), + "failed to test security descriptors"); + + return true; +} + +static bool test_printer_dm(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_PrinterInfo_DevMode(tctx, p, &t->handle, t->info2.printername, t->devmode), + "failed to test devicemodes"); + + return true; +} + +static bool test_printer_info_winreg(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_PrinterInfo_winreg(tctx, p, &t->handle, t->info2.printername), + "failed to test printer info winreg"); + + return true; +} + +static bool test_printserver_info_winreg(struct torture_context *tctx, + void *private_data) +{ + struct test_spoolss_context *t = + (struct test_spoolss_context *)talloc_get_type_abort(private_data, struct test_spoolss_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_PrintserverInfo_winreg(tctx, p, &t->server_handle), + "failed to test printserver info winreg"); + + return true; +} + + +static bool test_printer_change_id(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_ChangeID(tctx, p, &t->handle), + "failed to test change id"); + + return true; +} + +static bool test_printer_keys(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_printer_all_keys(tctx, b, &t->handle), + "failed to test printer keys"); + + return true; +} + +static bool test_printer_data_consistency(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_EnumPrinterData_consistency(tctx, p, &t->handle), + "failed to test printer data consistency"); + + return true; +} + +static bool test_printer_data_keys(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_SetPrinterDataEx_keys(tctx, p, &t->handle), + "failed to test printer data keys"); + + return true; +} + +static bool test_printer_data_values(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_SetPrinterDataEx_values(tctx, p, &t->handle), + "failed to test printer data values"); + + return true; +} + +static bool test_printer_data_set(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_SetPrinterDataEx_matrix(tctx, p, &t->handle, t->info2.printername, NULL, NULL), + "failed to test printer data set"); + + return true; +} + +static bool test_printer_data_winreg(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_PrinterData_winreg(tctx, p, &t->handle, t->info2.printername), + "failed to test printer data winreg"); + + return true; +} + +static bool test_printer_data_dsspooler(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + + torture_assert(tctx, + test_PrinterData_DsSpooler(tctx, p, &t->handle, t->info2.printername), + "failed to test printer data winreg dsspooler"); + + return true; +} + +static bool test_printer_ic(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + talloc_get_type_abort(private_data, + struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle gdi_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skip printer information context tests against samba"); + } + + { + struct spoolss_CreatePrinterIC r; + struct spoolss_DevmodeContainer devmode_ctr; + + ZERO_STRUCT(devmode_ctr); + + r.in.handle = &t->handle; + r.in.devmode_ctr = &devmode_ctr; + r.out.gdi_handle = &gdi_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_CreatePrinterIC_r(b, tctx, &r), + "CreatePrinterIC failed"); + torture_assert_werr_ok(tctx, r.out.result, + "CreatePrinterIC failed"); + } + + { + struct spoolss_PlayGDIScriptOnPrinterIC r; + DATA_BLOB in,out; + int i; + uint32_t num_fonts = 0; + + in = data_blob_string_const(""); + + r.in.gdi_handle = &gdi_handle; + r.in.pIn = in.data; + r.in.cIn = in.length; + r.in.ul = 0; + + for (i = 0; i < 4; i++) { + + out = data_blob_talloc_zero(tctx, i); + + r.in.cOut = out.length; + r.out.pOut = out.data; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r), + "PlayGDIScriptOnPrinterIC failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_ENOUGH_MEMORY, + "PlayGDIScriptOnPrinterIC failed"); + } + + out = data_blob_talloc_zero(tctx, 4); + + r.in.cOut = out.length; + r.out.pOut = out.data; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r), + "PlayGDIScriptOnPrinterIC failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_OK, + "PlayGDIScriptOnPrinterIC failed"); + + /* now we should have the required length, so retry with a + * buffer which is large enough to carry all font ids */ + + num_fonts = IVAL(r.out.pOut, 0); + + torture_comment(tctx, "PlayGDIScriptOnPrinterIC gave font count of %d\n", num_fonts); + + out = data_blob_talloc_zero(tctx, + num_fonts * sizeof(struct UNIVERSAL_FONT_ID) + 4); + + r.in.cOut = out.length; + r.out.pOut = out.data; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r), + "PlayGDIScriptOnPrinterIC failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_OK, + "PlayGDIScriptOnPrinterIC failed"); + + } + + { + struct spoolss_DeletePrinterIC r; + + r.in.gdi_handle = &gdi_handle; + r.out.gdi_handle = &gdi_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrinterIC_r(b, tctx, &r), + "DeletePrinterIC failed"); + torture_assert_werr_ok(tctx, r.out.result, + "DeletePrinterIC failed"); + + } + + return true; +} + +static bool test_printer_bidi(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + talloc_get_type_abort(private_data, + struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_SendRecvBidiData r; + struct RPC_BIDI_REQUEST_CONTAINER bidi_req; + struct RPC_BIDI_RESPONSE_CONTAINER *bidi_rep = NULL; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skip printer bidirectional tests against samba"); + } + + ZERO_STRUCT(bidi_req); + + r.in.hPrinter = t->handle; + r.in.pAction = "foobar"; + r.in.pReqData = &bidi_req; + r.out.ppRespData = &bidi_rep; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r), + "SendRecvBidiData failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "SendRecvBidiData failed"); + + if (!(t->info2.attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI)) { + torture_skip(tctx, "skipping further tests as printer is not BIDI enabled"); + } + + r.in.pAction = BIDI_ACTION_ENUM_SCHEMA; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r), + "SendRecvBidiData failed"); + torture_assert_werr_ok(tctx, r.out.result, + "SendRecvBidiData failed"); + + return true; +} + +static bool test_printer_set_publish(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfo7 info7; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + + info7.guid = ""; + info7.action = DSPRINT_PUBLISH; + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + info_ctr.level = 7; + info_ctr.info.info7 = &info7; + + torture_assert(tctx, + test_SetPrinter(tctx, b, handle, &info_ctr, + &devmode_ctr, &secdesc_ctr, 0), ""); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 2, &info), + ""); + torture_assert(tctx, + (info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED), + "info2 publish flag not set"); + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 7, &info), + ""); + if (info.info7.action & DSPRINT_PENDING) { + torture_comment(tctx, "publish is pending\n"); + torture_assert_int_equal(tctx, + info.info7.action, + (DSPRINT_PENDING | DSPRINT_PUBLISH), + "info7 publish flag not set"); + } else { + struct GUID guid; + char *ref_guid; + torture_assert_int_equal(tctx, + info.info7.action, + DSPRINT_PUBLISH, + "info7 publish flag not set"); + + /* GUID_from_string is able to parse both plain and + * curly-braced guids */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(info.info7.guid, + &guid), + "invalid published printer GUID"); + + /* Build reference GUID string */ + ref_guid = GUID_string2(tctx, &guid); + torture_assert_not_null(tctx, ref_guid, "ENOMEM"); + ref_guid = talloc_strdup_upper(tctx, ref_guid); + torture_assert_not_null(tctx, ref_guid, "ENOMEM"); + torture_assert_str_equal(tctx, info.info7.guid, ref_guid, + "invalid GUID format"); + } + + return true; +} + +static bool test_printer_set_unpublish(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfo7 info7; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + + info7.action = DSPRINT_UNPUBLISH; + info7.guid = ""; + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + info_ctr.level = 7; + info_ctr.info.info7 = &info7; + + torture_assert(tctx, + test_SetPrinter(tctx, b, handle, &info_ctr, + &devmode_ctr, &secdesc_ctr, 0), ""); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 2, &info), + ""); + torture_assert(tctx, + !(info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED), + "info2 publish flag still set"); + torture_assert(tctx, + test_GetPrinter_level(tctx, b, handle, 7, &info), + ""); + + if (info.info7.action & DSPRINT_PENDING) { + struct GUID guid; + torture_comment(tctx, "unpublish is pending\n"); + torture_assert_int_equal(tctx, + info.info7.action, + (DSPRINT_PENDING | DSPRINT_UNPUBLISH), + "info7 unpublish flag not set"); + torture_assert_ntstatus_ok(tctx, + GUID_from_string(info.info7.guid, + &guid), + "invalid printer GUID"); + } else { + torture_assert_int_equal(tctx, + info.info7.action, DSPRINT_UNPUBLISH, + "info7 unpublish flag not set"); + } + + return true; +} + +static bool test_printer_publish_toggle(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + talloc_get_type_abort(private_data, + struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle *handle = &t->handle; + union spoolss_PrinterInfo info7; + union spoolss_PrinterInfo info2; + + /* check publish status via level 7 and level 2 */ + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 7, &info7), + ""); + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info2), + ""); + + if (info2.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) { + torture_assert_int_equal(tctx, + info7.info7.action, DSPRINT_PUBLISH, + "info7 publish flag not set"); + torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), ""); + torture_assert(tctx, test_printer_set_publish(tctx, b, handle), ""); + } else { + torture_assert_int_equal(tctx, + info7.info7.action, DSPRINT_UNPUBLISH, + "info7 unpublish flag not set"); + torture_assert(tctx, test_printer_set_publish(tctx, b, handle), ""); + torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), ""); + } + + return true; +} + +static bool test_driver_info_winreg(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername; + + if (!t->have_driver) { + torture_skip(tctx, "skipping driver info winreg test as we don't have a driver"); + } + + torture_assert(tctx, + test_DriverInfo_winreg(tctx, p, &t->handle, t->info2.printername, driver_name, t->driver.remote.environment, 3), + "failed to test driver info winreg"); + + return true; +} + +static bool test_print_job_enum(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + bool ret = true; + uint32_t num_jobs = 8; + uint32_t *job_ids; + int i; + union spoolss_JobInfo *info = NULL; + uint32_t count; + + torture_assert(tctx, + test_PausePrinter(tctx, b, &t->handle), + "failed to pause printer"); + + /* purge in case of any jobs from previous tests */ + torture_assert(tctx, + test_printer_purge(tctx, b, &t->handle), + "failed to purge printer"); + + /* enum before jobs, valid level */ + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK, + &count, &info), + "EnumJobs with valid level"); + torture_assert_int_equal(tctx, count, 0, "EnumJobs count"); + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK, + &count, &info), + "EnumJobs with valid level"); + torture_assert_int_equal(tctx, count, 0, "EnumJobs count"); + /* enum before jobs, invalid level - expect failure */ + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 100, + WERR_INVALID_LEVEL, + &count, &info), + "EnumJobs with invalid level"); + + job_ids = talloc_zero_array(tctx, uint32_t, num_jobs); + + for (i = 0; i < num_jobs; i++) { + ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle, + "TorturePrintJob", + &job_ids[i]); + torture_assert(tctx, ret, "failed to add print job"); + } + + /* enum after jobs, valid level */ + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK, + &count, &info), + "EnumJobs with valid level"); + torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count"); + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK, + &count, &info), + "EnumJobs with valid level"); + torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count"); + /* enum after jobs, invalid level - expect failure */ + torture_assert(tctx, + test_EnumJobs_args(tctx, b, &t->handle, 100, + WERR_INVALID_LEVEL, + &count, &info), + "EnumJobs with invalid level"); + + for (i = 0; i < num_jobs; i++) { + test_SetJob(tctx, b, &t->handle, job_ids[i], NULL, + SPOOLSS_JOB_CONTROL_DELETE); + } + + torture_assert(tctx, + test_ResumePrinter(tctx, b, &t->handle), + "failed to resume printer"); + + return true; +} + +static bool test_printer_log_jobinfo(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + struct spoolss_BranchOfficeJobDataContainer info; + int i; + + struct spoolss_LogJobInfoForBranchOffice r; + + torture_comment(tctx, "Testing LogJobInfoForBranchOffice\n"); + + info.cJobDataEntries = 0; + info.JobData = NULL; + + r.in.hPrinter = &t->handle; + r.in.pBranchOfficeJobDataContainer = &info; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r), + "LogJobInfoForBranchOffice failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "LogJobInfoForBranchOffice failed"); + + info.cJobDataEntries = 1; + info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries); + + info.JobData[0].eEventType = kLogOfflineFileFull; + info.JobData[0].JobId = 42; + info.JobData[0].JobInfo.LogOfflineFileFull.pMachineName = talloc_strdup(tctx, "mthelena"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r), + "LogJobInfoForBranchOffice failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_OK, + "LogJobInfoForBranchOffice failed"); + + info.cJobDataEntries = 42; + info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries); + + for (i=0; i < info.cJobDataEntries; i++) { + info.JobData[i].eEventType = kLogOfflineFileFull; + info.JobData[i].JobId = i; + info.JobData[i].JobInfo.LogOfflineFileFull.pMachineName = talloc_asprintf(tctx, "torture_%d", i); + } + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r), + "LogJobInfoForBranchOffice failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_OK, + "LogJobInfoForBranchOffice failed"); + + return true; +} + +static bool test_printer_os_versions(struct torture_context *tctx, + void *private_data) +{ + struct torture_printer_context *t = + (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + struct dcerpc_binding_handle *b = p->binding_handle; + union spoolss_PrinterInfo info; + DATA_BLOB blob; + uint8_t *data; + uint32_t length; + struct spoolss_OSVersion osversion; + uint8_t os_major, os_minor; + uint16_t os_build; + struct policy_handle server_handle; + + torture_comment(tctx, "Testing OSVersion vs. PRINTER_INFO_STRESS\n"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &t->handle, 0, &info), + "failed to get level 0 printer info"); + + torture_assert(tctx, + test_OpenPrinter_server(tctx, p, &server_handle), + "failed to open printserver"); + + torture_assert(tctx, + test_GetPrinterData_checktype(tctx, b, &server_handle, "OSVersion", + NULL, NULL, &data, &length), + "failed to fetch OSVersion printer data"); + + test_ClosePrinter(tctx, b, &server_handle); + + blob = data_blob_const(data, length); + + torture_assert_ndr_success(tctx, + ndr_pull_struct_blob(&blob, tctx, &osversion, + (ndr_pull_flags_fn_t)ndr_pull_spoolss_OSVersion), + "failed to pull OSVersion"); + + os_major = CVAL(&info.info0.version, 0); + os_minor = CVAL(&info.info0.version, 1); + os_build = SVAL(&info.info0.version, 2); + + torture_assert_int_equal(tctx, os_major, osversion.major, "major"); + torture_assert_int_equal(tctx, os_minor, osversion.minor, "minor"); + torture_assert_int_equal(tctx, os_build, osversion.build, "build"); + + return true; +} + + +void torture_tcase_printer(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter_wrap); + torture_tcase_add_simple_test(tcase, "csetprinter", test_csetprinter); + torture_tcase_add_simple_test(tcase, "print_test", test_print_test); + torture_tcase_add_simple_test(tcase, "print_test_extended", test_print_test_extended); + torture_tcase_add_simple_test(tcase, "print_test_smbd", test_print_test_smbd); + torture_tcase_add_simple_test(tcase, "print_test_properties", test_print_test_properties); + torture_tcase_add_simple_test(tcase, "print_test_purge", test_print_test_purge); + torture_tcase_add_simple_test(tcase, "printer_info", test_printer_info); + torture_tcase_add_simple_test(tcase, "sd", test_printer_sd); + torture_tcase_add_simple_test(tcase, "dm", test_printer_dm); + torture_tcase_add_simple_test(tcase, "printer_info_winreg", test_printer_info_winreg); + torture_tcase_add_simple_test(tcase, "change_id", test_printer_change_id); + torture_tcase_add_simple_test(tcase, "keys", test_printer_keys); + torture_tcase_add_simple_test(tcase, "printerdata_consistency", test_printer_data_consistency); + torture_tcase_add_simple_test(tcase, "printerdata_keys", test_printer_data_keys); + torture_tcase_add_simple_test(tcase, "printerdata_values", test_printer_data_values); + torture_tcase_add_simple_test(tcase, "printerdata_set", test_printer_data_set); + torture_tcase_add_simple_test(tcase, "printerdata_winreg", test_printer_data_winreg); + torture_tcase_add_simple_test(tcase, "printerdata_dsspooler", test_printer_data_dsspooler); + torture_tcase_add_simple_test(tcase, "driver_info_winreg", test_driver_info_winreg); + torture_tcase_add_simple_test(tcase, "printer_rename", test_printer_rename); + torture_tcase_add_simple_test(tcase, "printer_ic", test_printer_ic); + torture_tcase_add_simple_test(tcase, "bidi", test_printer_bidi); + torture_tcase_add_simple_test(tcase, "publish_toggle", + test_printer_publish_toggle); + torture_tcase_add_simple_test(tcase, "print_job_enum", test_print_job_enum); + torture_tcase_add_simple_test(tcase, "log_jobinfo", test_printer_log_jobinfo); + torture_tcase_add_simple_test(tcase, "os_versions", test_printer_os_versions); +} + +struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "printer"); + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, "addprinter"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_printer_setup, + torture_rpc_spoolss_printer_teardown); + + torture_tcase_printer(tcase); + + tcase = torture_suite_add_tcase(suite, "addprinterex"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_printerex_setup, + torture_rpc_spoolss_printer_teardown); + + torture_tcase_printer(tcase); + + tcase = torture_suite_add_tcase(suite, "addprinterwkn"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_printerwkn_setup, + torture_rpc_spoolss_printer_teardown); + + tcase = torture_suite_add_tcase(suite, "addprinterexwkn"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_printerexwkn_setup, + torture_rpc_spoolss_printer_teardown); + +#if 0 + /* test is not correct */ + tcase = torture_suite_add_tcase(suite, "addprinterdm"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_printerdm_setup, + torture_rpc_spoolss_printer_teardown); + + torture_tcase_printer(tcase); +#endif + return suite; +} + +struct torture_suite *torture_rpc_spoolss(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss"); + struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_setup, + torture_rpc_spoolss_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter_badnamelist", test_OpenPrinter_badname_list); + torture_tcase_add_simple_test(tcase, "printer_data_list", test_GetPrinterData_list); + torture_tcase_add_simple_test(tcase, "enum_forms", test_PrintServer_EnumForms); + torture_tcase_add_simple_test(tcase, "forms", test_PrintServer_Forms); + torture_tcase_add_simple_test(tcase, "forms_winreg", test_PrintServer_Forms_Winreg); + torture_tcase_add_simple_test(tcase, "enum_ports", test_EnumPorts); + torture_tcase_add_simple_test(tcase, "add_port", test_AddPort); + torture_tcase_add_simple_test(tcase, "get_printer_driver_directory", test_GetPrinterDriverDirectory); + torture_tcase_add_simple_test(tcase, "get_print_processor_directory", test_GetPrintProcessorDirectory); + torture_tcase_add_simple_test(tcase, "enum_printer_drivers", test_EnumPrinterDrivers); + torture_tcase_add_simple_test(tcase, "enum_monitors", test_EnumMonitors); + torture_tcase_add_simple_test(tcase, "enum_print_processors", test_EnumPrintProcessors); + torture_tcase_add_simple_test(tcase, "print_processors_winreg", test_print_processors_winreg); + torture_tcase_add_simple_test(tcase, "add_processor", test_add_print_processor); + torture_tcase_add_simple_test(tcase, "enum_printprocdata", test_EnumPrintProcessorDataTypes); + torture_tcase_add_simple_test(tcase, "enum_printers", test_EnumPrinters); + torture_tcase_add_simple_test(tcase, "enum_ports_old", test_EnumPorts_old); + torture_tcase_add_simple_test(tcase, "enum_printers_old", test_EnumPrinters_old); + torture_tcase_add_simple_test(tcase, "enum_printers_servername", test_EnumPrinters_servername); + torture_tcase_add_simple_test(tcase, "enum_printer_drivers_old", test_EnumPrinterDrivers_old); + torture_tcase_add_simple_test(tcase, "architecture_buffer", test_architecture_buffer); + torture_tcase_add_simple_test(tcase, "get_core_printer_drivers", test_get_core_printer_drivers); + torture_tcase_add_simple_test(tcase, "get_printer_driver_package_path", test_get_printer_driver_package_path); + torture_tcase_add_simple_test(tcase, "get_printer", test_get_printer_printserverhandle); + torture_tcase_add_simple_test(tcase, "set_printer", test_set_printer_printserverhandle); + torture_tcase_add_simple_test(tcase, "printserver_info_winreg", test_printserver_info_winreg); + torture_tcase_add_simple_test(tcase, "addpermachineconnection", test_addpermachineconnection); + + torture_suite_add_suite(suite, torture_rpc_spoolss_printer(suite)); + + return suite; +} + +static bool test_GetPrinterDriverDirectory_getdir(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server, + const char *environment, + const char **dir_p) +{ + struct spoolss_GetPrinterDriverDirectory r; + uint32_t needed; + + r.in.server = server; + r.in.environment = environment; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r), + "failed to query driver directory"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r), + "failed to query driver directory"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "failed to query driver directory"); + + if (dir_p) { + *dir_p = r.out.info->info1.directory_name; + } + + return true; +} + +static const char *get_driver_from_info(struct spoolss_AddDriverInfoCtr *info_ctr) +{ + if (info_ctr == NULL) { + return NULL; + } + + switch (info_ctr->level) { + case 1: + return info_ctr->info.info1->driver_name; + case 2: + return info_ctr->info.info2->driver_name; + case 3: + return info_ctr->info.info3->driver_name; + case 4: + return info_ctr->info.info4->driver_name; + case 6: + return info_ctr->info.info6->driver_name; + case 8: + return info_ctr->info.info8->driver_name; + default: + return NULL; + } +} + +static const char *get_environment_from_info(struct spoolss_AddDriverInfoCtr *info_ctr) +{ + if (info_ctr == NULL) { + return NULL; + } + + switch (info_ctr->level) { + case 2: + return info_ctr->info.info2->architecture; + case 3: + return info_ctr->info.info3->architecture; + case 4: + return info_ctr->info.info4->architecture; + case 6: + return info_ctr->info.info6->architecture; + case 8: + return info_ctr->info.info8->architecture; + default: + return NULL; + } +} + + +static bool test_AddPrinterDriver_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, + struct spoolss_AddDriverInfoCtr *info_ctr, + WERROR expected_result) +{ + struct spoolss_AddPrinterDriver r; + const char *drivername = get_driver_from_info(info_ctr); + const char *environment = get_environment_from_info(info_ctr); + + r.in.servername = servername; + r.in.info_ctr = info_ctr; + + torture_comment(tctx, "Testing AddPrinterDriver(%s) level: %d, environment: '%s'\n", + drivername, info_ctr->level, environment); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_AddPrinterDriver_r(b, tctx, &r), + "spoolss_AddPrinterDriver failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "spoolss_AddPrinterDriver failed with unexpected result"); + + return true; + +} + +static bool test_AddPrinterDriverEx_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *servername, + struct spoolss_AddDriverInfoCtr *info_ctr, + uint32_t flags, + WERROR expected_result) +{ + struct spoolss_AddPrinterDriverEx r; + const char *drivername = get_driver_from_info(info_ctr); + const char *environment = get_environment_from_info(info_ctr); + + r.in.servername = servername; + r.in.info_ctr = info_ctr; + r.in.flags = flags; + + torture_comment(tctx, "Testing AddPrinterDriverEx(%s) level: %d, environment: '%s'\n", + drivername, info_ctr->level, environment); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_AddPrinterDriverEx_r(b, tctx, &r), + "AddPrinterDriverEx failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "AddPrinterDriverEx failed with unexpected result"); + + return true; +} + +#define ASSERT_DRIVER_PATH(tctx, path, driver_dir, cmt) \ + if (path && strlen(path)) {\ + torture_assert_strn_equal(tctx, path, driver_dir, strlen(driver_dir), cmt); \ + } + +static bool test_AddPrinterDriver_args_level_1(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + struct spoolss_AddDriverInfo1 info1; + + ZERO_STRUCT(info1); + + info_ctr.level = 1; + info_ctr.info.info1 = &info1; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriverEx level 1"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriver level 1"); + } + + info1.driver_name = r->driver_name; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriverEx level 1"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriver level 1"); + } + + return true; +} + +static bool test_AddPrinterDriver_args_level_2(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + struct spoolss_AddDriverInfo2 info2; + union spoolss_DriverInfo info; + + ZERO_STRUCT(info2); + + info_ctr.level = 2; + info_ctr.info.info2 = &info2; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.driver_name = r->driver_name; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.version = r->version; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.architecture = r->architecture; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.driver_path = r->driver_path; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.data_file = r->data_file; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriver level 2"); + } + + info2.config_file = r->config_file; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, 0, WERR_INVALID_PARAMETER), + "failed to test AddPrinterDriverEx"); + } + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK), + "failed to test AddPrinterDriverEx level 2"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK), + "failed to test AddPrinterDriver level 2"); + } + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name, &info), + "failed to find added printer driver"); + + if (remote_driver_dir) { + ASSERT_DRIVER_PATH(tctx, info.info2.driver_path, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info2.data_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info2.config_file, remote_driver_dir, "unexpected path"); + } + + return true; +} + +static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + struct spoolss_AddDriverInfo3 info3; + union spoolss_DriverInfo info; + + info3.driver_name = r->driver_name; + info3.version = r->version; + info3.architecture = r->architecture; + info3.driver_path = r->driver_path; + info3.data_file = r->data_file; + info3.config_file = r->config_file; + info3.help_file = r->help_file; + info3.monitor_name = r->monitor_name; + info3.default_datatype = r->default_datatype; + info3._ndr_size_dependent_files = r->_ndr_size_dependent_files; + info3.dependent_files = r->dependent_files; + + info_ctr.level = 3; + info_ctr.info.info3 = &info3; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK), + "failed to test AddPrinterDriverEx level 3"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK), + "failed to test AddPrinterDriver level 3"); + } + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name, &info), + "failed to find added printer driver"); + + if (remote_driver_dir) { + int i; + ASSERT_DRIVER_PATH(tctx, info.info3.driver_path, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info3.data_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info3.config_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info3.help_file, remote_driver_dir, "unexpected path"); + for (i=0; info.info3.dependent_files && info.info3.dependent_files[i] != NULL; i++) { + ASSERT_DRIVER_PATH(tctx, info.info3.dependent_files[i], remote_driver_dir, "unexpected path"); + } + } + + return true; +} + +static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + struct spoolss_AddDriverInfo4 info4; + union spoolss_DriverInfo info; + + info4.version = r->version; + info4.driver_name = r->driver_name; + info4.architecture = r->architecture; + info4.driver_path = r->driver_path; + info4.data_file = r->data_file; + info4.config_file = r->config_file; + info4.help_file = r->help_file; + info4.monitor_name = r->monitor_name; + info4.default_datatype = r->default_datatype; + info4._ndr_size_dependent_files = r->_ndr_size_dependent_files; + info4.dependent_files = r->dependent_files; + info4._ndr_size_previous_names = r->_ndr_size_previous_names; + info4.previous_names = r->previous_names; + + info_ctr.level = 4; + info_ctr.info.info4 = &info4; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK), + "failed to test AddPrinterDriverEx level 4"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK), + "failed to test AddPrinterDriver level 4"); + } + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name, &info), + "failed to find added printer driver"); + + if (remote_driver_dir) { + int i; + ASSERT_DRIVER_PATH(tctx, info.info4.driver_path, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info4.data_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info4.config_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info4.help_file, remote_driver_dir, "unexpected path"); + for (i=0; info.info4.dependent_files && info.info4.dependent_files[i] != NULL; i++) { + ASSERT_DRIVER_PATH(tctx, info.info4.dependent_files[i], remote_driver_dir, "unexpected path"); + } + } + + return true; +} + +static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + struct spoolss_AddDriverInfo6 info6; + union spoolss_DriverInfo info; + + info6.version = r->version; + info6.driver_name = r->driver_name; + info6.architecture = r->architecture; + info6.driver_path = r->driver_path; + info6.data_file = r->data_file; + info6.config_file = r->config_file; + info6.help_file = r->help_file; + info6.monitor_name = r->monitor_name; + info6.default_datatype = r->default_datatype; + info6._ndr_size_dependent_files = r->_ndr_size_dependent_files; + info6.dependent_files = r->dependent_files; + info6._ndr_size_previous_names = r->_ndr_size_previous_names; + info6.previous_names = r->previous_names; + info6.driver_date = r->driver_date; + info6.driver_version = r->driver_version; + info6.manufacturer_name = r->manufacturer_name; + info6.manufacturer_url = r->manufacturer_url; + info6.hardware_id = r->hardware_id; + info6.provider = r->provider; + + info_ctr.level = 6; + info_ctr.info.info6 = &info6; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK), + "failed to test AddPrinterDriverEx level 6"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriver level 6"); + } + + /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */ + + if (!ex) { + return true; + } + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name, &info), + "failed to find added printer driver"); + + if (remote_driver_dir) { + int i; + ASSERT_DRIVER_PATH(tctx, info.info6.driver_path, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info6.data_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info6.config_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info6.help_file, remote_driver_dir, "unexpected path"); + for (i=0; info.info6.dependent_files && info.info6.dependent_files[i] != NULL; i++) { + ASSERT_DRIVER_PATH(tctx, info.info6.dependent_files[i], remote_driver_dir, "unexpected path"); + } + } + + torture_assert_nttime_equal(tctx, info.info6.driver_date, info6.driver_date, "driverdate mismatch"); + torture_assert_u64_equal(tctx, info.info6.driver_version, info6.driver_version, "driverversion mismatch"); + + return true; +} + +static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + struct spoolss_AddDriverInfo8 *r, + uint32_t flags, + bool ex, + const char *remote_driver_dir) +{ + struct spoolss_AddDriverInfoCtr info_ctr; + union spoolss_DriverInfo info; + + info_ctr.level = 8; + info_ctr.info.info8 = r; + + if (ex) { + torture_assert(tctx, + test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK), + "failed to test AddPrinterDriverEx level 8"); + } else { + torture_assert(tctx, + test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL), + "failed to test AddPrinterDriver level 8"); + } + + /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */ + + if (!ex) { + return true; + } + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name, &info), + "failed to find added printer driver"); + + if (remote_driver_dir) { + int i; + ASSERT_DRIVER_PATH(tctx, info.info8.driver_path, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info8.data_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info8.config_file, remote_driver_dir, "unexpected path"); + ASSERT_DRIVER_PATH(tctx, info.info8.help_file, remote_driver_dir, "unexpected path"); + for (i=0; info.info8.dependent_files && info.info8.dependent_files[i] != NULL; i++) { + ASSERT_DRIVER_PATH(tctx, info.info8.dependent_files[i], remote_driver_dir, "unexpected path"); + } + } + + torture_assert_nttime_equal(tctx, info.info8.driver_date, r->driver_date, "driverdate mismatch"); + torture_assert_u64_equal(tctx, info.info8.driver_version, r->driver_version, "driverversion mismatch"); + + return true; +} + +#undef ASSERT_DRIVER_PATH + +static bool test_DeletePrinterDriver_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server, + const char *driver, + const char *environment, + WERROR expected_result) +{ + struct spoolss_DeletePrinterDriver r; + + r.in.server = server; + r.in.architecture = environment; + r.in.driver = driver; + + torture_comment(tctx, "Testing DeletePrinterDriver(%s)\n", driver); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrinterDriver_r(b, tctx, &r), + "DeletePrinterDriver failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "DeletePrinterDriver failed with unexpected result"); + + return true; +} + +static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server, + const char *driver, + const char *environment, + uint32_t delete_flags, + uint32_t version, + WERROR expected_result) +{ + struct spoolss_DeletePrinterDriverEx r; + + r.in.server = server; + r.in.architecture = environment; + r.in.driver = driver; + r.in.delete_flags = delete_flags; + r.in.version = version; + + torture_comment(tctx, "Testing DeletePrinterDriverEx(%s)\n", driver); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_DeletePrinterDriverEx_r(b, tctx, &r), + "DeletePrinterDriverEx failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "DeletePrinterDriverEx failed with unexpected result"); + + return true; +} + +static bool test_DeletePrinterDriver(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + const char *driver, + const char *environment) +{ + torture_assert(tctx, + test_DeletePrinterDriver_exp(tctx, b, server_name, driver, "FOOBAR", WERR_INVALID_ENVIRONMENT), + "failed to delete driver"); + + torture_assert(tctx, + test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK), + "failed to delete driver"); + + if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) { + torture_fail(tctx, "deleted driver still enumerated"); + } + + torture_assert(tctx, + test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_UNKNOWN_PRINTER_DRIVER), + "2nd delete failed"); + + return true; +} + +static bool test_DeletePrinterDriverEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + const char *driver, + const char *environment, + uint32_t delete_flags, + uint32_t version) +{ + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, "FOOBAR", delete_flags, version, WERR_INVALID_ENVIRONMENT), + "failed to delete driver"); + + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_OK), + "failed to delete driver"); + + if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) { + torture_fail(tctx, "deleted driver still enumerated"); + } + + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_UNKNOWN_PRINTER_DRIVER), + "2nd delete failed"); + + return true; +} + +static bool test_PrinterDriver_args(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *server_name, + uint32_t level, + struct spoolss_AddDriverInfo8 *r, + uint32_t add_flags, + uint32_t delete_flags, + uint32_t delete_version, + bool ex, + const char *remote_driver_dir) +{ + bool ret = true; + + switch (level) { + case 1: + ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + case 2: + ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + case 3: + ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + case 4: + ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + case 6: + ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + case 8: + ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex, remote_driver_dir); + break; + default: + return false; + } + + if (ret == false) { + return ret; + } + + if (level == 1) { + return ret; + } + + /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */ + + if (!ex && (level == 6 || level == 8)) { + return ret; + } + + { + struct dcerpc_pipe *p2; + struct policy_handle hive_handle; + struct dcerpc_binding_handle *b2; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &p2, &ndr_table_winreg), + "could not open winreg pipe"); + b2 = p2->binding_handle; + + torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), ""); + + ret = test_GetDriverInfo_winreg(tctx, b, NULL, NULL, r->driver_name, r->architecture, r->version, b2, &hive_handle, server_name); + + test_winreg_CloseKey(tctx, b2, &hive_handle); + + talloc_free(p2); + } + + if (ex) { + return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version); + } else { + return test_DeletePrinterDriver(tctx, b, server_name, r->driver_name, r->architecture); + } +} + +static bool fillup_printserver_info(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct torture_driver_context *d) +{ + struct policy_handle server_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + torture_assert(tctx, + test_OpenPrinter_server(tctx, p, &server_handle), + "failed to open printserver"); + torture_assert(tctx, + test_get_environment(tctx, b, &server_handle, &d->remote.environment), + "failed to get environment"); + torture_assert(tctx, + test_ClosePrinter(tctx, b, &server_handle), + "failed to close printserver"); + + torture_assert(tctx, + test_GetPrinterDriverDirectory_getdir(tctx, b, server_name_slash, + d->local.environment ? d->local.environment : d->remote.environment, + &d->remote.driver_directory), + "failed to get driver directory"); + + return true; +} + +static const char *driver_directory_dir(const char *driver_directory) +{ + char *p; + + p = strrchr(driver_directory, '\\'); + if (p) { + return p+1; + } + + return NULL; +} + +static const char *driver_directory_share(struct torture_context *tctx, + const char *driver_directory) +{ + const char *p; + char *tok; + + if (driver_directory[0] == '\\' && driver_directory[1] == '\\') { + driver_directory += 2; + } + + p = talloc_strdup(tctx, driver_directory); + + torture_assert(tctx, + next_token_talloc(tctx, &p, &tok, "\\"), + "cannot explode uri"); + torture_assert(tctx, + next_token_talloc(tctx, &p, &tok, "\\"), + "cannot explode uri"); + + return tok; +} + +#define CREATE_PRINTER_DRIVER_PATH(_d, _file) \ + talloc_asprintf((_d), "%s\\%s\\%s", (_d)->remote.driver_directory, (_d)->remote.driver_upload_directory, (_file)) + + +static bool create_printer_driver_directory(struct torture_context *tctx, + struct smbcli_state *cli, + struct torture_driver_context *d) +{ + char *driver_dir; + + if (d->remote.driver_upload_directory == NULL) { + return true; + } + + driver_dir = talloc_asprintf(tctx, + "%s\\%s", + driver_directory_dir(d->remote.driver_directory), + d->remote.driver_upload_directory); + torture_assert_not_null(tctx, driver_dir, "ENOMEM"); + + torture_comment(tctx, + "Create remote driver directory: %s\n", + driver_dir); + + torture_assert_ntstatus_ok(tctx, + smbcli_mkdir(cli->tree, + driver_dir), + "Failed to create driver directory"); + + return true; +} + +static bool upload_printer_driver_file(struct torture_context *tctx, + struct smbcli_state *cli, + struct torture_driver_context *d, + const char *file_name) +{ + FILE *f; + int fnum; + uint8_t *buf; + int maxwrite = 64512; + off_t nread = 0; + size_t start = 0; + const char *remote_dir = driver_directory_dir(d->remote.driver_directory); + const char *remote_name; + const char *local_name; + const char *p; + + if (!file_name || strlen(file_name) == 0) { + return true; + } + + p = strrchr(file_name, '\\'); + if (p == NULL) { + p = file_name; + } else { + p++; + } + + local_name = talloc_asprintf(tctx, "%s/%s", d->local.driver_directory, p); + torture_assert_not_null(tctx, local_name, "ENOMEM"); + if (d->remote.driver_upload_directory != NULL) { + remote_name = talloc_asprintf(tctx, + "%s\\%s\\%s", + remote_dir, + d->remote.driver_upload_directory, + p); + } else { + remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, p); + } + torture_assert_not_null(tctx, remote_name, "ENOMEM"); + + torture_comment(tctx, "Uploading %s to %s\n", local_name, remote_name); + + fnum = smbcli_open(cli->tree, remote_name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE); + if (fnum == -1) { + torture_fail(tctx, talloc_asprintf(tctx, "failed to open remote file: %s\n", remote_name)); + } + + f = fopen(local_name, "r"); + if (f == NULL) { + torture_fail(tctx, talloc_asprintf(tctx, "failed to open local file: %s\n", local_name)); + } + + buf = talloc_array(tctx, uint8_t, maxwrite); + if (!buf) { + fclose(f); + return false; + } + + while (!feof(f)) { + int n = maxwrite; + int ret; + + if ((n = fread(buf, 1, n, f)) < 1) { + if((n == 0) && feof(f)) + break; /* Empty local file. */ + + torture_warning(tctx, + "failed to read file: %s\n", strerror(errno)); + break; + } + + ret = smbcli_write(cli->tree, fnum, 0, buf, nread + start, n); + + if (n != ret) { + torture_warning(tctx, + "failed to write file: %s\n", smbcli_errstr(cli->tree)); + break; + } + + nread += n; + } + + fclose(f); + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli->tree, fnum), + "failed to close file"); + + return true; +} + +static bool connect_printer_driver_share(struct torture_context *tctx, + const char *server_name, + const char *share_name, + struct smbcli_state **cli) +{ + struct smbcli_options smb_options; + struct smbcli_session_options smb_session_options; + + torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n", + share_name, server_name); + + lpcfg_smbcli_options(tctx->lp_ctx, &smb_options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options); + + torture_assert_ntstatus_ok(tctx, + smbcli_full_connection(tctx, cli, server_name, + lpcfg_smb_ports(tctx->lp_ctx), + share_name, NULL, + lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, + &smb_options, + &smb_session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)), + "failed to open driver share"); + + return true; +} + +static bool upload_printer_driver(struct torture_context *tctx, + const char *server_name, + struct torture_driver_context *d) +{ + struct smbcli_state *cli; + const char *share_name = driver_directory_share(tctx, d->remote.driver_directory); + int i; + + torture_assert(tctx, + connect_printer_driver_share(tctx, server_name, share_name, &cli), + "failed to connect to driver share"); + + torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n", + server_name, share_name); + + torture_assert(tctx, + create_printer_driver_directory(tctx, cli, d), + "failed to create driver directory"); + + torture_assert(tctx, + upload_printer_driver_file(tctx, cli, d, d->info8.driver_path), + "failed to upload driver_path"); + torture_assert(tctx, + upload_printer_driver_file(tctx, cli, d, d->info8.data_file), + "failed to upload data_file"); + torture_assert(tctx, + upload_printer_driver_file(tctx, cli, d, d->info8.config_file), + "failed to upload config_file"); + torture_assert(tctx, + upload_printer_driver_file(tctx, cli, d, d->info8.help_file), + "failed to upload help_file"); + if (d->info8.dependent_files) { + for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) { + torture_assert(tctx, + upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]), + "failed to upload dependent_files"); + } + } + + talloc_free(cli); + + return true; +} + +static bool check_printer_driver_file(struct torture_context *tctx, + struct smbcli_state *cli, + struct torture_driver_context *d, + const char *file_name) +{ + const char *remote_arch_dir = driver_directory_dir(d->remote.driver_directory); + const char *remote_name = talloc_asprintf(tctx, "%s\\%d\\%s", + remote_arch_dir, + d->info8.version, + file_name); + int fnum; + + torture_assert(tctx, (file_name && strlen(file_name) != 0), "invalid filename"); + + torture_comment(tctx, "checking for driver file at %s\n", remote_name); + + fnum = smbcli_open(cli->tree, remote_name, O_RDONLY, DENY_NONE); + if (fnum == -1) { + return false; + } + + torture_assert_ntstatus_ok(tctx, + smbcli_close(cli->tree, fnum), + "failed to close driver file"); + + return true; +} + +static bool check_printer_driver_files(struct torture_context *tctx, + const char *server_name, + struct torture_driver_context *d, + bool expect_exist) +{ + struct smbcli_state *cli; + const char *share_name = driver_directory_share(tctx, d->remote.driver_directory); + int i; + + torture_assert(tctx, + connect_printer_driver_share(tctx, server_name, share_name, &cli), + "failed to connect to driver share"); + + torture_comment(tctx, "checking %sexistent driver files at \\\\%s\\%s\n", + (expect_exist ? "": "non-"), + server_name, share_name); + + if (d->info8.driver_path && d->info8.driver_path[0]) { + torture_assert(tctx, + check_printer_driver_file(tctx, cli, d, d->info8.driver_path) == expect_exist, + "failed driver_path check"); + } + if (d->info8.data_file && d->info8.data_file[0]) { + torture_assert(tctx, + check_printer_driver_file(tctx, cli, d, d->info8.data_file) == expect_exist, + "failed data_file check"); + } + if (d->info8.config_file && d->info8.config_file[0]) { + torture_assert(tctx, + check_printer_driver_file(tctx, cli, d, d->info8.config_file) == expect_exist, + "failed config_file check"); + } + if (d->info8.help_file && d->info8.help_file[0]) { + torture_assert(tctx, + check_printer_driver_file(tctx, cli, d, d->info8.help_file) == expect_exist, + "failed help_file check"); + } + if (d->info8.dependent_files) { + for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) { + torture_assert(tctx, + check_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]) == expect_exist, + "failed dependent_files check"); + } + } + + talloc_free(cli); + + return true; +} + +static bool remove_printer_driver_file(struct torture_context *tctx, + struct smbcli_state *cli, + struct torture_driver_context *d, + const char *file_name) +{ + const char *remote_name; + const char *remote_dir = driver_directory_dir(d->remote.driver_directory); + + if (!file_name || strlen(file_name) == 0) { + return true; + } + + remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name); + + torture_comment(tctx, "Removing %s\n", remote_name); + + torture_assert_ntstatus_ok(tctx, + smbcli_unlink(cli->tree, remote_name), + "failed to unlink"); + + return true; +} + +static bool remove_printer_driver(struct torture_context *tctx, + const char *server_name, + struct torture_driver_context *d) +{ + struct smbcli_state *cli; + const char *share_name = driver_directory_share(tctx, d->remote.driver_directory); + int i; + + torture_assert(tctx, + connect_printer_driver_share(tctx, server_name, share_name, &cli), + "failed to connect to driver share"); + + torture_comment(tctx, "Removing printer driver files from \\\\%s\\%s\n", + server_name, share_name); + + torture_assert(tctx, + remove_printer_driver_file(tctx, cli, d, d->info8.driver_path), + "failed to remove driver_path"); + torture_assert(tctx, + remove_printer_driver_file(tctx, cli, d, d->info8.data_file), + "failed to remove data_file"); + if (!strequal(d->info8.config_file, d->info8.driver_path)) { + torture_assert(tctx, + remove_printer_driver_file(tctx, cli, d, d->info8.config_file), + "failed to remove config_file"); + } + torture_assert(tctx, + remove_printer_driver_file(tctx, cli, d, d->info8.help_file), + "failed to remove help_file"); + if (d->info8.dependent_files) { + for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) { + if (strequal(d->info8.dependent_files->string[i], d->info8.driver_path) || + strequal(d->info8.dependent_files->string[i], d->info8.data_file) || + strequal(d->info8.dependent_files->string[i], d->info8.config_file) || + strequal(d->info8.dependent_files->string[i], d->info8.help_file)) { + continue; + } + torture_assert(tctx, + remove_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]), + "failed to remove dependent_files"); + } + } + + talloc_free(cli); + + return true; + +} + +static bool test_add_driver_arg(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct torture_driver_context *d) +{ + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + uint32_t levels[] = { 1, 2, 3, 4, 6, 8 }; + int i; + struct spoolss_AddDriverInfo8 info8; + uint32_t add_flags = APD_COPY_NEW_FILES; + uint32_t delete_flags = 0; + + ZERO_STRUCT(info8); + + torture_comment(tctx, "Testing PrinterDriver%s '%s' for environment '%s'\n", + d->ex ? "Ex" : "", d->info8.driver_name, d->local.environment); + + torture_assert(tctx, + fillup_printserver_info(tctx, p, d), + "failed to fillup printserver info"); + + if (!directory_exist(d->local.driver_directory)) { + torture_skip(tctx, "Skipping Printer Driver test as no local driver is available"); + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to upload printer driver"); + + info8 = d->info8; + if (d->info8.dependent_files) { + info8.dependent_files = talloc_zero(tctx, struct spoolss_StringArray); + if (d->info8.dependent_files->string) { + for (i=0; d->info8.dependent_files->string[i] != NULL; i++) { + } + info8.dependent_files->string = talloc_zero_array(info8.dependent_files, const char *, i+1); + for (i=0; d->info8.dependent_files->string[i] != NULL; i++) { + info8.dependent_files->string[i] = talloc_strdup(info8.dependent_files->string, d->info8.dependent_files->string[i]); + } + } + } + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + if (torture_setting_bool(tctx, "samba3", false)) { + switch (levels[i]) { + case 2: + case 4: + torture_comment(tctx, "skipping level %d against samba\n", levels[i]); + continue; + default: + break; + } + } + if (torture_setting_bool(tctx, "w2k3", false)) { + switch (levels[i]) { + case 8: + torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]); + continue; + default: + break; + } + } + + torture_comment(tctx, + "Testing PrinterDriver%s '%s' add & delete level %d\n", + d->ex ? "Ex" : "", info8.driver_name, levels[i]); + + ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory); + } + + info8.driver_path = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.driver_path); + info8.data_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.data_file); + if (d->info8.config_file) { + info8.config_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file); + } + if (d->info8.help_file) { + info8.help_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.help_file); + } + if (d->info8.dependent_files && d->info8.dependent_files->string) { + for (i=0; d->info8.dependent_files->string[i] != NULL; i++) { + info8.dependent_files->string[i] = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.dependent_files->string[i]); + } + } + + for (i=0; i < ARRAY_SIZE(levels); i++) { + + if (torture_setting_bool(tctx, "samba3", false)) { + switch (levels[i]) { + case 2: + case 4: + continue; + default: + break; + } + } + if (torture_setting_bool(tctx, "w2k3", false)) { + switch (levels[i]) { + case 8: + torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]); + continue; + default: + break; + } + } + + torture_comment(tctx, + "Testing PrinterDriver%s '%s' add & delete level %d (full unc paths)\n", + d->ex ? "Ex" : "", info8.driver_name, levels[i]); + + ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory); + } + + torture_assert(tctx, + remove_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to remove printer driver"); + + torture_comment(tctx, "\n"); + + return ret; +} + +static bool test_add_driver_ex_64(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_EX; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->ex = true; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_ex_32(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_EX; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->ex = true; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_64(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_ADD; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->ex = false; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_32(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_ADD; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->ex = false; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_adobe(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + + if (!torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skipping adobe test which only works against samba3"); + } + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, "Windows 4.0"); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_9X; + d->info8.driver_name = TORTURE_DRIVER_ADOBE; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV"); + d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD"); + d->info8.config_file = talloc_strdup(d, "ADOBEPS4.DRV"); +#if 0 + d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP"); + d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL"); +#endif + d->ex = false; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_adobe_cupsaddsmb(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + struct spoolss_StringArray *a; + + if (!torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skipping cupsaddsmb test which only works against samba3"); + } + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, "Windows 4.0"); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_9X; + d->info8.driver_name = TORTURE_DRIVER_ADOBE_CUPSADDSMB; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV"); + d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD"); + d->info8.config_file = NULL; + d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP"); + d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL"); + d->info8.default_datatype = talloc_strdup(d, "RAW"); + + a = talloc_zero(d, struct spoolss_StringArray); + a->string = talloc_zero_array(a, const char *, 7); + a->string[0] = talloc_strdup(a->string, "ADOBEPS4.DRV"); + a->string[1] = talloc_strdup(a->string, "DEFPRTR2.PPD"); + a->string[2] = talloc_strdup(a->string, "ADOBEPS4.HLP"); + a->string[3] = talloc_strdup(a->string, "PSMON.DLL"); + a->string[4] = talloc_strdup(a->string, "ADFONTS.MFM"); + a->string[5] = talloc_strdup(a->string, "ICONLIB.DLL"); + + d->info8.dependent_files = a; + d->ex = false; + + return test_add_driver_arg(tctx, p, d); +} + +static bool test_add_driver_timestamps(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + struct timeval t = timeval_current(); + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_TIMESTAMPS; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->info8.driver_date = timeval_to_nttime(&t); + d->ex = true; + + torture_assert(tctx, + test_add_driver_arg(tctx, p, d), + ""); + + unix_to_nt_time(&d->info8.driver_date, 1); + + torture_assert(tctx, + test_add_driver_arg(tctx, p, d), + ""); + + return true; +} + +static bool test_multiple_drivers(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + int i; + struct spoolss_AddDriverInfo8 info8; + uint32_t add_flags = APD_COPY_NEW_FILES; + uint32_t delete_flags = 0; + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386"); + + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->info8.architecture = d->local.environment; + d->ex = true; + + torture_assert(tctx, + fillup_printserver_info(tctx, p, d), + "failed to fillup printserver info"); + + if (!directory_exist(d->local.driver_directory)) { + torture_skip(tctx, "Skipping Printer Driver test as no local driver is available"); + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to upload printer driver"); + + info8 = d->info8; + + for (i=0; i < 3; i++) { + info8.driver_name = talloc_asprintf(d, "torture_test_driver_%d", i); + + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &info8, add_flags, true, NULL), + "failed to add driver"); + } + + torture_assert(tctx, + test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_0", info8.architecture, delete_flags, info8.version), + "failed to delete driver"); + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_1", NULL), + "torture_test_driver_1 no longer on the server"); + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL), + "torture_test_driver_2 no longer on the server"); + + torture_assert(tctx, + test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_1", info8.architecture, delete_flags, info8.version), + "failed to delete driver"); + + torture_assert(tctx, + test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL), + "torture_test_driver_2 no longer on the server"); + + torture_assert(tctx, + test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_2", info8.architecture, delete_flags, info8.version), + "failed to delete driver"); + + torture_assert(tctx, + remove_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to remove printer driver"); + + return true; +} + +static bool test_driver_copy_from_directory(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *architecture) +{ + struct torture_driver_context *d; + struct spoolss_StringArray *a; + uint32_t add_flags = APD_COPY_NEW_FILES|APD_COPY_FROM_DIRECTORY|APD_RETURN_BLOCKING_STATUS_CODE; + uint32_t delete_flags = DPD_DELETE_ALL_FILES; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, + "\\\\%s", + dcerpc_server_name(p)); + struct GUID guid = GUID_random(); + bool ok = false; + + d = talloc_zero(tctx, struct torture_driver_context); + torture_assert_not_null(tctx, d, "ENOMEM"); + + d->local.environment = talloc_strdup(d, architecture); + torture_assert_not_null_goto(tctx, d->local.environment, ok, done, "ENOMEM"); + + if (strequal(architecture, SPOOLSS_ARCHITECTURE_x64)) { + d->local.driver_directory = + talloc_strdup(d, "/usr/share/cups/drivers/x64"); + } else { + d->local.driver_directory = + talloc_strdup(d, "/usr/share/cups/drivers/i386"); + } + torture_assert_not_null_goto(tctx, d->local.driver_directory, ok, done, "ENOMEM"); + + d->remote.driver_upload_directory = GUID_string2(d, &guid); + torture_assert_not_null_goto(tctx, d->remote.driver_upload_directory, ok, done, "ENOMEM"); + + torture_assert(tctx, + fillup_printserver_info(tctx, p, d), + "failed to fillup printserver info"); + + d->ex = true; + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_COPY_DIR; + d->info8.architecture = d->local.environment; + + d->info8.driver_path = CREATE_PRINTER_DRIVER_PATH(d, "pscript5.dll"); + torture_assert_not_null_goto(tctx, d->info8.driver_path, ok, done, "ENOMEM"); + d->info8.data_file = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ppd"); + torture_assert_not_null_goto(tctx, d->info8.data_file, ok, done, "ENOMEM"); + d->info8.config_file = CREATE_PRINTER_DRIVER_PATH(d, "cupsui6.dll"); + torture_assert_not_null_goto(tctx, d->info8.config_file, ok, done, "ENOMEM"); + d->info8.help_file = CREATE_PRINTER_DRIVER_PATH(d, "pscript.hlp"); + torture_assert_not_null_goto(tctx, d->info8.help_file, ok, done, "ENOMEM"); + + a = talloc_zero(d, struct spoolss_StringArray); + torture_assert_not_null_goto(tctx, a, ok, done, "ENOMEM"); + a->string = talloc_zero_array(a, const char *, 3); + torture_assert_not_null_goto(tctx, a->string, ok, done, "ENOMEM"); + a->string[0] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.inf"); + torture_assert_not_null_goto(tctx, a->string[0], ok, done, "ENOMEM"); + a->string[1] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ini"); + torture_assert_not_null_goto(tctx, a->string[1], ok, done, "ENOMEM"); + + d->info8.dependent_files = a; + + if (!directory_exist(d->local.driver_directory)) { + torture_skip(tctx, + "Skipping Printer Driver test as no local drivers " + "are available"); + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to upload printer driver"); + + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, + b, + server_name_slash, + &d->info8, + add_flags, + true, + NULL), + "failed to add driver"); + + torture_assert(tctx, + test_DeletePrinterDriverEx(tctx, + b, + server_name_slash, + d->info8.driver_name, + d->local.environment, + delete_flags, + d->info8.version), + "failed to delete driver"); + + torture_assert(tctx, + check_printer_driver_files(tctx, + dcerpc_server_name(p), + d, + false), + "printer driver file check failed"); + + ok = true; +done: + talloc_free(d); + return ok; +} + +static bool test_driver_copy_from_directory_64(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_x64); +} + +static bool test_driver_copy_from_directory_32(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_NT_X86); +} + +static bool test_del_driver_all_files(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d; + struct spoolss_StringArray *a; + uint32_t add_flags = APD_COPY_NEW_FILES; + uint32_t delete_flags = DPD_DELETE_ALL_FILES; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + d = talloc_zero(tctx, struct torture_driver_context); + + d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64); + d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64"); + + d->ex = true; + d->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d->info8.driver_name = TORTURE_DRIVER_DELETER; + d->info8.architecture = d->local.environment; + d->info8.driver_path = talloc_strdup(d, "pscript5.dll"); + d->info8.data_file = talloc_strdup(d, "cups6.ppd"); + d->info8.config_file = talloc_strdup(d, "cupsui6.dll"); + d->info8.help_file = talloc_strdup(d, "pscript.hlp"); + + a = talloc_zero(d, struct spoolss_StringArray); + a->string = talloc_zero_array(a, const char *, 3); + a->string[0] = talloc_strdup(a->string, "cups6.inf"); + a->string[1] = talloc_strdup(a->string, "cups6.ini"); + + d->info8.dependent_files = a; + + torture_assert(tctx, + fillup_printserver_info(tctx, p, d), + "failed to fillup printserver info"); + + if (!directory_exist(d->local.driver_directory)) { + torture_skip(tctx, "Skipping Printer Driver test as no local driver is available"); + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d), + "failed to upload printer driver"); + + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d->info8, add_flags, true, NULL), + "failed to add driver"); + + torture_assert(tctx, + test_DeletePrinterDriverEx(tctx, b, server_name_slash, + d->info8.driver_name, + d->info8.architecture, + delete_flags, + d->info8.version), + "failed to delete driver"); + + torture_assert(tctx, + check_printer_driver_files(tctx, dcerpc_server_name(p), d, false), + "printer driver file check failed"); + + talloc_free(d); + return true; +} + +static bool test_del_driver_unused_files(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_driver_context *d1; + struct torture_driver_context *d2; + uint32_t add_flags = APD_COPY_NEW_FILES; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + d1 = talloc_zero(tctx, struct torture_driver_context); + d1->ex = true; + + d1->local.environment = talloc_strdup(d1, SPOOLSS_ARCHITECTURE_x64); + d1->local.driver_directory = talloc_strdup(d1, "/usr/share/cups/drivers/x64"); + + d1->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d1->info8.driver_name = TORTURE_DRIVER_DELETER; + d1->info8.architecture = NULL; + d1->info8.driver_path = talloc_strdup(d1, "pscript5.dll"); + d1->info8.data_file = talloc_strdup(d1, "cups6.ppd"); + d1->info8.config_file = talloc_strdup(d1, "cupsui6.dll"); + d1->info8.help_file = talloc_strdup(d1, "pscript.hlp"); + d1->info8.architecture = d1->local.environment; + + d2 = talloc_zero(tctx, struct torture_driver_context); + d2->ex = true; + + d2->local.environment = talloc_strdup(d2, SPOOLSS_ARCHITECTURE_x64); + d2->local.driver_directory = talloc_strdup(d2, "/usr/share/cups/drivers/x64"); + + d2->info8.version = SPOOLSS_DRIVER_VERSION_200X; + d2->info8.driver_name = TORTURE_DRIVER_DELETERIN; + d2->info8.architecture = NULL; + d2->info8.driver_path = talloc_strdup(d2, "pscript5.dll"); /* overlapping */ + d2->info8.data_file = talloc_strdup(d2, "cupsps6.dll"); + d2->info8.config_file = talloc_strdup(d2, "cups6.ini"); + d2->info8.help_file = talloc_strdup(d2, "pscript.hlp"); /* overlapping */ + d2->info8.architecture = d2->local.environment; + + torture_assert(tctx, + fillup_printserver_info(tctx, p, d1), + "failed to fillup printserver info"); + torture_assert(tctx, + fillup_printserver_info(tctx, p, d2), + "failed to fillup printserver info"); + + if (!directory_exist(d1->local.driver_directory)) { + torture_skip(tctx, "Skipping Printer Driver test as no local driver is available"); + } + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d1), + "failed to upload printer driver"); + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d1->info8, add_flags, true, NULL), + "failed to add driver"); + + torture_assert(tctx, + upload_printer_driver(tctx, dcerpc_server_name(p), d2), + "failed to upload printer driver"); + torture_assert(tctx, + test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d2->info8, add_flags, true, NULL), + "failed to add driver"); + + /* some files are in use by a separate driver, should fail */ + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash, + d1->info8.driver_name, + d1->info8.architecture, + DPD_DELETE_ALL_FILES, + d1->info8.version, + WERR_PRINTER_DRIVER_IN_USE), + "invalid delete driver response"); + + /* should only delete files not in use by other driver */ + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash, + d1->info8.driver_name, + d1->info8.architecture, + DPD_DELETE_UNUSED_FILES, + d1->info8.version, + WERR_OK), + "failed to delete driver (unused files)"); + + /* check non-overlapping were deleted */ + d1->info8.driver_path = NULL; + d1->info8.help_file = NULL; + torture_assert(tctx, + check_printer_driver_files(tctx, dcerpc_server_name(p), d1, false), + "printer driver file check failed"); + /* d2 files should be uneffected */ + torture_assert(tctx, + check_printer_driver_files(tctx, dcerpc_server_name(p), d2, true), + "printer driver file check failed"); + + torture_assert(tctx, + test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash, + d2->info8.driver_name, + d2->info8.architecture, + DPD_DELETE_ALL_FILES, + d2->info8.version, + WERR_OK), + "failed to delete driver"); + + torture_assert(tctx, + check_printer_driver_files(tctx, dcerpc_server_name(p), d2, false), + "printer driver file check failed"); + + talloc_free(d1); + talloc_free(d2); + return true; +} + +struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.driver"); + + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, + "driver", &ndr_table_spoolss); + torture_rpc_tcase_add_test(tcase, "add_driver_64", test_add_driver_64); + torture_rpc_tcase_add_test(tcase, "add_driver_ex_64", test_add_driver_ex_64); + + torture_rpc_tcase_add_test(tcase, "add_driver_32", test_add_driver_32); + torture_rpc_tcase_add_test(tcase, "add_driver_ex_32", test_add_driver_ex_32); + + torture_rpc_tcase_add_test(tcase, "add_driver_adobe", test_add_driver_adobe); + + torture_rpc_tcase_add_test(tcase, "add_driver_adobe_cupsaddsmb", test_add_driver_adobe_cupsaddsmb); + + torture_rpc_tcase_add_test(tcase, "add_driver_timestamps", test_add_driver_timestamps); + + torture_rpc_tcase_add_test(tcase, "multiple_drivers", test_multiple_drivers); + + torture_rpc_tcase_add_test(tcase, + "test_driver_copy_from_directory_64", + test_driver_copy_from_directory_64); + + torture_rpc_tcase_add_test(tcase, + "test_driver_copy_from_directory_32", + test_driver_copy_from_directory_32); + + torture_rpc_tcase_add_test(tcase, "del_driver_all_files", test_del_driver_all_files); + + torture_rpc_tcase_add_test(tcase, "del_driver_unused_files", test_del_driver_unused_files); + + return suite; +} diff --git a/source4/torture/rpc/spoolss_access.c b/source4/torture/rpc/spoolss_access.c new file mode 100644 index 0000000..b0d5265 --- /dev/null +++ b/source4/torture/rpc/spoolss_access.c @@ -0,0 +1,905 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc operations + + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" + +#define TORTURE_USER "torture_user" +#define TORTURE_USER_ADMINGROUP "torture_user_544" +#define TORTURE_USER_PRINTOPGROUP "torture_user_550" +#define TORTURE_USER_PRINTOPPRIV "torture_user_priv" +#define TORTURE_USER_SD "torture_user_sd" +#define TORTURE_WORKSTATION "torture_workstation" + +struct torture_user { + const char *username; + void *testuser; + uint32_t *builtin_memberships; + uint32_t num_builtin_memberships; + const char **privs; + uint32_t num_privs; + bool privs_present; + bool sd; + bool admin_rights; + bool system_security; +}; + +struct torture_access_context { + struct dcerpc_pipe *spoolss_pipe; + const char *printername; + struct security_descriptor *sd_orig; + struct torture_user user; +}; + +static bool test_openprinter_handle(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + const char *printername, + const char *username, + uint32_t access_mask, + WERROR expected_result, + struct policy_handle *handle) +{ + struct spoolss_OpenPrinterEx r; + struct spoolss_UserLevel1 level1; + struct dcerpc_binding_handle *b = p->binding_handle; + + level1.size = 28; + level1.client = talloc_asprintf(tctx, "\\\\%s", "smbtorture"); + level1.user = username; + /* Windows 7 and Windows Server 2008 R2 */ + level1.build = 7007; + level1.major = 6; + level1.minor = 1; + level1.processor= 0; + + r.in.printername = printername; + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = access_mask; + r.in.userlevel_ctr.level = 1; + r.in.userlevel_ctr.user_info.level1 = &level1; + r.out.handle = handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s) with access_mask 0x%08x (%s)\n", + r.in.printername, r.in.access_mask, name); + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), + "OpenPrinterEx failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + talloc_asprintf(tctx, "OpenPrinterEx(%s) as '%s' with access_mask: 0x%08x (%s) failed", + r.in.printername, username, r.in.access_mask, name)); + + return true; +} + +static bool test_openprinter_access(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *name, + const char *printername, + const char *username, + uint32_t access_mask, + WERROR expected_result) +{ + struct policy_handle handle; + struct dcerpc_binding_handle *b = p->binding_handle; + bool ret = true; + + ZERO_STRUCT(handle); + + if (printername == NULL) { + torture_comment(tctx, "skipping test %s as there is no printer\n", name); + return true; + } + + ret = test_openprinter_handle(tctx, p, name, printername, username, access_mask, expected_result, &handle); + if (is_valid_policy_hnd(&handle)) { + test_ClosePrinter(tctx, b, &handle); + } + + return ret; +} + +static bool spoolss_access_setup_membership(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t num_members, + uint32_t *members, + struct dom_sid *user_sid) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle connect_handle, domain_handle; + int i; + + torture_comment(tctx, + "Setting up BUILTIN membership for %s\n", + dom_sid_string(tctx, user_sid)); + for (i=0; i < num_members; i++) { + torture_comment(tctx, "adding user to S-1-5-32-%d\n", members[i]); + } + + { + struct samr_Connect2 r; + r.in.system_name = ""; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.connect_handle = &connect_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_Connect2_r(b, tctx, &r), + "samr_Connect2 failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_Connect2 failed"); + } + + { + struct samr_OpenDomain r; + r.in.connect_handle = &connect_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32"); + r.out.domain_handle = &domain_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenDomain_r(b, tctx, &r), + "samr_OpenDomain failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_OpenDomain failed"); + } + + for (i=0; i < num_members; i++) { + + struct policy_handle alias_handle; + + { + struct samr_OpenAlias r; + r.in.domain_handle = &domain_handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = members[i]; + r.out.alias_handle = &alias_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_OpenAlias_r(b, tctx, &r), + "samr_OpenAlias failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_OpenAlias failed"); + } + + { + struct samr_AddAliasMember r; + r.in.alias_handle = &alias_handle; + r.in.sid = user_sid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_samr_AddAliasMember_r(b, tctx, &r), + "samr_AddAliasMember failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "samr_AddAliasMember failed"); + } + + test_samr_handle_Close(b, tctx, &alias_handle); + } + + test_samr_handle_Close(b, tctx, &domain_handle); + test_samr_handle_Close(b, tctx, &connect_handle); + + return true; +} + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +static bool spoolss_access_setup_privs(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t num_privs, + const char **privs, + struct dom_sid *user_sid, + bool *privs_present) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle *handle; + int i; + + torture_assert(tctx, + test_lsa_OpenPolicy2(b, tctx, &handle), + "failed to open policy"); + + for (i=0; i < num_privs; i++) { + struct lsa_LookupPrivValue r; + struct lsa_LUID luid; + struct lsa_String name; + + init_lsa_String(&name, privs[i]); + + r.in.handle = handle; + r.in.name = &name; + r.out.luid = &luid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_LookupPrivValue_r(b, tctx, &r), + "lsa_LookupPrivValue failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "lsa_LookupPrivValue failed for '%s' with %s\n", + privs[i], nt_errstr(r.out.result)); + *privs_present = false; + return true; + } + } + + *privs_present = true; + + { + struct lsa_AddAccountRights r; + struct lsa_RightSet rights; + + rights.count = num_privs; + rights.names = talloc_zero_array(tctx, struct lsa_StringLarge, rights.count); + + for (i=0; i < rights.count; i++) { + init_lsa_StringLarge(&rights.names[i], privs[i]); + } + + r.in.handle = handle; + r.in.sid = user_sid; + r.in.rights = &rights; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_AddAccountRights_r(b, tctx, &r), + "lsa_AddAccountRights failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "lsa_AddAccountRights failed"); + } + + test_lsa_Close(b, tctx, handle); + + return true; +} + +static bool test_SetPrinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct spoolss_SetPrinterInfoCtr *info_ctr, + struct spoolss_DevmodeContainer *devmode_ctr, + struct sec_desc_buf *secdesc_ctr, + enum spoolss_PrinterControl command) +{ + struct spoolss_SetPrinter r; + + r.in.handle = handle; + r.in.info_ctr = info_ctr; + r.in.devmode_ctr = devmode_ctr; + r.in.secdesc_ctr = secdesc_ctr; + r.in.command = command; + + torture_comment(tctx, "Testing SetPrinter level %d\n", r.in.info_ctr->level); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), + "failed to call SetPrinter"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to call SetPrinter"); + + return true; +} + +static bool spoolss_access_setup_sd(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *printername, + const struct dom_sid *user_sid, + struct security_descriptor **sd_orig) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle handle; + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo3 info3; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct security_ace *ace; + struct security_descriptor *sd; + + torture_assert(tctx, + test_openprinter_handle(tctx, p, "", printername, "", SEC_FLAG_MAXIMUM_ALLOWED, WERR_OK, &handle), + "failed to open printer"); + + torture_assert(tctx, + test_GetPrinter_level(tctx, b, &handle, 3, &info), + "failed to get sd"); + + sd = security_descriptor_copy(tctx, info.info3.secdesc); + *sd_orig = security_descriptor_copy(tctx, info.info3.secdesc); + + ace = talloc_zero(tctx, struct security_ace); + + ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace->flags = 0; + ace->access_mask = PRINTER_ALL_ACCESS; + ace->trustee = *user_sid; + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_add(sd, ace), + "failed to add new ace"); + + ace = talloc_zero(tctx, struct security_ace); + + ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace->flags = SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY; + ace->access_mask = SEC_GENERIC_ALL; + ace->trustee = *user_sid; + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_add(sd, ace), + "failed to add new ace"); + + ZERO_STRUCT(info3); + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + info_ctr.level = 3; + info_ctr.info.info3 = &info3; + secdesc_ctr.sd = sd; + + torture_assert(tctx, + test_SetPrinter(tctx, b, &handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), + "failed to set sd"); + + return true; +} + +static bool test_EnumPrinters_findone(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char **printername) +{ + struct spoolss_EnumPrinters r; + uint32_t count; + union spoolss_PrinterInfo *info; + uint32_t needed; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + *printername = NULL; + + r.in.flags = PRINTER_ENUM_LOCAL; + r.in.server = NULL; + r.in.level = 1; + r.in.buffer = NULL; + r.in.offered = 0; + r.out.count = &count; + r.out.info = &info; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "failed to enum printers"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); + r.in.buffer = &blob; + r.in.offered = needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_EnumPrinters_r(b, tctx, &r), + "failed to enum printers"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "failed to enum printers"); + + for (i=0; i < count; i++) { + + if (count > 1 && strequal(info[i].info1.name, "Microsoft XPS Document Writer")) { + continue; + } + + torture_comment(tctx, "testing printer: %s\n", + info[i].info1.name); + + *printername = talloc_strdup(tctx, info[i].info1.name); + + break; + } + + return true; +} + +static bool torture_rpc_spoolss_access_setup_common(struct torture_context *tctx, struct torture_access_context *t) +{ + void *testuser; + const char *testuser_passwd; + struct cli_credentials *test_credentials; + struct dom_sid *test_sid; + struct dcerpc_pipe *p; + const char *printername; + const char *binding = torture_setting_string(tctx, "binding", NULL); + struct dcerpc_pipe *spoolss_pipe; + + testuser = torture_create_testuser_max_pwlen(tctx, t->user.username, + torture_setting_string(tctx, "workgroup", NULL), + ACB_NORMAL, + &testuser_passwd, + 32); + if (!testuser) { + torture_fail(tctx, "Failed to create test user"); + } + + test_credentials = cli_credentials_init(tctx); + cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, lpcfg_workgroup(tctx->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, t->user.username, CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED); + test_sid = discard_const_p(struct dom_sid, + torture_join_user_sid(testuser)); + + if (t->user.num_builtin_memberships) { + struct dcerpc_pipe *samr_pipe = torture_join_samr_pipe(testuser); + + torture_assert(tctx, + spoolss_access_setup_membership(tctx, samr_pipe, + t->user.num_builtin_memberships, + t->user.builtin_memberships, + test_sid), + "failed to setup membership"); + } + + if (t->user.num_privs) { + struct dcerpc_pipe *lsa_pipe; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &lsa_pipe, &ndr_table_lsarpc), + "Error connecting to server"); + + torture_assert(tctx, + spoolss_access_setup_privs(tctx, lsa_pipe, + t->user.num_privs, + t->user.privs, + test_sid, + &t->user.privs_present), + "failed to setup privs"); + talloc_free(lsa_pipe); + } + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &spoolss_pipe, &ndr_table_spoolss), + "Error connecting to server"); + + torture_assert(tctx, + test_EnumPrinters_findone(tctx, spoolss_pipe, &printername), + "failed to enumerate printers"); + + if (t->user.sd && printername) { + torture_assert(tctx, + spoolss_access_setup_sd(tctx, spoolss_pipe, + printername, + test_sid, + &t->sd_orig), + "failed to setup sd"); + } + + talloc_free(spoolss_pipe); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect(tctx, &p, binding, &ndr_table_spoolss, + test_credentials, tctx->ev, tctx->lp_ctx), + "Error connecting to server"); + + t->spoolss_pipe = p; + t->printername = printername; + t->user.testuser = testuser; + + return true; +} + +static bool torture_rpc_spoolss_access_setup(struct torture_context *tctx, void **data) +{ + struct torture_access_context *t; + + *data = t = talloc_zero(tctx, struct torture_access_context); + + t->user.username = talloc_strdup(t, TORTURE_USER); + + return torture_rpc_spoolss_access_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_access_admin_setup(struct torture_context *tctx, void **data) +{ + struct torture_access_context *t; + + *data = t = talloc_zero(tctx, struct torture_access_context); + + t->user.num_builtin_memberships = 1; + t->user.builtin_memberships = talloc_zero_array(t, uint32_t, t->user.num_builtin_memberships); + t->user.builtin_memberships[0] = BUILTIN_RID_ADMINISTRATORS; + t->user.username = talloc_strdup(t, TORTURE_USER_ADMINGROUP); + t->user.admin_rights = true; + t->user.system_security = true; + + return torture_rpc_spoolss_access_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_access_printop_setup(struct torture_context *tctx, void **data) +{ + struct torture_access_context *t; + + *data = t = talloc_zero(tctx, struct torture_access_context); + + t->user.num_builtin_memberships = 1; + t->user.builtin_memberships = talloc_zero_array(t, uint32_t, t->user.num_builtin_memberships); + t->user.builtin_memberships[0] = BUILTIN_RID_PRINT_OPERATORS; + t->user.username = talloc_strdup(t, TORTURE_USER_PRINTOPGROUP); + t->user.admin_rights = true; + + return torture_rpc_spoolss_access_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_access_priv_setup(struct torture_context *tctx, void **data) +{ + struct torture_access_context *t; + + *data = t = talloc_zero(tctx, struct torture_access_context); + + t->user.username = talloc_strdup(t, TORTURE_USER_PRINTOPPRIV); + t->user.num_privs = 1; + t->user.privs = talloc_zero_array(t, const char *, t->user.num_privs); + t->user.privs[0] = talloc_strdup(t, "SePrintOperatorPrivilege"); + t->user.admin_rights = true; + + return torture_rpc_spoolss_access_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_access_sd_setup(struct torture_context *tctx, void **data) +{ + struct torture_access_context *t; + + *data = t = talloc_zero(tctx, struct torture_access_context); + + t->user.username = talloc_strdup(t, TORTURE_USER_SD); + t->user.sd = true; + t->user.admin_rights = true; + + return torture_rpc_spoolss_access_setup_common(tctx, t); +} + +static bool torture_rpc_spoolss_access_teardown_common(struct torture_context *tctx, struct torture_access_context *t) +{ + if (t->user.testuser) { + torture_leave_domain(tctx, t->user.testuser); + } + + /* remove membership ? */ + if (t->user.num_builtin_memberships) { + } + + /* remove privs ? */ + if (t->user.num_privs) { + } + + /* restore sd */ + if (t->user.sd && t->printername) { + struct policy_handle handle; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo3 info3; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct dcerpc_pipe *spoolss_pipe; + struct dcerpc_binding_handle *b; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &spoolss_pipe, &ndr_table_spoolss), + "Error connecting to server"); + + b = spoolss_pipe->binding_handle; + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(info3); + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + info_ctr.level = 3; + info_ctr.info.info3 = &info3; + secdesc_ctr.sd = t->sd_orig; + + torture_assert(tctx, + test_openprinter_handle(tctx, spoolss_pipe, "", t->printername, "", SEC_FLAG_MAXIMUM_ALLOWED, WERR_OK, &handle), + "failed to open printer"); + + torture_assert(tctx, + test_SetPrinter(tctx, b, &handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), + "failed to set sd"); + + talloc_free(spoolss_pipe); + } + + return true; +} + +static bool torture_rpc_spoolss_access_teardown(struct torture_context *tctx, void *data) +{ + struct torture_access_context *t = talloc_get_type(data, struct torture_access_context); + bool ret; + + ret = torture_rpc_spoolss_access_teardown_common(tctx, t); + talloc_free(t); + + return ret; +} + +static bool test_openprinter(struct torture_context *tctx, + void *private_data) +{ + struct torture_access_context *t = + (struct torture_access_context *)talloc_get_type_abort(private_data, struct torture_access_context); + struct dcerpc_pipe *p = t->spoolss_pipe; + bool ret = true; + const char *username = t->user.username; + const char *servername_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + int i; + + struct { + const char *name; + uint32_t access_mask; + const char *printername; + WERROR expected_result; + } checks[] = { + + /* printserver handle tests */ + + { + .name = "0", + .access_mask = 0, + .printername = servername_slash, + .expected_result = WERR_OK + }, + { + .name = "SEC_FLAG_MAXIMUM_ALLOWED", + .access_mask = SEC_FLAG_MAXIMUM_ALLOWED, + .printername = servername_slash, + .expected_result = WERR_OK + }, + { + .name = "SERVER_ACCESS_ENUMERATE", + .access_mask = SERVER_ACCESS_ENUMERATE, + .printername = servername_slash, + .expected_result = WERR_OK + }, + { + .name = "SERVER_ACCESS_ADMINISTER", + .access_mask = SERVER_ACCESS_ADMINISTER, + .printername = servername_slash, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "SERVER_READ", + .access_mask = SERVER_READ, + .printername = servername_slash, + .expected_result = WERR_OK + }, + { + .name = "SERVER_WRITE", + .access_mask = SERVER_WRITE, + .printername = servername_slash, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "SERVER_EXECUTE", + .access_mask = SERVER_EXECUTE, + .printername = servername_slash, + .expected_result = WERR_OK + }, + { + .name = "SERVER_ALL_ACCESS", + .access_mask = SERVER_ALL_ACCESS, + .printername = servername_slash, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + + /* printer handle tests */ + + { + .name = "0", + .access_mask = 0, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "SEC_FLAG_MAXIMUM_ALLOWED", + .access_mask = SEC_FLAG_MAXIMUM_ALLOWED, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "SEC_FLAG_SYSTEM_SECURITY", + .access_mask = SEC_FLAG_SYSTEM_SECURITY, + .printername = t->printername, + .expected_result = t->user.system_security ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "0x010e0000", + .access_mask = 0x010e0000, + .printername = t->printername, + .expected_result = t->user.system_security ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "PRINTER_ACCESS_USE", + .access_mask = PRINTER_ACCESS_USE, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "SEC_STD_READ_CONTROL", + .access_mask = SEC_STD_READ_CONTROL, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "PRINTER_ACCESS_ADMINISTER", + .access_mask = PRINTER_ACCESS_ADMINISTER, + .printername = t->printername, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "SEC_STD_WRITE_DAC", + .access_mask = SEC_STD_WRITE_DAC, + .printername = t->printername, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "SEC_STD_WRITE_OWNER", + .access_mask = SEC_STD_WRITE_OWNER, + .printername = t->printername, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "PRINTER_READ", + .access_mask = PRINTER_READ, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "PRINTER_WRITE", + .access_mask = PRINTER_WRITE, + .printername = t->printername, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + }, + { + .name = "PRINTER_EXECUTE", + .access_mask = PRINTER_EXECUTE, + .printername = t->printername, + .expected_result = WERR_OK + }, + { + .name = "PRINTER_ALL_ACCESS", + .access_mask = PRINTER_ALL_ACCESS, + .printername = t->printername, + .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED + } + }; + + if (t->user.num_privs && !t->user.privs_present) { + torture_skip(tctx, "skipping test as not all required privileges are present on the server\n"); + } + + for (i=0; i < ARRAY_SIZE(checks); i++) { + ret &= test_openprinter_access(tctx, p, + checks[i].name, + checks[i].printername, + username, + checks[i].access_mask, + checks[i].expected_result); + } + + return ret; +} + +static bool test_openprinter_wrap(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct torture_access_context *t; + const char *printername; + bool ret = true; + + t = talloc_zero(tctx, struct torture_access_context); + + t->user.username = talloc_strdup(tctx, "dummy"); + t->spoolss_pipe = p; + + torture_assert(tctx, + test_EnumPrinters_findone(tctx, p, &printername), + "failed to enumerate printers"); + + t->printername = printername; + + ret = test_openprinter(tctx, (void *)t); + + talloc_free(t); + + return ret; +} + +struct torture_suite *torture_rpc_spoolss_access(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.access"); + struct torture_tcase *tcase; + struct torture_rpc_tcase *rpc_tcase; + + tcase = torture_suite_add_tcase(suite, "normaluser"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_access_setup, + torture_rpc_spoolss_access_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter); + + tcase = torture_suite_add_tcase(suite, "adminuser"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_access_admin_setup, + torture_rpc_spoolss_access_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter); + + tcase = torture_suite_add_tcase(suite, "printopuser"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_access_printop_setup, + torture_rpc_spoolss_access_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter); + + tcase = torture_suite_add_tcase(suite, "printopuserpriv"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_access_priv_setup, + torture_rpc_spoolss_access_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter); + + tcase = torture_suite_add_tcase(suite, "normaluser_sd"); + + torture_tcase_set_fixture(tcase, + torture_rpc_spoolss_access_sd_setup, + torture_rpc_spoolss_access_teardown); + + torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter); + + rpc_tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "workstation", + &ndr_table_spoolss, + TORTURE_WORKSTATION); + + torture_rpc_tcase_add_test(rpc_tcase, "openprinter", test_openprinter_wrap); + + return suite; +} diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c new file mode 100644 index 0000000..bd9dac4 --- /dev/null +++ b/source4/torture/rpc/spoolss_notify.c @@ -0,0 +1,636 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc notify operations + + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "torture/rpc/torture_rpc.h" +#include "rpc_server/dcerpc_server.h" +#include "rpc_server/dcerpc_server_proto.h" +#include "rpc_server/service_rpc.h" +#include "samba/process_model.h" +#include "smb_server/smb_server.h" +#include "lib/socket/netif.h" +#include "ntvfs/ntvfs.h" +#include "param/param.h" + +static struct dcesrv_context_callbacks srv_cb = { + .log.successful_authz = log_successful_dcesrv_authz_event, + .auth.gensec_prepare = dcesrv_gensec_prepare, + .assoc_group.find = dcesrv_assoc_group_find_s4, +}; + +static NTSTATUS spoolss__op_bind(struct dcesrv_connection_context *context, + const struct dcesrv_interface *iface) +{ + return NT_STATUS_OK; +} + +static void spoolss__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +} + +static NTSTATUS spoolss__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + dce_call->fault_code = 0; + + if (opnum >= ndr_table_spoolss.num_calls) { + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NET_WRITE_FAULT; + } + + *r = talloc_size(mem_ctx, ndr_table_spoolss.calls[opnum].struct_size); + NT_STATUS_HAVE_NO_MEMORY(*r); + + /* unravel the NDR for the packet */ + ndr_err = ndr_table_spoolss.calls[opnum].ndr_pull(pull, NDR_IN, *r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +/* Note that received_packets are allocated on the NULL context + * because no other context appears to stay around long enough. */ +static struct received_packet { + uint16_t opnum; + void *r; + struct received_packet *prev, *next; +} *received_packets = NULL; + +static void free_received_packets(void) +{ + struct received_packet *rp; + struct received_packet *rp_next; + + for (rp = received_packets; rp; rp = rp_next) { + rp_next = rp->next; + DLIST_REMOVE(received_packets, rp); + talloc_unlink(rp, rp->r); + talloc_free(rp); + } + received_packets = NULL; +} + +static WERROR _spoolss_ReplyOpenPrinter(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + struct spoolss_ReplyOpenPrinter *r) +{ + DEBUG(1,("_spoolss_ReplyOpenPrinter\n")); + + NDR_PRINT_IN_DEBUG(spoolss_ReplyOpenPrinter, r); + + r->out.handle = talloc(r, struct policy_handle); + r->out.handle->handle_type = 42; + r->out.handle->uuid = GUID_random(); + r->out.result = WERR_OK; + + NDR_PRINT_OUT_DEBUG(spoolss_ReplyOpenPrinter, r); + + return WERR_OK; +} + +static WERROR _spoolss_ReplyClosePrinter(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + struct spoolss_ReplyClosePrinter *r) +{ + DEBUG(1,("_spoolss_ReplyClosePrinter\n")); + + NDR_PRINT_IN_DEBUG(spoolss_ReplyClosePrinter, r); + + ZERO_STRUCTP(r->out.handle); + r->out.result = WERR_OK; + + NDR_PRINT_OUT_DEBUG(spoolss_ReplyClosePrinter, r); + + return WERR_OK; +} + +static WERROR _spoolss_RouterReplyPrinterEx(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + struct spoolss_RouterReplyPrinterEx *r) +{ + DEBUG(1,("_spoolss_RouterReplyPrinterEx\n")); + + NDR_PRINT_IN_DEBUG(spoolss_RouterReplyPrinterEx, r); + + r->out.reply_result = talloc(r, uint32_t); + *r->out.reply_result = 0; + r->out.result = WERR_OK; + + NDR_PRINT_OUT_DEBUG(spoolss_RouterReplyPrinterEx, r); + + return WERR_OK; +} + +static NTSTATUS spoolss__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + struct received_packet *rp; + + rp = talloc_zero(NULL, struct received_packet); + rp->opnum = opnum; + rp->r = talloc_reference(rp, r); + + DLIST_ADD_END(received_packets, rp); + + switch (opnum) { + case 58: { + struct spoolss_ReplyOpenPrinter *r2 = (struct spoolss_ReplyOpenPrinter *)r; + r2->out.result = _spoolss_ReplyOpenPrinter(dce_call, mem_ctx, r2); + break; + } + case 60: { + struct spoolss_ReplyClosePrinter *r2 = (struct spoolss_ReplyClosePrinter *)r; + r2->out.result = _spoolss_ReplyClosePrinter(dce_call, mem_ctx, r2); + break; + } + case 66: { + struct spoolss_RouterReplyPrinterEx *r2 = (struct spoolss_RouterReplyPrinterEx *)r; + r2->out.result = _spoolss_RouterReplyPrinterEx(dce_call, mem_ctx, r2); + break; + } + + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + return NT_STATUS_OK; +} + + +static NTSTATUS spoolss__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + return NT_STATUS_OK; +} + + +static NTSTATUS spoolss__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + ndr_err = ndr_table_spoolss.calls[opnum].ndr_push(push, NDR_OUT, r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static const struct dcesrv_interface notify_test_spoolss_interface = { + .name = "spoolss", + .syntax_id = {{0x12345678,0x1234,0xabcd,{0xef,0x00},{0x01,0x23,0x45,0x67,0x89,0xab}},1.0}, + .bind = spoolss__op_bind, + .unbind = spoolss__op_unbind, + .ndr_pull = spoolss__op_ndr_pull, + .dispatch = spoolss__op_dispatch, + .reply = spoolss__op_reply, + .ndr_push = spoolss__op_ndr_push +}; + +static bool spoolss__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version) +{ + if (notify_test_spoolss_interface.syntax_id.if_version == if_version && + GUID_equal(¬ify_test_spoolss_interface.syntax_id.uuid, uuid)) { + memcpy(iface,¬ify_test_spoolss_interface, sizeof(*iface)); + return true; + } + + return false; +} + +static bool spoolss__op_interface_by_name(struct dcesrv_interface *iface, const char *name) +{ + if (strcmp(notify_test_spoolss_interface.name, name)==0) { + memcpy(iface, ¬ify_test_spoolss_interface, sizeof(*iface)); + return true; + } + + return false; +} + +static NTSTATUS spoolss__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + uint32_t i; + + for (i=0;icount;i++) { + NTSTATUS ret; + const char *name = ndr_table_spoolss.endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, + name, + NULL, + ¬ify_test_spoolss_interface, + NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,("spoolss_op_init_server: failed to register endpoint '%s'\n",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS spoolss__op_shutdown_server(struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server) +{ + return NT_STATUS_OK; +} + +static bool test_OpenPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *printername) +{ + struct spoolss_OpenPrinter r; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + + r.in.printername = printername; + r.in.datatype = NULL; + r.in.devmode_ctr.devmode= NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = handle; + + torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername); + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinter_r(b, tctx, &r), + "OpenPrinter failed"); + torture_assert_werr_ok(tctx, r.out.result, + "OpenPrinter failed"); + + return true; +} + +static struct spoolss_NotifyOption *setup_printserver_NotifyOption(struct torture_context *tctx) +{ + struct spoolss_NotifyOption *o; + + o = talloc_zero(tctx, struct spoolss_NotifyOption); + + o->version = 2; + o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH; + + o->count = 2; + o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count); + + o->types[0].type = PRINTER_NOTIFY_TYPE; + o->types[0].count = 1; + o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count); + o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_SERVER_NAME; + + o->types[1].type = JOB_NOTIFY_TYPE; + o->types[1].count = 1; + o->types[1].fields = talloc_array(o->types, union spoolss_Field, o->types[1].count); + o->types[1].fields[0].field = JOB_NOTIFY_FIELD_MACHINE_NAME; + + return o; +} + +#if 0 +static struct spoolss_NotifyOption *setup_printer_NotifyOption(struct torture_context *tctx) +{ + struct spoolss_NotifyOption *o; + + o = talloc_zero(tctx, struct spoolss_NotifyOption); + + o->version = 2; + o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH; + + o->count = 1; + o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count); + + o->types[0].type = PRINTER_NOTIFY_TYPE; + o->types[0].count = 1; + o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count); + o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_COMMENT; + + return o; +} +#endif + +static bool test_RemoteFindFirstPrinterChangeNotifyEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *address, + struct spoolss_NotifyOption *option) +{ + struct spoolss_RemoteFindFirstPrinterChangeNotifyEx r; + const char *local_machine = talloc_asprintf(tctx, "\\\\%s", address); + + torture_comment(tctx, "Testing RemoteFindFirstPrinterChangeNotifyEx(%s)\n", local_machine); + + r.in.flags = 0; + r.in.local_machine = local_machine; + r.in.options = 0; + r.in.printer_local = 0; + r.in.notify_options = option; + r.in.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_RemoteFindFirstPrinterChangeNotifyEx_r(b, tctx, &r), + "RemoteFindFirstPrinterChangeNotifyEx failed"); + torture_assert_werr_ok(tctx, r.out.result, + "error return code for RemoteFindFirstPrinterChangeNotifyEx"); + + return true; +} + +static bool test_RouterRefreshPrinterChangeNotify(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct spoolss_NotifyOption *options, + struct spoolss_NotifyInfo **info) +{ + struct spoolss_RouterRefreshPrinterChangeNotify r; + + torture_comment(tctx, "Testing RouterRefreshPrinterChangeNotify\n"); + + r.in.handle = handle; + r.in.change_low = 0; + r.in.options = options; + r.out.info = info; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_RouterRefreshPrinterChangeNotify_r(b, tctx, &r), + "RouterRefreshPrinterChangeNotify failed"); + torture_assert_werr_ok(tctx, r.out.result, + "error return code for RouterRefreshPrinterChangeNotify"); + + return true; +} + +#if 0 +static bool test_SetPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + union spoolss_PrinterInfo info; + struct spoolss_SetPrinter r; + struct spoolss_SetPrinterInfo2 info2; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), ""); + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + info2.servername = info.info2.servername; + info2.printername = info.info2.printername; + info2.sharename = info.info2.sharename; + info2.portname = info.info2.portname; + info2.drivername = info.info2.drivername; + info2.comment = talloc_asprintf(tctx, "torture_comment %d\n", (int)time(NULL)); + info2.location = info.info2.location; + info2.devmode_ptr = 0; + info2.sepfile = info.info2.sepfile; + info2.printprocessor = info.info2.printprocessor; + info2.datatype = info.info2.datatype; + info2.parameters = info.info2.parameters; + info2.secdesc_ptr = 0; + info2.attributes = info.info2.attributes; + info2.priority = info.info2.priority; + info2.defaultpriority = info.info2.defaultpriority; + info2.starttime = info.info2.starttime; + info2.untiltime = info.info2.untiltime; + info2.status = info.info2.status; + info2.cjobs = info.info2.cjobs; + info2.averageppm = info.info2.averageppm; + + info_ctr.level = 2; + info_ctr.info.info2 = &info2; + + r.in.handle = handle; + r.in.info_ctr = &info_ctr; + r.in.devmode_ctr = &devmode_ctr; + r.in.secdesc_ctr = &secdesc_ctr; + r.in.command = 0; + + torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), "SetPrinter failed"); + torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed"); + + return true; +} +#endif + +static bool test_start_dcerpc_server(struct torture_context *tctx, + struct tevent_context *event_ctx, + struct dcesrv_context **dce_ctx_p, + const char **address_p) +{ + struct dcesrv_endpoint_server ep_server; + NTSTATUS status; + struct dcesrv_context *dce_ctx; + const char *endpoints[] = { "spoolss", NULL }; + struct dcesrv_endpoint *e; + const char *address; + struct interface *ifaces; + + ntvfs_init(tctx->lp_ctx); + + /* fill in our name */ + ep_server.name = "spoolss"; + + ep_server.initialized = false; + + /* fill in all the operations */ + ep_server.init_server = spoolss__op_init_server; + ep_server.shutdown_server = spoolss__op_shutdown_server; + + ep_server.interface_by_uuid = spoolss__op_interface_by_uuid; + ep_server.interface_by_name = spoolss__op_interface_by_name; + + torture_assert_ntstatus_ok(tctx, dcerpc_register_ep_server(&ep_server), + "unable to register spoolss server"); + + lpcfg_set_cmdline(tctx->lp_ctx, "dcerpc endpoint servers", "spoolss"); + + load_interface_list(tctx, tctx->lp_ctx, &ifaces); + address = iface_list_first_v4(ifaces); + + torture_comment(tctx, "Listening for callbacks on %s\n", address); + + status = process_model_init(tctx->lp_ctx); + torture_assert_ntstatus_ok(tctx, status, + "unable to initialize process models"); + + status = smbsrv_add_socket(tctx, event_ctx, tctx->lp_ctx, + process_model_startup("single"), + address, NULL); + torture_assert_ntstatus_ok(tctx, status, "starting smb server"); + + status = dcesrv_init_context(tctx, tctx->lp_ctx, &srv_cb, &dce_ctx); + torture_assert_ntstatus_ok(tctx, status, + "unable to initialize DCE/RPC server"); + + status = dcesrv_init_ep_servers(dce_ctx, endpoints); + torture_assert_ntstatus_ok(tctx, + status, + "unable to initialize DCE/RPC ep servers"); + + for (e=dce_ctx->endpoint_list;e;e=e->next) { + status = dcesrv_add_ep(dce_ctx, tctx->lp_ctx, + e, tctx->ev, + process_model_startup("single"), NULL); + torture_assert_ntstatus_ok(tctx, status, + "unable listen on dcerpc endpoint server"); + } + + *dce_ctx_p = dce_ctx; + *address_p = address; + + return true; +} + +static struct received_packet *last_packet(struct received_packet *p) +{ + struct received_packet *tmp; + for (tmp = p; tmp->next; tmp = tmp->next) { + } + return tmp; +} + +static bool test_RFFPCNEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcesrv_context *dce_ctx; + struct policy_handle handle; + const char *address; + struct received_packet *tmp; + struct spoolss_NotifyOption *server_option = setup_printserver_NotifyOption(tctx); +#if 0 + struct spoolss_NotifyOption *printer_option = setup_printer_NotifyOption(tctx); +#endif + struct dcerpc_binding_handle *b = p->binding_handle; + const char *printername = NULL; + struct spoolss_NotifyInfo *info = NULL; + + free_received_packets(); + + /* Start DCE/RPC server */ + torture_assert(tctx, test_start_dcerpc_server(tctx, tctx->ev, &dce_ctx, &address), ""); + + printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + + torture_assert(tctx, test_OpenPrinter(tctx, p, &handle, printername), ""); + torture_assert(tctx, test_RemoteFindFirstPrinterChangeNotifyEx(tctx, b, &handle, address, server_option), ""); + torture_assert(tctx, received_packets, "no packets received"); + torture_assert_int_equal(tctx, received_packets->opnum, NDR_SPOOLSS_REPLYOPENPRINTER, + "no ReplyOpenPrinter packet after RemoteFindFirstPrinterChangeNotifyEx"); + torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, b, &handle, NULL, &info), ""); + torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, b, &handle, server_option, &info), ""); + torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), ""); + tmp = last_packet(received_packets); + torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYCLOSEPRINTER, + "no ReplyClosePrinter packet after ClosePrinter"); +#if 0 + printername = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name); + + torture_assert(tctx, test_OpenPrinter(tctx, p, &handle, "Epson AL-2600"), ""); + torture_assert(tctx, test_RemoteFindFirstPrinterChangeNotifyEx(tctx, p, &handle, address, printer_option), ""); + tmp = last_packet(received_packets); + torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYOPENPRINTER, + "no ReplyOpenPrinter packet after RemoteFindFirstPrinterChangeNotifyEx"); + torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, p, &handle, NULL, &info), ""); + torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, p, &handle, printer_option, &info), ""); + torture_assert(tctx, test_SetPrinter(tctx, p, &handle), ""); + tmp = last_packet(received_packets); + torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_ROUTERREPLYPRINTEREX, + "no RouterReplyPrinterEx packet after ClosePrinter"); + torture_assert(tctx, test_ClosePrinter(tctx, p, &handle), ""); + tmp = last_packet(received_packets); + torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYCLOSEPRINTER, + "no ReplyClosePrinter packet after ClosePrinter"); +#endif + /* Shut down DCE/RPC server */ + talloc_free(dce_ctx); + free_received_packets(); + + return true; +} + +/** Test that makes sure that calling ReplyOpenPrinter() + * on Samba 4 will cause an irpc broadcast call. + */ +static bool test_ReplyOpenPrinter(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct spoolss_ReplyOpenPrinter r; + struct spoolss_ReplyClosePrinter s; + struct policy_handle h; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "skipping ReplyOpenPrinter server implementation test against s3\n"); + } + + r.in.server_name = "earth"; + r.in.printer_local = 2; + r.in.type = REG_DWORD; + r.in.bufsize = 0; + r.in.buffer = NULL; + r.out.handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_ReplyOpenPrinter_r(b, tctx, &r), + "spoolss_ReplyOpenPrinter call failed"); + + torture_assert_werr_ok(tctx, r.out.result, "error return code"); + + s.in.handle = &h; + s.out.handle = &h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_spoolss_ReplyClosePrinter_r(b, tctx, &s), + "spoolss_ReplyClosePrinter call failed"); + + torture_assert_werr_ok(tctx, r.out.result, "error return code"); + + return true; +} + +struct torture_suite *torture_rpc_spoolss_notify(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.notify"); + + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, + "notify", &ndr_table_spoolss); + + torture_rpc_tcase_add_test(tcase, "testRFFPCNEx", test_RFFPCNEx); + torture_rpc_tcase_add_test(tcase, "testReplyOpenPrinter", test_ReplyOpenPrinter); + + return suite; +} diff --git a/source4/torture/rpc/spoolss_win.c b/source4/torture/rpc/spoolss_win.c new file mode 100644 index 0000000..9162acd --- /dev/null +++ b/source4/torture/rpc/spoolss_win.c @@ -0,0 +1,612 @@ +/* + Unix SMB/CIFS implementation. + test suite for spoolss rpc operations as performed by various win versions + + Copyright (C) Kai Blin 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_spoolss_c.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "param/param.h" + +struct test_spoolss_win_context { + /* EnumPrinters */ + uint32_t printer_count; + union spoolss_PrinterInfo *printer_info; + union spoolss_PrinterInfo *current_info; + + /* EnumPrinterKeys */ + const char **printer_keys; + + bool printer_has_driver; +}; + +/* This is a convenience function for all OpenPrinterEx calls */ +static bool test_OpenPrinterEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *printer_name, + uint32_t access_mask) +{ + NTSTATUS status; + struct spoolss_OpenPrinterEx op; + struct spoolss_UserLevel1 ul_1; + + torture_comment(tctx, "Opening printer '%s'\n", printer_name); + + op.in.printername = talloc_strdup(tctx, printer_name); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode = NULL; + op.in.access_mask = access_mask; + op.in.userlevel_ctr.level = 1; + op.in.userlevel_ctr.user_info.level1 = &ul_1; + op.out.handle = handle; + + ul_1.size = 1234; + ul_1.client = "\\clientname"; + ul_1.user = "username"; + ul_1.build = 1; + ul_1.major = 2; + ul_1.minor = 3; + ul_1.processor = 4567; + + status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &op); + torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed"); + torture_assert_werr_ok(tctx, op.out.result, "OpenPrinterEx failed"); + + return true; +} + +static bool test_OpenPrinterAsAdmin(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + const char *printername) +{ + NTSTATUS status; + struct spoolss_OpenPrinterEx op; + struct spoolss_ClosePrinter cp; + struct spoolss_UserLevel1 ul_1; + struct policy_handle handle; + + ul_1.size = 1234; + ul_1.client = "\\clientname"; + ul_1.user = "username"; + ul_1.build = 1; + ul_1.major = 2; + ul_1.minor = 3; + ul_1.processor = 4567; + + op.in.printername = talloc_strdup(tctx, printername); + op.in.datatype = NULL; + op.in.devmode_ctr.devmode = NULL; + op.in.access_mask = SERVER_ALL_ACCESS; + op.in.userlevel_ctr.level = 1; + op.in.userlevel_ctr.user_info.level1 = &ul_1; + op.out.handle = &handle; + + cp.in.handle = &handle; + cp.out.handle = &handle; + + torture_comment(tctx, "Testing OpenPrinterEx(%s) with admin rights\n", + op.in.printername); + + status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &op); + + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(op.out.result)) { + status = dcerpc_spoolss_ClosePrinter_r(b, tctx, &cp); + torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed"); + } + + return true; +} + + + +/* This replicates the opening sequence of OpenPrinterEx calls XP does */ +static bool test_OpenPrinterSequence(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle) +{ + bool ret; + char *printername = talloc_asprintf(tctx, "\\\\%s", + dcerpc_server_name(p)); + struct dcerpc_binding_handle *b = p->binding_handle; + + /* First, see if we can open the printer read_only */ + ret = test_OpenPrinterEx(tctx, b, handle, printername, 0); + torture_assert(tctx, ret == true, "OpenPrinterEx failed."); + + ret = test_ClosePrinter(tctx, b, handle); + torture_assert(tctx, ret, "ClosePrinter failed"); + + /* Now let's see if we have admin rights to it. */ + ret = test_OpenPrinterAsAdmin(tctx, b, printername); + torture_assert(tctx, ret == true, + "OpenPrinterEx as admin failed unexpectedly."); + + ret = test_OpenPrinterEx(tctx, b, handle, printername, SERVER_EXECUTE); + torture_assert(tctx, ret == true, "OpenPrinterEx failed."); + + return true; +} + +static bool test_GetPrinterData(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *value_name, + WERROR expected_werr, + uint32_t expected_value) +{ + NTSTATUS status; + struct spoolss_GetPrinterData gpd; + uint32_t needed; + enum winreg_Type type; + uint8_t *data = talloc_zero_array(tctx, uint8_t, 4); + + torture_comment(tctx, "Testing GetPrinterData(%s).\n", value_name); + gpd.in.handle = handle; + gpd.in.value_name = value_name; + gpd.in.offered = 4; + gpd.out.needed = &needed; + gpd.out.type = &type; + gpd.out.data = data; + + status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &gpd); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed."); + torture_assert_werr_equal(tctx, gpd.out.result, expected_werr, + "GetPrinterData did not return expected error value."); + + if (W_ERROR_IS_OK(expected_werr)) { + uint32_t value = IVAL(data, 0); + torture_assert_int_equal(tctx, value, + expected_value, + talloc_asprintf(tctx, "GetPrinterData for %s did not return expected value.", value_name)); + } + return true; +} + +static bool test_EnumPrinters(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct test_spoolss_win_context *ctx, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_EnumPrinters ep; + DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size); + uint32_t needed; + uint32_t count; + union spoolss_PrinterInfo *info; + struct dcerpc_binding_handle *b = p->binding_handle; + + ep.in.flags = PRINTER_ENUM_NAME; + ep.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + ep.in.level = 2; + ep.in.buffer = &blob; + ep.in.offered = initial_blob_size; + ep.out.needed = &needed; + ep.out.count = &count; + ep.out.info = &info; + + status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &ep); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed."); + + if (W_ERROR_EQUAL(ep.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(ctx, needed); + ep.in.buffer = &blob; + ep.in.offered = needed; + status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &ep); + torture_assert_ntstatus_ok(tctx, status,"EnumPrinters failed."); + } + + torture_assert_werr_ok(tctx, ep.out.result, "EnumPrinters failed."); + + ctx->printer_count = count; + ctx->printer_info = info; + + torture_comment(tctx, "Found %d printer(s).\n", ctx->printer_count); + + return true; +} + +static bool test_GetPrinter(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + struct test_spoolss_win_context *ctx, + uint32_t level, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_GetPrinter gp; + DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size); + uint32_t needed; + + torture_comment(tctx, "Test GetPrinter level %d\n", level); + + gp.in.handle = handle; + gp.in.level = level; + gp.in.buffer = (initial_blob_size == 0)?NULL:&blob; + gp.in.offered = initial_blob_size; + gp.out.needed = &needed; + + status = dcerpc_spoolss_GetPrinter_r(b, tctx, &gp); + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + + if (W_ERROR_EQUAL(gp.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(ctx, needed); + gp.in.buffer = &blob; + gp.in.offered = needed; + status = dcerpc_spoolss_GetPrinter_r(b, tctx, &gp); + torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed"); + } + + torture_assert_werr_ok(tctx, gp.out.result, "GetPrinter failed"); + + ctx->current_info = gp.out.info; + + if (level == 2 && gp.out.info) { + ctx->printer_has_driver = gp.out.info->info2.drivername && + strlen(gp.out.info->info2.drivername); + } + + return true; +} + +static bool test_EnumJobs(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_EnumJobs ej; + DATA_BLOB blob = data_blob_talloc_zero(tctx, 1024); + uint32_t needed; + uint32_t count; + union spoolss_JobInfo *info; + + torture_comment(tctx, "Test EnumJobs\n"); + + ZERO_STRUCT(ej); + ej.in.handle = handle; + ej.in.firstjob = 0; + ej.in.numjobs = 0; + ej.in.level = 2; + ej.in.buffer = &blob; + ej.in.offered = 1024; + ej.out.needed = &needed; + ej.out.count = &count; + ej.out.info = &info; + + status = dcerpc_spoolss_EnumJobs_r(b, tctx, &ej); + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + if (W_ERROR_EQUAL(ej.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, needed); + ej.in.offered = needed; + ej.in.buffer = &blob; + status = dcerpc_spoolss_EnumJobs_r(b, tctx, &ej); + torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed"); + } + torture_assert_werr_ok(tctx, ej.out.result, "EnumJobs failed"); + + return true; +} + +static bool test_GetPrinterDriver2(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct test_spoolss_win_context *ctx, + struct policy_handle *handle) +{ + NTSTATUS status; + struct spoolss_GetPrinterDriver2 gpd2; + DATA_BLOB blob = data_blob_talloc_zero(tctx, 87424); + uint32_t needed; + uint32_t server_major_version; + uint32_t server_minor_version; + + torture_comment(tctx, "Testing GetPrinterDriver2\n"); + + gpd2.in.handle = handle; + gpd2.in.architecture = "Windows NT x86"; + gpd2.in.level = 101; + gpd2.in.buffer = &blob; + gpd2.in.offered = 87424; + gpd2.in.client_major_version = 3; + gpd2.in.client_minor_version = 0; + gpd2.out.needed = &needed; + gpd2.out.server_major_version = &server_major_version; + gpd2.out.server_minor_version = &server_minor_version; + + status = dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &gpd2); + torture_assert_ntstatus_ok(tctx, status, "GetPrinterDriver2 failed"); + + if (ctx->printer_has_driver) { + torture_assert_werr_ok(tctx, gpd2.out.result, + "GetPrinterDriver2 failed."); + } + + return true; +} + +static bool test_EnumForms(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + uint32_t initial_blob_size) +{ + NTSTATUS status; + struct spoolss_EnumForms ef; + DATA_BLOB blob = data_blob_talloc_zero(tctx, initial_blob_size); + uint32_t needed; + uint32_t count; + union spoolss_FormInfo *info; + + torture_comment(tctx, "Testing EnumForms\n"); + + ef.in.handle = handle; + ef.in.level = 1; + ef.in.buffer = (initial_blob_size == 0)?NULL:&blob; + ef.in.offered = initial_blob_size; + ef.out.needed = &needed; + ef.out.count = &count; + ef.out.info = &info; + + status = dcerpc_spoolss_EnumForms_r(b, tctx, &ef); + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + + if (W_ERROR_EQUAL(ef.out.result, WERR_INSUFFICIENT_BUFFER)) { + blob = data_blob_talloc_zero(tctx, needed); + ef.in.buffer = &blob; + ef.in.offered = needed; + status = dcerpc_spoolss_EnumForms_r(b, tctx, &ef); + torture_assert_ntstatus_ok(tctx, status, "EnumForms failed"); + } + + torture_assert_werr_ok(tctx, ef.out.result, "EnumForms failed"); + + return true; +} + +static bool test_EnumPrinterKey(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char* key, + struct test_spoolss_win_context *ctx) +{ + NTSTATUS status; + struct spoolss_EnumPrinterKey epk; + uint32_t needed = 0; + union spoolss_KeyNames key_buffer; + uint32_t _ndr_size; + + torture_comment(tctx, "Testing EnumPrinterKey(%s)\n", key); + + epk.in.handle = handle; + epk.in.key_name = talloc_strdup(tctx, key); + epk.in.offered = 0; + epk.out.needed = &needed; + epk.out.key_buffer = &key_buffer; + epk.out._ndr_size = &_ndr_size; + + status = dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &epk); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterKey failed"); + + if (W_ERROR_EQUAL(epk.out.result, WERR_MORE_DATA)) { + epk.in.offered = needed; + status = dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &epk); + torture_assert_ntstatus_ok(tctx, status, + "EnumPrinterKey failed"); + } + + torture_assert_werr_ok(tctx, epk.out.result, "EnumPrinterKey failed"); + + ctx->printer_keys = key_buffer.string_array; + + return true; +} + +static bool test_EnumPrinterDataEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key, + uint32_t initial_blob_size, + WERROR expected_error) +{ + NTSTATUS status; + struct spoolss_EnumPrinterDataEx epde; + struct spoolss_PrinterEnumValues *info; + uint32_t needed; + uint32_t count; + + torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key); + + epde.in.handle = handle; + epde.in.key_name = talloc_strdup(tctx, key); + epde.in.offered = 0; + epde.out.needed = &needed; + epde.out.count = &count; + epde.out.info = &info; + + status = dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &epde); + torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed."); + if (W_ERROR_EQUAL(epde.out.result, WERR_MORE_DATA)) { + epde.in.offered = needed; + status = dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &epde); + torture_assert_ntstatus_ok(tctx, status, + "EnumPrinterDataEx failed."); + } + + torture_assert_werr_equal(tctx, epde.out.result, expected_error, + "EnumPrinterDataEx failed."); + + return true; +} + +static bool test_WinXP(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + bool ret = true; + struct test_spoolss_win_context *ctx, *tmp_ctx; + struct policy_handle handle01, handle02, handle03, handle04; + /* Sometimes a handle stays unused. In order to make this clear in the + * code, the unused_handle structures are used for that. */ + struct policy_handle unused_handle1, unused_handle2; + char *server_name; + uint32_t i; + struct dcerpc_binding_handle *b = p->binding_handle; + + ctx = talloc_zero(tctx, struct test_spoolss_win_context); + tmp_ctx = talloc_zero(tctx, struct test_spoolss_win_context); + + ret &= test_OpenPrinterSequence(tctx, p, &handle01); + ret &= test_GetPrinterData(tctx, b, &handle01,"UISingleJobStatusString", + WERR_INVALID_PARAMETER, 0); + torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n"); + + server_name = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p)); + ret &= test_OpenPrinterEx(tctx, b, &unused_handle1, server_name, 0); + + ret &= test_EnumPrinters(tctx, p, ctx, 1024); + + ret &= test_OpenPrinterEx(tctx, b, &handle02, server_name, 0); + ret &= test_GetPrinterData(tctx, b, &handle02, "MajorVersion", WERR_OK, + 3); + ret &= test_ClosePrinter(tctx, b, &handle02); + + /* If no printers were found, skip all tests that need a printer */ + if (ctx->printer_count == 0) { + goto end_testWinXP; + } + + ret &= test_OpenPrinterEx(tctx, b, &handle02, + ctx->printer_info[0].info2.printername, + PRINTER_ACCESS_USE); + ret &= test_GetPrinter(tctx, b, &handle02, ctx, 2, 0); + + torture_assert_str_equal(tctx, ctx->current_info->info2.printername, + ctx->printer_info[0].info2.printername, + "GetPrinter returned unexpected printername"); + /*FIXME: Test more components of the PrinterInfo2 struct */ + + ret &= test_OpenPrinterEx(tctx, b, &handle03, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, b, &handle03, ctx, 0, 1164); + ret &= test_GetPrinter(tctx, b, &handle03, ctx, 2, 0); + + ret &= test_OpenPrinterEx(tctx, b, &handle04, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 0); + ret &= test_ClosePrinter(tctx, b, &handle04); + + ret &= test_OpenPrinterEx(tctx, b, &handle04, + ctx->printer_info[0].info2.printername, 0); + ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 4096); + ret &= test_ClosePrinter(tctx, b, &handle04); + + ret &= test_OpenPrinterAsAdmin(tctx, b, + ctx->printer_info[0].info2.printername); + + ret &= test_OpenPrinterEx(tctx, b, &handle04, + ctx->printer_info[0].info2.printername, PRINTER_READ); + ret &= test_GetPrinterData(tctx, b, &handle04,"UISingleJobStatusString", + WERR_FILE_NOT_FOUND, 0); + torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n"); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + + ret &= test_EnumJobs(tctx, b, &handle04); + ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 4096); + + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + ret &= test_ClosePrinter(tctx, b, &handle04); + + ret &= test_EnumPrinters(tctx, p, ctx, 1556); + ret &= test_GetPrinterDriver2(tctx, b, ctx, &handle03); + ret &= test_EnumForms(tctx, b, &handle03, 0); + + ret &= test_EnumPrinterKey(tctx, b, &handle03, "", ctx); + + for (i=0; ctx->printer_keys && ctx->printer_keys[i] != NULL; i++) { + + ret &= test_EnumPrinterKey(tctx, b, &handle03, + ctx->printer_keys[i], + tmp_ctx); + ret &= test_EnumPrinterDataEx(tctx, b, &handle03, + ctx->printer_keys[i], 0, + WERR_OK); + } + + ret &= test_EnumPrinterDataEx(tctx, b, &handle03, "", 0, + WERR_INVALID_PARAMETER); + + ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 2, 0); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + + ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 2, 2556); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + + ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 7, 0); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + + ret &= test_ClosePrinter(tctx, b, &handle03); + + ret &= test_OpenPrinterEx(tctx, b, &unused_handle2, + ctx->printer_info[0].info2.printername, 0); + ret &= test_ClosePrinter(tctx, b, &unused_handle2); + + ret &= test_OpenPrinterEx(tctx, b, &handle03, server_name, 0); + ret &= test_GetPrinterData(tctx, b, &handle03, "W3SvcInstalled", + WERR_OK, 0); + ret &= test_ClosePrinter(tctx, b, &handle03); + + ret &= test_ClosePrinter(tctx, b, &unused_handle1); + ret &= test_ClosePrinter(tctx, b, &handle02); + + ret &= test_OpenPrinterEx(tctx, b, &handle02, + ctx->printer_info[0].info2.sharename, 0); + ret &= test_GetPrinter(tctx, b, &handle02, tmp_ctx, 2, 0); + ret &= test_ClosePrinter(tctx, b, &handle02); + +end_testWinXP: + ret &= test_ClosePrinter(tctx, b, &handle01); + + talloc_free(tmp_ctx); + talloc_free(ctx); + return ret; +} + +struct torture_suite *torture_rpc_spoolss_win(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.win"); + + struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, + "win", &ndr_table_spoolss); + + torture_rpc_tcase_add_test(tcase, "testWinXP", test_WinXP); + + return suite; +} + diff --git a/source4/torture/rpc/srvsvc.c b/source4/torture/rpc/srvsvc.c new file mode 100644 index 0000000..c777f36 --- /dev/null +++ b/source4/torture/rpc/srvsvc.c @@ -0,0 +1,1206 @@ +/* + Unix SMB/CIFS implementation. + test suite for srvsvc rpc operations + + Copyright (C) Stefan (metze) Metzmacher 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "torture/rpc/torture_rpc.h" + +/**************************/ +/* srvsvc_NetCharDev */ +/**************************/ +static bool test_NetCharDevGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *devname) +{ + NTSTATUS status; + struct srvsvc_NetCharDevGetInfo r; + union srvsvc_NetCharDevInfo info; + uint32_t levels[] = {0, 1}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.device_name = devname; + r.out.info = &info; + + for (i=0;ibinding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.device_name = devname; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.info_ctr = &info_ctr; + r.out.totalentries = &totalentries; + + for (i=0;ictr.ctr1->count;j++) { + const char *device; + device = r.out.info_ctr->ctr.ctr1->array[j].device; + if (!test_NetCharDevGetInfo(p, tctx, device)) { + return false; + } + if (!test_NetCharDevControl(p, tctx, device)) { + return false; + } + } + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetCharDevQ */ +/**************************/ +static bool test_NetCharDevQGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, + const char *devicequeue) +{ + NTSTATUS status; + struct srvsvc_NetCharDevQGetInfo r; + union srvsvc_NetCharDevQInfo info; + uint32_t levels[] = {0, 1}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.queue_name = devicequeue; + r.in.user = talloc_asprintf(tctx,"Administrator"); + r.out.info = &info; + + for (i=0;ibinding_handle; + + r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p)); + r.in.queue_name = devicequeue; + + for (i=0;idevice = r.in.queue_name; + break; + case 1: + r.in.info.info1 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo1); + r.in.info.info1->device = r.in.queue_name; + r.in.info.info1->priority = 0x000; + r.in.info.info1->devices = r.in.queue_name; + r.in.info.info1->users = 0x000; + r.in.info.info1->num_ahead = 0x000; + break; + default: + break; + } + r.in.parm_error = &parm_error; + status = dcerpc_srvsvc_NetCharDevQSetInfo_r(b, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n", + r.in.level, r.in.queue_name, nt_errstr(status)); + ret = false; + continue; + } + if (!W_ERROR_IS_OK(r.out.result)) { + d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n", + r.in.level, r.in.queue_name, win_errstr(r.out.result)); + continue; + } + } + + return ret; +} +#endif + +static bool test_NetCharDevQEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetCharDevQEnum r; + struct srvsvc_NetCharDevQInfoCtr info_ctr; + struct srvsvc_NetCharDevQCtr0 c0; + struct srvsvc_NetCharDevQCtr1 c1; + uint32_t totalentries = 0; + uint32_t levels[] = {0, 1}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.user = talloc_asprintf(tctx,"%s","Administrator"); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ictr.ctr1->count;j++) { + const char *device; + device = r.out.info_ctr->ctr.ctr1->array[j].device; + if (!test_NetCharDevQGetInfo(p, tctx, device)) { + return false; + } + } + } + } + + return true; +} + +/**************************/ +/* srvsvc_NetConn */ +/**************************/ +static bool test_NetConnEnum(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetConnEnum r; + struct srvsvc_NetConnInfoCtr info_ctr; + struct srvsvc_NetConnCtr0 c0; + struct srvsvc_NetConnCtr1 c1; + uint32_t totalentries = 0; + uint32_t levels[] = {0, 1}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.path = talloc_asprintf(tctx,"%s","IPC$"); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.path = NULL; + r.in.user = NULL; + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)4096; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.client = NULL; + r.in.user = NULL; + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ibinding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.device_name = device_name; + r.out.type = &type; + + torture_comment(tctx, + "Testing NetShareCheck on device '%s'\n", r.in.device_name); + + status = dcerpc_srvsvc_NetShareCheck_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "dcerpc_srvsvc_NetShareCheck failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetShareCheck failed"); + + return true; +} + +static bool test_NetShareGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *sharename, bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareGetInfo r; + union srvsvc_NetShareInfo info; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_OK, WERR_OK }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + { 1005, WERR_OK, WERR_OK }, + }; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.share_name = sharename; + r.out.info = &info; + + for (i=0;iinfo2 || !r.out.info->info2->path) continue; + if (!test_NetShareCheck(p, tctx, r.out.info->info2->path)) { + return false; + } + } + + return true; +} + +static bool test_NetShareGetInfoAdminFull(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareGetInfo(tctx, p, "IPC$", true); +} + +static bool test_NetShareGetInfoAdminAnon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareGetInfo(tctx, p, "IPC$", false); +} + +static bool test_NetShareAddSetDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetShareAdd a; + struct srvsvc_NetShareSetInfo r; + struct srvsvc_NetShareGetInfo q; + struct srvsvc_NetShareDel d; + struct sec_desc_buf sd_buf; + union srvsvc_NetShareInfo info; + struct { + uint32_t level; + WERROR expected; + } levels[] = { + { 0, WERR_INVALID_LEVEL }, + { 1, WERR_OK }, + { 2, WERR_OK }, + { 501, WERR_INVALID_LEVEL }, + { 502, WERR_OK }, + { 1004, WERR_OK }, + { 1005, WERR_OK }, + { 1006, WERR_OK }, +/* { 1007, WERR_OK }, */ + { 1501, WERR_OK }, + }; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + a.in.server_unc = r.in.server_unc = q.in.server_unc = d.in.server_unc = + talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.share_name = talloc_strdup(tctx, "testshare"); + + info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2); + info.info2->name = r.in.share_name; + info.info2->type = STYPE_DISKTREE; + info.info2->comment = talloc_strdup(tctx, "test comment"); + info.info2->permissions = 123434566; + info.info2->max_users = -1; + info.info2->current_users = 0; + info.info2->path = talloc_strdup(tctx, "C:\\"); + info.info2->password = NULL; + + a.in.info = &info; + a.in.level = 2; + a.in.parm_error = NULL; + + status = dcerpc_srvsvc_NetShareAdd_r(b, tctx, &a); + torture_assert_ntstatus_ok(tctx, status, "NetShareAdd level 2 on share 'testshare' failed"); + torture_assert_werr_ok(tctx, a.out.result, "NetShareAdd level 2 on share 'testshare' failed"); + + r.in.parm_error = NULL; + + q.in.level = 502; + + for (i = 0; i < ARRAY_SIZE(levels); i++) { + + r.in.level = levels[i].level; + ZERO_STRUCT(r.out); + + torture_comment(tctx, "Testing NetShareSetInfo level %u on share '%s'\n", + r.in.level, r.in.share_name); + + switch (levels[i].level) { + case 0: + info.info0 = talloc(tctx, struct srvsvc_NetShareInfo0); + info.info0->name = r.in.share_name; + break; + case 1: + info.info1 = talloc(tctx, struct srvsvc_NetShareInfo1); + info.info1->name = r.in.share_name; + info.info1->type = STYPE_DISKTREE; + info.info1->comment = talloc_strdup(tctx, "test comment 1"); + break; + case 2: + info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2); + info.info2->name = r.in.share_name; + info.info2->type = STYPE_DISKTREE; + info.info2->comment = talloc_strdup(tctx, "test comment 2"); + info.info2->permissions = 0; + info.info2->max_users = 2; + info.info2->current_users = 1; + info.info2->path = talloc_strdup(tctx, "::BLaH::"); /* "C:\\"); */ + info.info2->password = NULL; + break; + case 501: + info.info501 = talloc(tctx, struct srvsvc_NetShareInfo501); + info.info501->name = r.in.share_name; + info.info501->type = STYPE_DISKTREE; + info.info501->comment = talloc_strdup(tctx, "test comment 501"); + info.info501->csc_policy = 0; + break; + case 502: + ZERO_STRUCT(sd_buf); + info.info502 = talloc(tctx, struct srvsvc_NetShareInfo502); + info.info502->name = r.in.share_name; + info.info502->type = STYPE_DISKTREE; + info.info502->comment = talloc_strdup(tctx, "test comment 502"); + info.info502->permissions = 0; + info.info502->max_users = 502; + info.info502->current_users = 1; + info.info502->path = talloc_strdup(tctx, "C:\\"); + info.info502->password = NULL; + info.info502->sd_buf = sd_buf; + break; + case 1004: + info.info1004 = talloc(tctx, struct srvsvc_NetShareInfo1004); + info.info1004->comment = talloc_strdup(tctx, "test comment 1004"); + break; + case 1005: + info.info1005 = talloc(tctx, struct srvsvc_NetShareInfo1005); + info.info1005->dfs_flags = 0; + break; + case 1006: + info.info1006 = talloc(tctx, struct srvsvc_NetShareInfo1006); + info.info1006->max_users = 1006; + break; +/* case 1007: + info.info1007 = talloc(tctx, struct srvsvc_NetShareInfo1007); + info.info1007->flags = 0; + info.info1007->alternate_directory_name = talloc_strdup(tctx, "test"); + break; +*/ + case 1501: + info.info1501 = talloc_zero(tctx, struct sec_desc_buf); + break; + } + + r.in.info = &info; + + status = dcerpc_srvsvc_NetShareSetInfo_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, levels[i].expected, "NetShareSetInfo failed"); + + q.in.share_name = r.in.share_name; + q.out.info = &info; + + status = dcerpc_srvsvc_NetShareGetInfo_r(b, tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed"); + torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed"); + + torture_assert_str_equal(tctx, q.out.info->info502->name, r.in.share_name, + "share name invalid"); + + switch (levels[i].level) { + case 0: + break; + case 1: + torture_assert_str_equal(tctx, q.out.info->info502->comment, "test comment 1", "comment"); + break; + case 2: + torture_assert_str_equal(tctx, q.out.info->info2->comment, "test comment 2", "comment"); + torture_assert_int_equal(tctx, q.out.info->info2->max_users, 2, "max users"); + torture_assert_str_equal(tctx, q.out.info->info2->path, "C:\\", "path"); + break; + case 501: + torture_assert_str_equal(tctx, q.out.info->info501->comment, "test comment 501", "comment"); + break; + case 502: + torture_assert_str_equal(tctx, q.out.info->info502->comment, "test comment 502", "comment"); + torture_assert_int_equal(tctx, q.out.info->info502->max_users, 502, "max users"); + torture_assert_str_equal(tctx, q.out.info->info502->path, "C:\\", "path"); + break; + case 1004: + torture_assert_str_equal(tctx, q.out.info->info1004->comment, "test comment 1004", + "comment"); + break; + case 1005: + break; + case 1006: + torture_assert_int_equal(tctx, q.out.info->info1006->max_users, 1006, "Max users"); + break; +/* case 1007: + break; +*/ + case 1501: + break; + } + } + + d.in.share_name = r.in.share_name; + d.in.reserved = 0; + + status = dcerpc_srvsvc_NetShareDel_r(b, tctx, &d); + torture_assert_ntstatus_ok(tctx, status, "NetShareDel on share 'testshare502' failed"); + torture_assert_werr_ok(tctx, a.out.result, "NetShareDel on share 'testshare502' failed"); + + return true; +} + +/**************************/ +/* srvsvc_NetShare */ +/**************************/ +static bool test_NetShareEnumAll(struct torture_context *tctx, + struct dcerpc_pipe *p, + bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareEnumAll r; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr0 c0; + struct srvsvc_NetShareCtr1 c1; + struct srvsvc_NetShareCtr2 c2; + struct srvsvc_NetShareCtr501 c501; + struct srvsvc_NetShareCtr502 c502; + uint32_t totalentries = 0; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_ACCESS_DENIED, WERR_OK }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + }; + int i; + uint32_t resume_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info_ctr); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.resume_handle = &resume_handle; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ictr.ctr2) { + for (j=0;jctr.ctr2->count;j++) { + const char *name; + name = r.out.info_ctr->ctr.ctr2->array[j].name; + if (!test_NetShareGetInfo(tctx, p, name, admin)) { + return false; + } + } + } + } + + return true; +} + +static bool test_NetShareEnumAllFull(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnumAll(tctx, p, true); +} + +static bool test_NetShareEnumAllAnon(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_NetShareEnumAll(tctx, p, false); +} + +static bool test_NetShareEnum(struct torture_context *tctx, + struct dcerpc_pipe *p, bool admin) +{ + NTSTATUS status; + struct srvsvc_NetShareEnum r; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr0 c0; + struct srvsvc_NetShareCtr1 c1; + struct srvsvc_NetShareCtr2 c2; + struct srvsvc_NetShareCtr501 c501; + struct srvsvc_NetShareCtr502 c502; + uint32_t totalentries = 0; + struct { + uint32_t level; + WERROR anon_status; + WERROR admin_status; + } levels[] = { + { 0, WERR_OK, WERR_OK }, + { 1, WERR_OK, WERR_OK }, + { 2, WERR_ACCESS_DENIED, WERR_OK }, + { 501, WERR_INVALID_LEVEL, WERR_INVALID_LEVEL }, + { 502, WERR_ACCESS_DENIED, WERR_OK }, + }; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.in.info_ctr = &info_ctr; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + + for (i=0;ibinding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + + for (i=0;ibinding_handle; + + ZERO_STRUCT(info); + ZERO_STRUCT(r); + + r.in.server_unc = NULL; + r.in.resume_handle = &resume_handle; + r.in.info = &info; + r.out.info = &info; + r.out.totalentries = &totalentries; + r.out.resume_handle = &resume_handle; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(transports); + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s", dcerpc_server_name(p)); + r.in.transports = &transports; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = NULL; + r.out.totalentries = &totalentries; + r.out.transports = &transports; + + for (i=0;ibinding_handle; + + r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p)); + r.out.info = &info; + + torture_comment(tctx, "Testing NetRemoteTOD\n"); + status = dcerpc_srvsvc_NetRemoteTOD_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetRemoteTOD failed"); + torture_assert_werr_ok(tctx, r.out.result, "NetRemoteTOD failed"); + + return true; +} + +/**************************/ +/* srvsvc_NetName */ +/**************************/ + +static bool test_NetNameValidate(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct srvsvc_NetNameValidate r; + char *invalidc; + char *name; + int i, n, min, max; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.flags = 0x0; + + d_printf("Testing NetNameValidate\n"); + + /* valid path types only between 1 and 13 */ + for (i = 1; i < 14; i++) { + +again: + /* let's limit ourselves to a maximum of 4096 bytes */ + r.in.name = name = talloc_array(tctx, char, 4097); + max = 4096; + min = 0; + n = max; + + while (1) { + + /* Find maximum length accepted by this type */ + ZERO_STRUCT(r.out); + r.in.name_type = i; + memset(name, 'A', n); + name[n] = '\0'; + + status = dcerpc_srvsvc_NetNameValidate_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetNameValidate failed while checking maximum size (%s)\n", + nt_errstr(status)); + break; + } + + if (W_ERROR_IS_OK(r.out.result)) { + min = n; + n += (max - min + 1)/2; + if (n == min) { + /* + * We did not move, so + * do not loop forever + */ + break; + } + continue; + + } else { + if ((min + 1) >= max) break; /* found it */ + + max = n; + n -= (max - min)/2; + continue; + } + } + + talloc_free(name); + + d_printf("Maximum length for type %2d, flags %08x: %d\n", i, r.in.flags, max); + + /* find invalid chars for this type check only ASCII between 0x20 and 0x7e */ + + invalidc = talloc_strdup(tctx, ""); + + for (n = 0x20; n < 0x7e; n++) { + r.in.name = name = talloc_asprintf(tctx, "%c", (char)n); + + status = dcerpc_srvsvc_NetNameValidate_r(b, tctx, &r); + if (!NT_STATUS_IS_OK(status)) { + d_printf("NetNameValidate failed while checking valid chars (%s)\n", + nt_errstr(status)); + break; + } + + if (!W_ERROR_IS_OK(r.out.result)) { + invalidc = talloc_asprintf_append_buffer(invalidc, "%c", (char)n); + } + + talloc_free(name); + } + + d_printf(" Invalid chars for type %2d, flags %08x: \"%s\"\n", i, r.in.flags, invalidc); + + /* only two values are accepted for flags: 0x0 and 0x80000000 */ + if (r.in.flags == 0x0) { + r.in.flags = 0x80000000; + goto again; + } + + r.in.flags = 0x0; + } + + return true; +} + +struct torture_suite *torture_rpc_srvsvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "srvsvc"); + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "srvsvc (admin access)", &ndr_table_srvsvc); + + torture_rpc_tcase_add_test(tcase, "NetCharDevEnum", test_NetCharDevEnum); + torture_rpc_tcase_add_test(tcase, "NetCharDevQEnum", test_NetCharDevQEnum); + torture_rpc_tcase_add_test(tcase, "NetConnEnum", test_NetConnEnum); + torture_rpc_tcase_add_test(tcase, "NetFileEnum", test_NetFileEnum); + torture_rpc_tcase_add_test(tcase, "NetSessEnum", test_NetSessEnum); + torture_rpc_tcase_add_test(tcase, "NetShareEnumAll", test_NetShareEnumAllFull); + torture_rpc_tcase_add_test(tcase, "NetSrvGetInfo", test_NetSrvGetInfo); + torture_rpc_tcase_add_test(tcase, "NetDiskEnum", test_NetDiskEnum); + torture_rpc_tcase_add_test(tcase, "NetTransportEnum", test_NetTransportEnum); + torture_rpc_tcase_add_test(tcase, "NetRemoteTOD", test_NetRemoteTOD); + torture_rpc_tcase_add_test(tcase, "NetShareEnum", test_NetShareEnumFull); + torture_rpc_tcase_add_test(tcase, "NetShareGetInfo", test_NetShareGetInfoAdminFull); + test = torture_rpc_tcase_add_test(tcase, "NetShareAddSetDel", + test_NetShareAddSetDel); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetNameValidate", test_NetNameValidate); + + tcase = torture_suite_add_anon_rpc_iface_tcase(suite, + "srvsvc anonymous access", + &ndr_table_srvsvc); + + torture_rpc_tcase_add_test(tcase, "NetShareEnumAll", + test_NetShareEnumAllAnon); + torture_rpc_tcase_add_test(tcase, "NetShareEnum", + test_NetShareEnumAnon); + torture_rpc_tcase_add_test(tcase, "NetShareGetInfo", + test_NetShareGetInfoAdminAnon); + + return suite; +} diff --git a/source4/torture/rpc/svcctl.c b/source4/torture/rpc/svcctl.c new file mode 100644 index 0000000..2962f15 --- /dev/null +++ b/source4/torture/rpc/svcctl.c @@ -0,0 +1,828 @@ +/* + Unix SMB/CIFS implementation. + test suite for svcctl rpc operations + + Copyright (C) Jelmer Vernooij 2004 + Copyright (C) Guenther Deschner 2008,2009,2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_svcctl_c.h" +#include "librpc/gen_ndr/ndr_svcctl.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" + +#define TORTURE_DEFAULT_SERVICE "Spooler" + +static bool test_OpenSCManager(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *h) +{ + struct svcctl_OpenSCManagerW r; + + r.in.MachineName = NULL; + r.in.DatabaseName = NULL; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = h; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_OpenSCManagerW_r(b, tctx, &r), + "OpenSCManager failed!"); + + return true; +} + +static bool test_CloseServiceHandle(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *h) +{ + struct svcctl_CloseServiceHandle r; + + r.in.handle = h; + r.out.handle = h; + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_CloseServiceHandle_r(b, tctx, &r), + "CloseServiceHandle failed"); + + return true; +} + +static bool test_OpenService(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *h, + const char *name, + struct policy_handle *s) +{ + struct svcctl_OpenServiceW r; + + r.in.scmanager_handle = h; + r.in.ServiceName = name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = s; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_OpenServiceW_r(b, tctx, &r), + "OpenServiceW failed!"); + torture_assert_werr_ok(tctx, r.out.result, "OpenServiceW failed!"); + + return true; + +} + +static bool test_QueryServiceStatus(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceStatus r; + struct policy_handle h, s; + struct SERVICE_STATUS service_status; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.handle = &s; + r.out.service_status = &service_status; + + status = dcerpc_svcctl_QueryServiceStatus_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatus failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatus failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceStatusEx(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceStatusEx r; + struct policy_handle h, s; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t info_level = SVC_STATUS_PROCESS_INFO; + uint8_t *buffer; + uint32_t offered = 0; + uint32_t needed = 0; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + buffer = talloc(tctx, uint8_t); + + r.in.handle = &s; + r.in.info_level = info_level; + r.in.offered = offered; + r.out.buffer = buffer; + r.out.needed = &needed; + + status = dcerpc_svcctl_QueryServiceStatusEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = needed; + buffer = talloc_array(tctx, uint8_t, needed); + r.out.buffer = buffer; + + status = dcerpc_svcctl_QueryServiceStatusEx_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatusEx failed!"); + } + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceConfigW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceConfigW r; + struct QUERY_SERVICE_CONFIG query; + struct policy_handle h, s; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t offered = 0; + uint32_t needed = 0; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.handle = &s; + r.in.offered = offered; + r.out.query = &query; + r.out.needed = &needed; + + status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = needed; + status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!"); + } + + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfigW failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceConfig2W(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceConfig2W r; + struct policy_handle h, s; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint32_t info_level = SERVICE_CONFIG_DESCRIPTION; + uint8_t *buffer; + uint32_t offered = 0; + uint32_t needed = 0; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + buffer = talloc(tctx, uint8_t); + + r.in.handle = &s; + r.in.info_level = info_level; + r.in.offered = offered; + r.out.buffer = buffer; + r.out.needed = &needed; + + status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = needed; + buffer = talloc_array(tctx, uint8_t, needed); + r.out.buffer = buffer; + + status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!"); + } + + r.in.info_level = SERVICE_CONFIG_FAILURE_ACTIONS; + r.in.offered = offered; + r.out.buffer = buffer; + r.out.needed = &needed; + + status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = needed; + buffer = talloc_array(tctx, uint8_t, needed); + r.out.buffer = buffer; + + status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!"); + } + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceConfigEx(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceConfigEx r; + struct policy_handle h, s; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + struct SC_RPC_CONFIG_INFOW info; + int i; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + for (i=0; i < 16; i++) { + + r.in.hService = s; + r.in.dwInfoLevel = i; + r.out.pInfo = &info; + + status = dcerpc_svcctl_QueryServiceConfigEx_r(b, tctx, &r); + if (i == 8) { + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigEx failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfigEx failed!"); + } else { + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE, "QueryServiceConfigEx failed!"); + } + } + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceObjectSecurity(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceObjectSecurity r; + struct policy_handle h, s; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint8_t *buffer = NULL; + uint32_t needed; + + enum ndr_err_code ndr_err; + struct security_descriptor sd; + DATA_BLOB blob; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.handle = &s; + r.in.security_flags = 0; + r.in.offered = 0; + r.out.buffer = NULL; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r), + "QueryServiceObjectSecurity failed!"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "QueryServiceObjectSecurity failed!"); + + r.in.security_flags = SECINFO_DACL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r), + "QueryServiceObjectSecurity failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.offered = needed; + buffer = talloc_array(tctx, uint8_t, needed); + r.out.buffer = buffer; + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r), + "QueryServiceObjectSecurity failed!"); + } + + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceObjectSecurity failed!"); + + blob = data_blob_const(buffer, needed); + + ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + + if (DEBUGLEVEL >= 1) { + NDR_PRINT_DEBUG(security_descriptor, &sd); + } + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_SetServiceObjectSecurity(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceObjectSecurity q; + struct svcctl_SetServiceObjectSecurity r; + struct policy_handle h, s; + struct dcerpc_binding_handle *b = p->binding_handle; + + uint8_t *buffer; + uint32_t needed; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + q.in.handle = &s; + q.in.security_flags = SECINFO_DACL; + q.in.offered = 0; + q.out.buffer = NULL; + q.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &q), + "QueryServiceObjectSecurity failed!"); + + if (W_ERROR_EQUAL(q.out.result, WERR_INSUFFICIENT_BUFFER)) { + q.in.offered = needed; + buffer = talloc_array(tctx, uint8_t, needed); + q.out.buffer = buffer; + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &q), + "QueryServiceObjectSecurity failed!"); + } + + torture_assert_werr_ok(tctx, q.out.result, + "QueryServiceObjectSecurity failed!"); + + r.in.handle = &s; + r.in.security_flags = SECINFO_DACL; + r.in.buffer = q.out.buffer; + r.in.offered = *q.out.needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_SetServiceObjectSecurity_r(b, tctx, &r), + "SetServiceObjectSecurity failed!"); + torture_assert_werr_ok(tctx, r.out.result, + "SetServiceObjectSecurity failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_StartServiceW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_StartServiceW r; + struct policy_handle h, s; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.handle = &s; + r.in.NumArgs = 0; + r.in.Arguments = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_StartServiceW_r(b, tctx, &r), + "StartServiceW failed!"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_SERVICE_ALREADY_RUNNING, + "StartServiceW failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_ControlService(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_ControlService r; + struct policy_handle h, s; + struct SERVICE_STATUS service_status; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.handle = &s; + r.in.control = 0; + r.out.service_status = &service_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_ControlService_r(b, tctx, &r), + "ControlService failed!"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "ControlService failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_ControlServiceExW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_ControlServiceExW r; + struct policy_handle h, s; + struct dcerpc_binding_handle *b = p->binding_handle; + union SC_RPC_SERVICE_CONTROL_IN_PARAMSW ControlInParams; + union SC_RPC_SERVICE_CONTROL_OUT_PARAMSW ControlOutParams; + struct SERVICE_CONTROL_STATUS_REASON_OUT_PARAMS psrOutParams; + struct SERVICE_CONTROL_STATUS_REASON_IN_PARAMSW psrInParams; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + ZERO_STRUCT(psrInParams); + ZERO_STRUCT(psrOutParams); + + psrInParams.dwReason = SERVICE_STOP_CUSTOM | + SERVICE_STOP_REASON_MAJOR_APPLICATION | + SERVICE_STOP_REASON_MINOR_ENVIRONMENT; + psrInParams.pszComment = "wurst"; + + ControlInParams.psrInParams = &psrInParams; + ControlOutParams.psrOutParams = &psrOutParams; + + r.in.hService = s; + r.in.dwControl = SVCCTL_CONTROL_STOP; + r.in.dwInfoLevel = 1; + r.in.pControlInParams = &ControlInParams; + r.out.pControlOutParams = &ControlOutParams; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_ControlServiceExW_r(b, tctx, &r), + "ControlServiceExW failed!"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "ControlServiceExW failed!"); + + if (!test_CloseServiceHandle(b, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_EnumServicesStatusW r; + struct policy_handle h; + int i; + NTSTATUS status; + uint32_t resume_handle = 0; + struct ENUM_SERVICE_STATUSW *service = NULL; + uint32_t needed = 0; + uint32_t services_returned = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + r.in.handle = &h; + r.in.type = SERVICE_TYPE_WIN32; + r.in.state = SERVICE_STATE_ALL; + r.in.offered = 0; + r.in.resume_handle = &resume_handle; + r.out.service = NULL; + r.out.resume_handle = &resume_handle; + r.out.services_returned = &services_returned; + r.out.needed = &needed; + + status = dcerpc_svcctl_EnumServicesStatusW_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + r.in.offered = needed; + r.out.service = talloc_array(tctx, uint8_t, needed); + + status = dcerpc_svcctl_EnumServicesStatusW_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); + torture_assert_werr_ok(tctx, r.out.result, "EnumServicesStatus failed"); + } + + if (services_returned > 0) { + + enum ndr_err_code ndr_err; + DATA_BLOB blob; + struct ndr_pull *ndr; + + blob.length = r.in.offered; + blob.data = talloc_steal(tctx, r.out.service); + + ndr = ndr_pull_init_blob(&blob, tctx); + + service = talloc_array(tctx, struct ENUM_SERVICE_STATUSW, services_returned); + if (!service) { + return false; + } + + ndr_err = ndr_pull_ENUM_SERVICE_STATUSW_array( + ndr, services_returned, service); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + } + + for(i = 0; i < services_returned; i++) { + + torture_assert(tctx, service[i].service_name, + "Service without name returned!"); + + printf("%-20s \"%s\", Type: %d, State: %d\n", + service[i].service_name, service[i].display_name, + service[i].status.type, service[i].status.state); + } + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_EnumDependentServicesW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_EnumDependentServicesW r; + struct policy_handle h, s; + uint32_t needed; + uint32_t services_returned; + uint32_t i; + uint32_t states[] = { SERVICE_STATE_ACTIVE, + SERVICE_STATE_INACTIVE, + SERVICE_STATE_ALL }; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s)) + return false; + + r.in.service = &s; + r.in.offered = 0; + r.in.state = 0; + r.out.service_status = NULL; + r.out.services_returned = &services_returned; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_EnumDependentServicesW_r(b, tctx, &r), + "EnumDependentServicesW failed!"); + + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "EnumDependentServicesW failed!"); + + for (i=0; ibinding_handle; + + if (!test_OpenSCManager(b, tctx, &h)) + return false; + + if (!test_CloseServiceHandle(b, tctx, &h)) + return false; + + return true; +} + +static bool test_ChangeServiceConfigW(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct svcctl_ChangeServiceConfigW r; + struct svcctl_QueryServiceConfigW q; + struct policy_handle h, s; + NTSTATUS status; + struct dcerpc_binding_handle *b = p->binding_handle; + struct QUERY_SERVICE_CONFIG query; + bool ok; + + uint32_t offered = 0; + uint32_t needed = 0; + + ok = test_OpenSCManager(b, tctx, &h); + if (!ok) { + return false; + } + + ok = test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s); + if (!ok) { + return false; + } + + q.in.handle = &s; + q.in.offered = offered; + q.out.query = &query; + q.out.needed = &needed; + + status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!"); + + if (W_ERROR_EQUAL(q.out.result, WERR_INSUFFICIENT_BUFFER)) { + q.in.offered = needed; + status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &q); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!"); + } + torture_assert_werr_ok(tctx, q.out.result, "QueryServiceConfigW failed!"); + + r.in.handle = &s; + r.in.type = query.service_type; + r.in.start_type = query.start_type; + r.in.error_control = query.error_control; + + /* + * according to MS-SCMR 3.1.4.11 NULL params are supposed to leave the + * existing values intact. + */ + + r.in.binary_path = NULL; + r.in.load_order_group = NULL; + r.in.dependencies = NULL; + r.in.dwDependSize = 0; + r.in.service_start_name = NULL; + r.in.password = NULL; + r.in.dwPwSize = 0; + r.in.display_name = NULL; + r.in.tag_id = NULL; + r.out.tag_id = NULL; + + status = dcerpc_svcctl_ChangeServiceConfigW_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "ChangeServiceConfigW failed!"); + torture_assert_werr_ok(tctx, r.out.result, "ChangeServiceConfigW failed!"); + + ok = test_CloseServiceHandle(b, tctx, &s); + if (!ok) { + return false; + } + + ok = test_CloseServiceHandle(b, tctx, &h); + if (!ok) { + return false; + } + + return true; +} + +struct torture_suite *torture_rpc_svcctl(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "svcctl"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "svcctl", &ndr_table_svcctl); + + torture_rpc_tcase_add_test(tcase, "SCManager", + test_SCManager); + torture_rpc_tcase_add_test(tcase, "EnumServicesStatus", + test_EnumServicesStatus); + torture_rpc_tcase_add_test(tcase, "EnumDependentServicesW", + test_EnumDependentServicesW); + torture_rpc_tcase_add_test(tcase, "QueryServiceStatus", + test_QueryServiceStatus); + torture_rpc_tcase_add_test(tcase, "QueryServiceStatusEx", + test_QueryServiceStatusEx); + torture_rpc_tcase_add_test(tcase, "QueryServiceConfigW", + test_QueryServiceConfigW); + torture_rpc_tcase_add_test(tcase, "QueryServiceConfig2W", + test_QueryServiceConfig2W); + torture_rpc_tcase_add_test(tcase, "QueryServiceConfigEx", + test_QueryServiceConfigEx); + torture_rpc_tcase_add_test(tcase, "QueryServiceObjectSecurity", + test_QueryServiceObjectSecurity); + torture_rpc_tcase_add_test(tcase, "SetServiceObjectSecurity", + test_SetServiceObjectSecurity); + torture_rpc_tcase_add_test(tcase, "StartServiceW", + test_StartServiceW); + torture_rpc_tcase_add_test(tcase, "ControlService", + test_ControlService); + torture_rpc_tcase_add_test(tcase, "ControlServiceExW", + test_ControlServiceExW); + torture_rpc_tcase_add_test(tcase, "ChangeServiceConfigW", + test_ChangeServiceConfigW); + + return suite; +} diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c new file mode 100644 index 0000000..6fb5f86 --- /dev/null +++ b/source4/torture/rpc/testjoin.c @@ -0,0 +1,915 @@ +/* + Unix SMB/CIFS implementation. + + utility code to join/leave a domain + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + this code is used by other torture modules to join/leave a domain + as either a member, bdc or thru a trust relationship +*/ + +#include "includes.h" +#include "system/time.h" +#include "libnet/libnet.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_samr_c.h" + +#include "libcli/auth/libcli_auth.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/security/security.h" +#include "param/param.h" +#include "source3/rpc_client/init_samr.h" + +struct test_join { + struct dcerpc_pipe *p; + struct policy_handle user_handle; + struct policy_handle domain_handle; + struct libnet_JoinDomain *libnet_r; + struct dom_sid *dom_sid; + const char *dom_netbios_name; + const char *dom_dns_name; + struct dom_sid *user_sid; + struct GUID user_guid; + const char *netbios_name; +}; + + +static NTSTATUS DeleteUser_byname(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *handle, const char *name) +{ + NTSTATUS status; + struct samr_DeleteUser d; + struct policy_handle user_handle; + uint32_t rid; + struct samr_LookupNames n; + struct samr_Ids rids, types; + struct lsa_String sname; + struct samr_OpenUser r; + + sname.string = name; + + n.in.domain_handle = handle; + n.in.num_names = 1; + n.in.names = &sname; + n.out.rids = &rids; + n.out.types = &types; + + status = dcerpc_samr_LookupNames_r(b, mem_ctx, &n); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (NT_STATUS_IS_OK(n.out.result)) { + rid = n.out.rids->ids[0]; + } else { + return n.out.result; + } + + r.in.domain_handle = handle; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.rid = rid; + r.out.user_handle = &user_handle; + + status = dcerpc_samr_OpenUser_r(b, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "OpenUser(%s) failed - %s\n", name, nt_errstr(status)); + return status; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "OpenUser(%s) failed - %s\n", name, nt_errstr(r.out.result)); + return r.out.result; + } + + d.in.user_handle = &user_handle; + d.out.user_handle = &user_handle; + status = dcerpc_samr_DeleteUser_r(b, mem_ctx, &d); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(d.out.result)) { + return d.out.result; + } + + return NT_STATUS_OK; +} + +/* + create a test user in the domain + an opaque pointer is returned. Pass it to torture_leave_domain() + when finished +*/ + +struct test_join *torture_create_testuser_max_pwlen(struct torture_context *tctx, + const char *username, + const char *domain, + uint16_t acct_type, + const char **random_password, + int max_pw_len) +{ + NTSTATUS status; + struct samr_Connect c; + struct samr_CreateUser2 r; + struct samr_OpenDomain o; + struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + struct samr_SetUserInfo s; + union samr_UserInfo u; + struct policy_handle handle; + uint32_t access_granted; + uint32_t rid; + DATA_BLOB session_key; + struct lsa_String name; + + int policy_min_pw_len = 0; + struct test_join *join; + char *random_pw; + const char *dc_binding = torture_setting_string(tctx, "dc_binding", NULL); + struct dcerpc_binding_handle *b = NULL; + join = talloc(NULL, struct test_join); + if (join == NULL) { + return NULL; + } + + ZERO_STRUCTP(join); + + torture_comment(tctx, "Connecting to SAMR\n"); + + if (dc_binding) { + status = dcerpc_pipe_connect(join, + &join->p, + dc_binding, + &ndr_table_samr, + samba_cmdline_get_creds(), + NULL, tctx->lp_ctx); + + } else { + status = torture_rpc_connection(tctx, + &join->p, + &ndr_table_samr); + } + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + b = join->p->binding_handle; + + c.in.system_name = NULL; + c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + c.out.connect_handle = &handle; + + status = dcerpc_samr_Connect_r(b, join, &c); + if (!NT_STATUS_IS_OK(status)) { + const char *errstr = nt_errstr(status); + torture_comment(tctx, "samr_Connect failed - %s\n", errstr); + return NULL; + } + if (!NT_STATUS_IS_OK(c.out.result)) { + const char *errstr = nt_errstr(c.out.result); + torture_comment(tctx, "samr_Connect failed - %s\n", errstr); + return NULL; + } + + if (domain) { + torture_comment(tctx, "Opening domain %s\n", domain); + + name.string = domain; + l.in.connect_handle = &handle; + l.in.domain_name = &name; + l.out.sid = &sid; + + status = dcerpc_samr_LookupDomain_r(b, join, &l); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(l.out.result)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result)); + goto failed; + } + } else { + struct samr_EnumDomains e; + uint32_t resume_handle = 0, num_entries; + struct samr_SamArray *sam; + int i; + + e.in.connect_handle = &handle; + e.in.buf_size = (uint32_t)-1; + e.in.resume_handle = &resume_handle; + e.out.sam = &sam; + e.out.num_entries = &num_entries; + e.out.resume_handle = &resume_handle; + + status = dcerpc_samr_EnumDomains_r(b, join, &e); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "EnumDomains failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(e.out.result)) { + torture_comment(tctx, "EnumDomains failed - %s\n", nt_errstr(e.out.result)); + goto failed; + } + if ((num_entries != 2) || (sam && sam->count != 2)) { + torture_comment(tctx, "unexpected number of domains\n"); + goto failed; + } + for (i=0; i < 2; i++) { + if (!strequal(sam->entries[i].name.string, "builtin")) { + domain = sam->entries[i].name.string; + break; + } + } + if (domain) { + torture_comment(tctx, "Opening domain %s\n", domain); + + name.string = domain; + l.in.connect_handle = &handle; + l.in.domain_name = &name; + l.out.sid = &sid; + + status = dcerpc_samr_LookupDomain_r(b, join, &l); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(l.out.result)) { + torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result)); + goto failed; + } + } else { + torture_comment(tctx, "cannot proceed without domain name\n"); + goto failed; + } + } + + talloc_steal(join, *l.out.sid); + join->dom_sid = *l.out.sid; + join->dom_netbios_name = talloc_strdup(join, domain); + if (!join->dom_netbios_name) goto failed; + + o.in.connect_handle = &handle; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.in.sid = *l.out.sid; + o.out.domain_handle = &join->domain_handle; + + status = dcerpc_samr_OpenDomain_r(b, join, &o); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(o.out.result)) { + torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(o.out.result)); + goto failed; + } + + torture_comment(tctx, "Creating account %s\n", username); + +again: + name.string = username; + r.in.domain_handle = &join->domain_handle; + r.in.account_name = &name; + r.in.acct_flags = acct_type; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.user_handle = &join->user_handle; + r.out.access_granted = &access_granted; + r.out.rid = &rid; + + status = dcerpc_samr_CreateUser2_r(b, join, &r); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "CreateUser2 failed - %s\n", nt_errstr(status)); + goto failed; + } + + if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) { + status = DeleteUser_byname(tctx, b, join, &join->domain_handle, name.string); + if (NT_STATUS_IS_OK(status)) { + goto again; + } + } + + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "CreateUser2 failed - %s\n", nt_errstr(r.out.result)); + goto failed; + } + + join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid); + + pwp.in.user_handle = &join->user_handle; + pwp.out.info = &info; + + status = dcerpc_samr_GetUserPwInfo_r(b, join, &pwp); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(pwp.out.result)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + + random_pw = generate_random_password(join, MAX(8, policy_min_pw_len), max_pw_len); + + torture_comment(tctx, "Setting account password '%s'\n", random_pw); + + ZERO_STRUCT(u); + s.in.user_handle = &join->user_handle; + s.in.info = &u; + s.in.level = 24; + + u.info24.password_expired = 0; + + status = dcerpc_fetch_session_key(join->p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + torture_leave_domain(tctx, join); + goto failed; + } + + status = init_samr_CryptPassword(random_pw, + &session_key, + &u.info24.password); + torture_assert_ntstatus_ok(tctx, + status, + "init_samr_CryptPassword failed"); + + status = dcerpc_samr_SetUserInfo_r(b, join, &s); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(s.out.result)); + goto failed; + } + + ZERO_STRUCT(u); + s.in.user_handle = &join->user_handle; + s.in.info = &u; + s.in.level = 21; + + u.info21.acct_flags = acct_type | ACB_PWNOEXP; + u.info21.fields_present = SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME; + + u.info21.comment.string = talloc_asprintf(join, + "Tortured by Samba4: %s", + timestring(join, time(NULL))); + + u.info21.full_name.string = talloc_asprintf(join, + "Torture account for Samba4: %s", + timestring(join, time(NULL))); + + u.info21.description.string = talloc_asprintf(join, + "Samba4 torture account created by host %s: %s", + lpcfg_netbios_name(tctx->lp_ctx), + timestring(join, time(NULL))); + + torture_comment(tctx, "Resetting ACB flags, force pw change time\n"); + + status = dcerpc_samr_SetUserInfo_r(b, join, &s); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(status)); + goto failed; + } + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(s.out.result)); + goto failed; + } + + if (random_password) { + *random_password = random_pw; + } + + return join; + +failed: + torture_leave_domain(tctx, join); + return NULL; +} + +/* + * Set privileges on an account. + */ + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} +static void init_lsa_String(struct lsa_String *name, const char *s) +{ + name->string = s; +} + +bool torture_setup_privs(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint32_t num_privs, + const char **privs, + const struct dom_sid *user_sid) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct policy_handle *handle; + int i; + + torture_assert(tctx, + test_lsa_OpenPolicy2(b, tctx, &handle), + "failed to open policy"); + + for (i=0; i < num_privs; i++) { + struct lsa_LookupPrivValue r; + struct lsa_LUID luid; + struct lsa_String name; + + init_lsa_String(&name, privs[i]); + + r.in.handle = handle; + r.in.name = &name; + r.out.luid = &luid; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_LookupPrivValue_r(b, tctx, &r), + "lsa_LookupPrivValue failed"); + if (!NT_STATUS_IS_OK(r.out.result)) { + torture_comment(tctx, "lsa_LookupPrivValue failed for '%s' with %s\n", + privs[i], nt_errstr(r.out.result)); + return false; + } + } + + { + struct lsa_AddAccountRights r; + struct lsa_RightSet rights; + + rights.count = num_privs; + rights.names = talloc_zero_array(tctx, struct lsa_StringLarge, rights.count); + for (i=0; i < rights.count; i++) { + init_lsa_StringLarge(&rights.names[i], privs[i]); + } + + r.in.handle = handle; + r.in.sid = discard_const_p(struct dom_sid, user_sid); + r.in.rights = &rights; + + torture_assert_ntstatus_ok(tctx, + dcerpc_lsa_AddAccountRights_r(b, tctx, &r), + "lsa_AddAccountRights failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "lsa_AddAccountRights failed"); + } + + test_lsa_Close(b, tctx, handle); + + return true; +} + +struct test_join *torture_create_testuser(struct torture_context *torture, + const char *username, + const char *domain, + uint16_t acct_type, + const char **random_password) +{ + return torture_create_testuser_max_pwlen(torture, username, domain, acct_type, random_password, 255); +} + +NTSTATUS torture_delete_testuser(struct torture_context *torture, + struct test_join *join, + const char *username) +{ + NTSTATUS status; + + status = DeleteUser_byname(torture, + join->p->binding_handle, + torture, + &join->domain_handle, + username); + + return status; +} + +_PUBLIC_ struct test_join *torture_join_domain(struct torture_context *tctx, + const char *machine_name, + uint32_t acct_flags, + struct cli_credentials **machine_credentials) +{ + NTSTATUS status; + struct libnet_context *libnet_ctx; + struct libnet_JoinDomain *libnet_r; + struct test_join *tj; + struct samr_SetUserInfo s; + union samr_UserInfo u; + const char *binding_str = NULL; + struct dcerpc_binding *binding = NULL; + enum dcerpc_transport_t transport; + + tj = talloc_zero(tctx, struct test_join); + if (!tj) return NULL; + + binding_str = torture_setting_string(tctx, "binding", NULL); + if (binding_str == NULL) { + const char *host = torture_setting_string(tctx, "host", NULL); + binding_str = talloc_asprintf(tj, "ncacn_np:%s", host); + } + status = dcerpc_parse_binding(tj, binding_str, &binding); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_parse_binding(%s) failed - %s\n", + binding_str, nt_errstr(status))); + talloc_free(tj); + return NULL; + } + transport = dcerpc_binding_get_transport(binding); + switch (transport) { + case NCALRPC: + case NCACN_UNIX_STREAM: + break; + default: + dcerpc_binding_set_transport(binding, NCACN_NP); + dcerpc_binding_set_flags(binding, 0, DCERPC_AUTH_OPTIONS); + break; + } + + libnet_r = talloc_zero(tj, struct libnet_JoinDomain); + if (!libnet_r) { + talloc_free(tj); + return NULL; + } + + libnet_ctx = libnet_context_init(tctx->ev, tctx->lp_ctx); + if (!libnet_ctx) { + talloc_free(tj); + return NULL; + } + + tj->libnet_r = libnet_r; + + libnet_ctx->cred = samba_cmdline_get_creds(); + libnet_r->in.binding = dcerpc_binding_string(libnet_r, binding); + if (libnet_r->in.binding == NULL) { + talloc_free(tj); + return NULL; + } + libnet_r->in.level = LIBNET_JOINDOMAIN_SPECIFIED; + libnet_r->in.netbios_name = machine_name; + libnet_r->in.account_name = talloc_asprintf(libnet_r, "%s$", machine_name); + if (!libnet_r->in.account_name) { + talloc_free(tj); + return NULL; + } + + libnet_r->in.acct_type = acct_flags; + libnet_r->in.recreate_account = true; + + status = libnet_JoinDomain(libnet_ctx, libnet_r, libnet_r); + if (!NT_STATUS_IS_OK(status)) { + if (libnet_r->out.error_string) { + DEBUG(0, ("Domain join failed - %s\n", libnet_r->out.error_string)); + } else { + DEBUG(0, ("Domain join failed - %s\n", nt_errstr(status))); + } + talloc_free(tj); + return NULL; + } + tj->p = libnet_r->out.samr_pipe; + tj->user_handle = *libnet_r->out.user_handle; + tj->dom_sid = libnet_r->out.domain_sid; + talloc_steal(tj, libnet_r->out.domain_sid); + tj->dom_netbios_name = libnet_r->out.domain_name; + talloc_steal(tj, libnet_r->out.domain_name); + tj->dom_dns_name = libnet_r->out.realm; + talloc_steal(tj, libnet_r->out.realm); + tj->user_guid = libnet_r->out.account_guid; + tj->netbios_name = talloc_strdup(tj, machine_name); + if (!tj->netbios_name) { + talloc_free(tj); + return NULL; + } + + ZERO_STRUCT(u); + s.in.user_handle = &tj->user_handle; + s.in.info = &u; + s.in.level = 21; + + u.info21.fields_present = SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME; + u.info21.comment.string = talloc_asprintf(tj, + "Tortured by Samba4: %s", + timestring(tj, time(NULL))); + u.info21.full_name.string = talloc_asprintf(tj, + "Torture account for Samba4: %s", + timestring(tj, time(NULL))); + + u.info21.description.string = talloc_asprintf(tj, + "Samba4 torture account created by host %s: %s", + lpcfg_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL))); + + status = dcerpc_samr_SetUserInfo_r(tj->p->binding_handle, tj, &s); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "SetUserInfo (non-critical) failed - %s\n", nt_errstr(status)); + } + if (!NT_STATUS_IS_OK(s.out.result)) { + torture_comment(tctx, "SetUserInfo (non-critical) failed - %s\n", nt_errstr(s.out.result)); + } + + *machine_credentials = cli_credentials_init(tj); + cli_credentials_set_conf(*machine_credentials, tctx->lp_ctx); + cli_credentials_set_workstation(*machine_credentials, machine_name, CRED_SPECIFIED); + cli_credentials_set_domain(*machine_credentials, libnet_r->out.domain_name, CRED_SPECIFIED); + if (libnet_r->out.realm) { + cli_credentials_set_realm(*machine_credentials, libnet_r->out.realm, CRED_SPECIFIED); + } + cli_credentials_set_username(*machine_credentials, libnet_r->in.account_name, CRED_SPECIFIED); + cli_credentials_set_password(*machine_credentials, libnet_r->out.join_password, CRED_SPECIFIED); + cli_credentials_set_kvno(*machine_credentials, libnet_r->out.kvno); + if (acct_flags & ACB_SVRTRUST) { + cli_credentials_set_secure_channel_type(*machine_credentials, + SEC_CHAN_BDC); + } else if (acct_flags & ACB_WSTRUST) { + cli_credentials_set_secure_channel_type(*machine_credentials, + SEC_CHAN_WKSTA); + } else { + DEBUG(0, ("Invalid account type specified to torture_join_domain\n")); + talloc_free(*machine_credentials); + return NULL; + } + + return tj; +} + +struct dcerpc_pipe *torture_join_samr_pipe(struct test_join *join) +{ + return join->p; +} + +struct policy_handle *torture_join_samr_user_policy(struct test_join *join) +{ + return &join->user_handle; +} + +static NTSTATUS torture_leave_ads_domain(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct libnet_JoinDomain *libnet_r) +{ + int rtn; + TALLOC_CTX *tmp_ctx; + + struct ldb_dn *server_dn; + struct ldb_context *ldb_ctx; + + char *remote_ldb_url; + + /* Check if we are a domain controller. If not, exit. */ + if (!libnet_r->out.server_dn_str) { + return NT_STATUS_OK; + } + + tmp_ctx = talloc_named(mem_ctx, 0, "torture_leave temporary context"); + if (!tmp_ctx) { + libnet_r->out.error_string = NULL; + return NT_STATUS_NO_MEMORY; + } + + ldb_ctx = ldb_init(tmp_ctx, torture->ev); + if (!ldb_ctx) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* Remove CN=Servers,... entry from the AD. */ + server_dn = ldb_dn_new(tmp_ctx, ldb_ctx, libnet_r->out.server_dn_str); + if (! ldb_dn_validate(server_dn)) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", + dcerpc_binding_get_string_option(libnet_r->out.samr_binding, "host")); + if (!remote_ldb_url) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + ldb_set_opaque(ldb_ctx, "credentials", samba_cmdline_get_creds()); + ldb_set_opaque(ldb_ctx, "loadparm", samba_cmdline_get_lp_ctx()); + + rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL); + if (rtn != LDB_SUCCESS) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + rtn = ldb_delete(ldb_ctx, server_dn); + if (rtn != LDB_SUCCESS) { + libnet_r->out.error_string = NULL; + talloc_free(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(0, ("%s removed successfully.\n", libnet_r->out.server_dn_str)); + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* + leave the domain, deleting the machine acct +*/ + +_PUBLIC_ void torture_leave_domain(struct torture_context *tctx, struct test_join *join) +{ + struct samr_DeleteUser d; + NTSTATUS status; + + if (!join) { + return; + } + d.in.user_handle = &join->user_handle; + d.out.user_handle = &join->user_handle; + + /* Delete machine account */ + status = dcerpc_samr_DeleteUser_r(join->p->binding_handle, join, &d); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "DeleteUser failed\n"); + } else if (!NT_STATUS_IS_OK(d.out.result)) { + torture_comment(tctx, "Delete of machine account %s failed\n", + join->netbios_name); + } else { + torture_comment(tctx, "Delete of machine account %s was successful.\n", + join->netbios_name); + } + + if (join->libnet_r) { + status = torture_leave_ads_domain(tctx, join, join->libnet_r); + } + + talloc_free(join); +} + +/* + return the dom sid for a test join +*/ +_PUBLIC_ const struct dom_sid *torture_join_sid(struct test_join *join) +{ + return join->dom_sid; +} + +const struct dom_sid *torture_join_user_sid(struct test_join *join) +{ + return join->user_sid; +} + +const char *torture_join_netbios_name(struct test_join *join) +{ + return join->netbios_name; +} + +const struct GUID *torture_join_user_guid(struct test_join *join) +{ + return &join->user_guid; +} + +const char *torture_join_dom_netbios_name(struct test_join *join) +{ + return join->dom_netbios_name; +} + +const char *torture_join_dom_dns_name(struct test_join *join) +{ + return join->dom_dns_name; +} + +#if 0 /* Left as the documentation of the join process, but see new implementation in libnet_become_dc.c */ +struct test_join_ads_dc { + struct test_join *join; +}; + +struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name, + const char *domain, + struct cli_credentials **machine_credentials) +{ + struct test_join_ads_dc *join; + + join = talloc(NULL, struct test_join_ads_dc); + if (join == NULL) { + return NULL; + } + + join->join = torture_join_domain(machine_name, + ACB_SVRTRUST, + machine_credentials); + + if (!join->join) { + return NULL; + } + +/* W2K: */ + /* W2K: modify userAccountControl from 4096 to 532480 */ + + /* W2K: modify RDN to OU=Domain Controllers and skip the $ from server name */ + + /* ask objectVersion of Schema Partition */ + + /* ask rIDManagerReferenz of the Domain Partition */ + + /* ask fsMORoleOwner of the RID-Manager$ object + * returns CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name, ... + */ + + /* ask for dnsHostName of CN=,CN=Servers,CN=Default-First-Site-Name, ... */ + + /* ask for objectGUID of CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name, ... */ + + /* ask for * of CN=Default-First-Site-Name, ... */ + + /* search (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=$)) in Domain Partition + * attributes : distinguishedName, userAccountControl + */ + + /* ask * for CN=,CN=Servers,CN=Default-First-Site-Name,... + * should fail with noSuchObject + */ + + /* add CN=,CN=Servers,CN=Default-First-Site-Name,... + * + * objectClass = server + * systemFlags = 50000000 + * serverReferenz = CN=,OU=Domain Controllers,... + */ + + /* ask for * of CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name, ... + * should fail with noSuchObject + */ + + /* search for (ncname=) in CN=Partitions,CN=Configuration,... + * attributes: ncName, dnsRoot + */ + + /* modify add CN=,CN=Servers,CN=Default-First-Site-Name,... + * serverReferenz = CN=,OU=Domain Controllers,... + * should fail with attributeOrValueExists + */ + + /* modify replace CN=,CN=Servers,CN=Default-First-Site-Name,... + * serverReferenz = CN=,OU=Domain Controllers,... + */ + + /* DsAddEntry to create the CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name, ... + * + */ + + /* replicate CN=Schema,CN=Configuration,... + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* replicate CN=Configuration,... + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* replicate Domain Partition + * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71") + * + */ + + /* call DsReplicaUpdateRefs() for all partitions like this: + * req1: struct drsuapi_DsReplicaUpdateRefsRequest1 + * naming_context : * + * naming_context: struct drsuapi_DsReplicaObjectIdentifier + * __ndr_size : 0x000000ae (174) + * __ndr_size_sid : 0x00000000 (0) + * guid : 00000000-0000-0000-0000-000000000000 + * sid : S-0-0 + * dn : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base' + * dest_dsa_dns_name : * + * dest_dsa_dns_name : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base' + * dest_dsa_guid : 4a0df188-a0b8-47ea-bbe5-e614723f16dd + * options : 0x0000001c (28) + * 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION + * 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE + * 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE + * 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE + * 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010 + * + * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the + * CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name, ... + */ + +/* W2K3: see libnet/libnet_become_dc.c */ + return join; +} + +#endif diff --git a/source4/torture/rpc/torture_rpc.h b/source4/torture/rpc/torture_rpc.h new file mode 100644 index 0000000..9217461 --- /dev/null +++ b/source4/torture/rpc/torture_rpc.h @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __TORTURE_RPC_H__ +#define __TORTURE_RPC_H__ + +#include "lib/torture/torture.h" +#include "auth/credentials/credentials.h" +#include "torture/rpc/drsuapi.h" +#include "libnet/libnet_join.h" +#include "librpc/rpc/dcerpc.h" +#include "libcli/raw/libcliraw.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "torture/rpc/proto.h" + +struct torture_rpc_tcase { + struct torture_tcase tcase; + const struct ndr_interface_table *table; + const char *machine_name; + bool (*setup_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *); + bool (*teardown_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *); +}; + +struct torture_rpc_tcase_data { + struct test_join *join_ctx; + struct dcerpc_pipe *pipe; + struct cli_credentials *credentials; +}; + +NTSTATUS torture_rpc_connection(struct torture_context *tctx, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table); +NTSTATUS torture_rpc_connection_with_binding(struct torture_context *tctx, + struct dcerpc_binding *binding, + struct dcerpc_pipe **p, + const struct ndr_interface_table *table); + +struct test_join *torture_join_domain(struct torture_context *tctx, + const char *machine_name, + uint32_t acct_flags, + struct cli_credentials **machine_credentials); +const struct dom_sid *torture_join_sid(struct test_join *join); +void torture_leave_domain(struct torture_context *tctx, struct test_join *join); +struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table); +struct torture_rpc_tcase *torture_suite_add_rpc_setup_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + bool (*setup_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *), + bool (*teardown_fn)(struct torture_context *, + struct dcerpc_pipe *, + void *)); + +struct torture_test *torture_rpc_tcase_add_test( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *)); +struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table); + +struct torture_test *torture_rpc_tcase_add_test_join( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + struct cli_credentials *, struct test_join *)); +_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_setup( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn)(struct torture_context *, + struct dcerpc_pipe *, + void *), + void *userdata); +struct torture_test *torture_rpc_tcase_add_test_ex( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, + void *), + void *userdata); +struct torture_rpc_tcase *torture_suite_add_machine_bdc_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name); +struct torture_rpc_tcase *torture_suite_add_machine_workstation_rpc_iface_tcase( + struct torture_suite *suite, + const char *name, + const struct ndr_interface_table *table, + const char *machine_name); +struct torture_test *torture_rpc_tcase_add_test_creds( + struct torture_rpc_tcase *tcase, + const char *name, + bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *)); +bool torture_suite_init_rpc_tcase(struct torture_suite *suite, + struct torture_rpc_tcase *tcase, + const char *name, + const struct ndr_interface_table *table); + + + +#endif /* __TORTURE_RPC_H__ */ diff --git a/source4/torture/rpc/unixinfo.c b/source4/torture/rpc/unixinfo.c new file mode 100644 index 0000000..227b002 --- /dev/null +++ b/source4/torture/rpc/unixinfo.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + test suite for unixinfo rpc operations + + Copyright (C) Volker Lendecke 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_unixinfo_c.h" +#include "libcli/security/security.h" + +/** + test the SidToUid interface +*/ +static bool test_sidtouid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct unixinfo_SidToUid r; + struct dom_sid *sid; + uint64_t uid; + struct dcerpc_binding_handle *b = p->binding_handle; + + sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432"); + r.in.sid = *sid; + r.out.uid = &uid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_SidToUid_r(b, tctx, &r), + "SidToUid failed"); + if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, r.out.result)) { + } else torture_assert_ntstatus_ok(tctx, r.out.result, "SidToUid failed"); + + return true; +} + +/* + test the UidToSid interface +*/ +static bool test_uidtosid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct unixinfo_UidToSid r; + struct dom_sid sid; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.uid = 1000; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_UidToSid_r(b, tctx, &r), + "UidToSid failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "UidToSid failed"); + return true; +} + +static bool test_getpwuid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint64_t uids[512]; + uint32_t num_uids = ARRAY_SIZE(uids); + uint32_t i; + struct unixinfo_GetPWUid r; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0; ibinding_handle; + + sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432"); + r.in.sid = *sid; + r.out.gid = &gid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_SidToGid_r(b, tctx, &r), + "SidToGid failed"); + if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, r.out.result)) { + } else torture_assert_ntstatus_ok(tctx, r.out.result, "SidToGid failed"); + + return true; +} + +/* + test the GidToSid interface +*/ +static bool test_gidtosid(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct unixinfo_GidToSid r; + struct dom_sid sid; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.gid = 1000; + r.out.sid = &sid; + + torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GidToSid_r(b, tctx, &r), + "GidToSid failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, "GidToSid failed"); + + return true; +} + +struct torture_suite *torture_rpc_unixinfo(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + + suite = torture_suite_create(mem_ctx, "unixinfo"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "unixinfo", + &ndr_table_unixinfo); + + torture_rpc_tcase_add_test(tcase, "sidtouid", test_sidtouid); + torture_rpc_tcase_add_test(tcase, "uidtosid", test_uidtosid); + torture_rpc_tcase_add_test(tcase, "getpwuid", test_getpwuid); + torture_rpc_tcase_add_test(tcase, "sidtogid", test_sidtogid); + torture_rpc_tcase_add_test(tcase, "gidtosid", test_gidtosid); + + return suite; +} diff --git a/source4/torture/rpc/winreg.c b/source4/torture/rpc/winreg.c new file mode 100644 index 0000000..b0e153f --- /dev/null +++ b/source4/torture/rpc/winreg.c @@ -0,0 +1,3335 @@ +/* + Unix SMB/CIFS implementation. + test suite for winreg rpc operations + + Copyright (C) Tim Potter 2003 + Copyright (C) Jelmer Vernooij 2004-2007 + Copyright (C) Günther Deschner 2007,2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_winreg_c.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "lib/registry/registry.h" + +#define TEST_KEY_BASE "winreg_torture_test" +#define TEST_KEY1 "spottyfoot" +#define TEST_KEY2 "with a SD (#1)" +#define TEST_KEY3 "with a subkey" +#define TEST_KEY4 "sd_tests" +#define TEST_SUBKEY "subkey" +#define TEST_SUBKEY_SD "subkey_sd" +#define TEST_SUBSUBKEY_SD "subkey_sd\\subsubkey_sd" +#define TEST_VALUE "torture_value_name" +#define TEST_KEY_VOLATILE "torture_volatile_key" +#define TEST_SUBKEY_VOLATILE "torture_volatile_subkey" +#define TEST_KEY_SYMLINK "torture_symlink_key" +#define TEST_KEY_SYMLINK_DEST "torture_symlink_dest" + +#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500" + +static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s) +{ + name->string = s; +} + +static void init_winreg_String(struct winreg_String *name, const char *s) +{ + name->name = s; + if (s) { + name->name_len = 2 * (strlen_m(s) + 1); + name->name_size = name->name_len; + } else { + name->name_len = 0; + name->name_size = 0; + } +} + +static bool test_GetVersion(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_GetVersion r; + uint32_t v; + + torture_comment(tctx, "Testing GetVersion\n"); + + ZERO_STRUCT(r); + r.in.handle = handle; + r.out.version = &v; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_GetVersion_r(b, tctx, &r), + "GetVersion failed"); + + torture_assert_werr_ok(tctx, r.out.result, "GetVersion failed"); + + return true; +} + +static bool test_NotifyChangeKeyValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_NotifyChangeKeyValue r; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.in.watch_subtree = true; + r.in.notify_filter = 0; + r.in.unknown = r.in.unknown2 = 0; + init_winreg_String(&r.in.string1, NULL); + init_winreg_String(&r.in.string2, NULL); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_NotifyChangeKeyValue_r(b, tctx, &r), + "NotifyChangeKeyValue failed"); + + if (!W_ERROR_IS_OK(r.out.result)) { + torture_comment(tctx, + "NotifyChangeKeyValue failed - %s - not considering\n", + win_errstr(r.out.result)); + return true; + } + + return true; +} + +static bool test_CreateKey_opts(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *name, + const char *kclass, + uint32_t options, + uint32_t access_mask, + struct winreg_SecBuf *secdesc, + WERROR expected_result, + enum winreg_CreateAction *action_taken_p, + struct policy_handle *new_handle_p) +{ + struct winreg_CreateKey r; + struct policy_handle newhandle; + enum winreg_CreateAction action_taken = 0; + + torture_comment(tctx, "Testing CreateKey(%s)\n", name); + + ZERO_STRUCT(r); + r.in.handle = handle; + init_winreg_String(&r.in.name, name); + init_winreg_String(&r.in.keyclass, kclass); + r.in.options = options; + r.in.access_mask = access_mask; + r.in.action_taken = &action_taken; + r.in.secdesc = secdesc; + r.out.new_handle = &newhandle; + r.out.action_taken = &action_taken; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey_r(b, tctx, &r), + "CreateKey failed"); + + torture_assert_werr_equal(tctx, r.out.result, expected_result, "CreateKey failed"); + + if (new_handle_p) { + *new_handle_p = newhandle; + } + if (action_taken_p) { + *action_taken_p = *r.out.action_taken; + } + + return true; +} + +static bool test_CreateKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *name, + const char *kclass) +{ + return test_CreateKey_opts(tctx, b, handle, name, kclass, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, /* secdesc */ + WERR_OK, + NULL, /* action_taken */ + NULL /* new_handle */); +} + +/* + createkey testing with a SD +*/ +static bool test_CreateKey_sd(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *name, + const char *kclass, + struct policy_handle *newhandle) +{ + struct winreg_CreateKey r; + enum winreg_CreateAction action_taken = 0; + struct security_descriptor *sd; + DATA_BLOB sdblob; + struct winreg_SecBuf secbuf; + + sd = security_descriptor_dacl_create(tctx, + 0, + NULL, NULL, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + NULL); + + torture_assert_ndr_success(tctx, + ndr_push_struct_blob(&sdblob, tctx, sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor), + "Failed to push security_descriptor ?!\n"); + + secbuf.sd.data = sdblob.data; + secbuf.sd.len = sdblob.length; + secbuf.sd.size = sdblob.length; + secbuf.length = sdblob.length-10; + secbuf.inherit = 0; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.out.new_handle = newhandle; + init_winreg_String(&r.in.name, name); + init_winreg_String(&r.in.keyclass, kclass); + r.in.options = 0x0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.in.action_taken = r.out.action_taken = &action_taken; + r.in.secdesc = &secbuf; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey_r(b, tctx, &r), + "CreateKey with sd failed"); + + torture_assert_werr_ok(tctx, r.out.result, "CreateKey with sd failed"); + + return true; +} + +static bool _test_GetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *sec_info_ptr, + WERROR get_werr, + struct security_descriptor **sd_out) +{ + struct winreg_GetKeySecurity r; + struct security_descriptor *sd = NULL; + uint32_t sec_info; + DATA_BLOB sdblob; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (sec_info_ptr) { + sec_info = *sec_info_ptr; + } else { + sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; + } + + ZERO_STRUCT(r); + + r.in.handle = handle; + r.in.sec_info = sec_info; + r.in.sd = r.out.sd = talloc_zero(tctx, struct KeySecurityData); + r.in.sd->size = 0x1000; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_GetKeySecurity_r(b, tctx, &r), + "GetKeySecurity failed"); + + torture_assert_werr_equal(tctx, r.out.result, get_werr, + "GetKeySecurity failed"); + + sdblob.data = r.out.sd->data; + sdblob.length = r.out.sd->len; + + sd = talloc_zero(tctx, struct security_descriptor); + + torture_assert_ndr_success(tctx, + ndr_pull_struct_blob(&sdblob, tctx, sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor), + "pull_security_descriptor failed"); + + if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) { + NDR_PRINT_DEBUG(security_descriptor, sd); + } + + if (sd_out) { + *sd_out = sd; + } else { + talloc_free(sd); + } + + return true; +} + +static bool test_GetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct security_descriptor **sd_out) +{ + return _test_GetKeySecurity(p, tctx, handle, NULL, WERR_OK, sd_out); +} + +static bool _test_SetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t *sec_info_ptr, + struct security_descriptor *sd, + WERROR werr) +{ + struct winreg_SetKeySecurity r; + struct KeySecurityData *sdata = NULL; + DATA_BLOB sdblob; + uint32_t sec_info; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + + if (sd && (p->conn->flags & DCERPC_DEBUG_PRINT_OUT)) { + NDR_PRINT_DEBUG(security_descriptor, sd); + } + + torture_assert_ndr_success(tctx, + ndr_push_struct_blob(&sdblob, tctx, sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor), + "push_security_descriptor failed"); + + sdata = talloc_zero(tctx, struct KeySecurityData); + sdata->data = sdblob.data; + sdata->size = sdblob.length; + sdata->len = sdblob.length; + + if (sec_info_ptr) { + sec_info = *sec_info_ptr; + } else { + sec_info = SECINFO_UNPROTECTED_SACL | + SECINFO_UNPROTECTED_DACL; + if (sd->owner_sid) { + sec_info |= SECINFO_OWNER; + } + if (sd->group_sid) { + sec_info |= SECINFO_GROUP; + } + if (sd->sacl) { + sec_info |= SECINFO_SACL; + } + if (sd->dacl) { + sec_info |= SECINFO_DACL; + } + } + + r.in.handle = handle; + r.in.sec_info = sec_info; + r.in.sd = sdata; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_SetKeySecurity_r(b, tctx, &r), + "SetKeySecurity failed"); + + torture_assert_werr_equal(tctx, r.out.result, werr, + "SetKeySecurity failed"); + + return true; +} + +static bool test_SetKeySecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + struct security_descriptor *sd) +{ + return _test_SetKeySecurity(p, tctx, handle, NULL, sd, WERR_OK); +} + +static bool test_CloseKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_CloseKey r; + + ZERO_STRUCT(r); + r.in.handle = r.out.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r), + "CloseKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed"); + + return true; +} + +static bool test_FlushKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + struct winreg_FlushKey r; + + ZERO_STRUCT(r); + r.in.handle = handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_FlushKey_r(b, tctx, &r), + "FlushKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "FlushKey failed"); + + return true; +} + +static bool test_OpenKey_opts(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *hive_handle, + const char *keyname, + uint32_t options, + uint32_t access_mask, + struct policy_handle *key_handle, + WERROR expected_result) +{ + struct winreg_OpenKey r; + + ZERO_STRUCT(r); + r.in.parent_handle = hive_handle; + init_winreg_String(&r.in.keyname, keyname); + r.in.options = options; + r.in.access_mask = access_mask; + r.out.handle = key_handle; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey_r(b, tctx, &r), + "OpenKey failed"); + + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "OpenKey failed"); + + return true; +} + +static bool test_OpenKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *hive_handle, + const char *keyname, struct policy_handle *key_handle) +{ + return test_OpenKey_opts(tctx, b, hive_handle, keyname, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + key_handle, + WERR_OK); +} + +static bool test_Cleanup(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *key) +{ + struct winreg_DeleteKey r; + + ZERO_STRUCT(r); + r.in.handle = handle; + + init_winreg_String(&r.in.key, key); + dcerpc_winreg_DeleteKey_r(b, tctx, &r); + + return true; +} + +static bool _test_GetSetSecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + WERROR get_werr, + WERROR set_werr) +{ + struct security_descriptor *sd = NULL; + + if (!_test_GetKeySecurity(p, tctx, handle, NULL, get_werr, &sd)) { + return false; + } + + if (!_test_SetKeySecurity(p, tctx, handle, NULL, sd, set_werr)) { + return false; + } + + return true; +} + +static bool test_SecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "SecurityDescriptor get & set\n"); + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle, + WERR_OK, WERR_OK)) { + ret = false; + } + + if (!test_CloseKey(b, tctx, &new_handle)) { + return false; + } + + return ret; +} + +static bool _test_SecurityDescriptor(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t access_mask, + const char *key, + WERROR open_werr, + WERROR get_werr, + WERROR set_werr) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, key, + REG_OPTION_NON_VOLATILE, + access_mask, + &new_handle, + open_werr), + "failed to open key"); + + if (!W_ERROR_IS_OK(open_werr)) { + return true; + } + + if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle, + get_werr, set_werr)) { + ret = false; + } + + if (!test_CloseKey(b, tctx, &new_handle)) { + return false; + } + + return ret; +} + +static bool test_dacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if (dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) { + return true; + } + } + + return false; +} + +static bool _test_dacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_dacl_trustee_present(p, tctx, &new_handle, sid); + + test_CloseKey(b, tctx, &new_handle); + + return ret; +} + +static bool test_sacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + int i; + uint32_t sec_info = SECINFO_SACL; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->sacl) { + return false; + } + + for (i = 0; i < sd->sacl->num_aces; i++) { + if (dom_sid_equal(&sd->sacl->aces[i].trustee, sid)) { + return true; + } + } + + return false; +} + +static bool _test_sacl_trustee_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, key, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_SYSTEM_SECURITY, + &new_handle, + WERR_OK), + "failed to open key"); + + ret = test_sacl_trustee_present(p, tctx, &new_handle, sid); + + test_CloseKey(b, tctx, &new_handle); + + return ret; +} + +static bool test_owner_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + uint32_t sec_info = SECINFO_OWNER; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->owner_sid) { + return false; + } + + return dom_sid_equal(sd->owner_sid, sid); +} + +static bool _test_owner_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_owner_present(p, tctx, &new_handle, sid); + + test_CloseKey(b, tctx, &new_handle); + + return ret; +} + +static bool test_group_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid) +{ + struct security_descriptor *sd = NULL; + uint32_t sec_info = SECINFO_GROUP; + + if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) { + return false; + } + + if (!sd || !sd->group_sid) { + return false; + } + + return dom_sid_equal(sd->group_sid, sid); +} + +static bool _test_group_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + ret = test_group_present(p, tctx, &new_handle, sid); + + test_CloseKey(b, tctx, &new_handle); + + return ret; +} + +static bool test_dacl_trustee_flags_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct dom_sid *sid, + uint8_t flags) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if ((dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) && + (sd->dacl->aces[i].flags == flags)) { + return true; + } + } + + return false; +} + +static bool test_dacl_ace_present(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const struct security_ace *ace) +{ + struct security_descriptor *sd = NULL; + int i; + + if (!test_GetKeySecurity(p, tctx, handle, &sd)) { + return false; + } + + if (!sd || !sd->dacl) { + return false; + } + + for (i = 0; i < sd->dacl->num_aces; i++) { + if (security_ace_equal(&sd->dacl->aces[i], ace)) { + return true; + } + } + + return false; +} + +static bool test_RestoreSecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + struct security_descriptor *sd) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) { + ret = false; + } + + if (!test_CloseKey(b, tctx, &new_handle)) { + ret = false; + } + + return ret; +} + +static bool test_BackupSecurity(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + struct security_descriptor **sd) +{ + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + if (!test_GetKeySecurity(p, tctx, &new_handle, sd)) { + ret = false; + } + + if (!test_CloseKey(b, tctx, &new_handle)) { + ret = false; + } + + return ret; +} + +static bool test_SecurityDescriptorInheritance(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + /* get sd + add ace SEC_ACE_FLAG_CONTAINER_INHERIT + set sd + get sd + check ace + add subkey + get sd + check ace + add subsubkey + get sd + check ace + del subsubkey + del subkey + reset sd + */ + + struct security_descriptor *sd = NULL; + struct security_descriptor *sd_orig = NULL; + struct security_ace *ace = NULL; + struct policy_handle new_handle; + bool ret = true; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *test_subkey_sd; + const char *test_subsubkey_sd; + + torture_comment(tctx, "SecurityDescriptor inheritance\n"); + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) { + return false; + } + + sd_orig = security_descriptor_copy(tctx, sd); + if (sd_orig == NULL) { + return false; + } + + ace = security_ace_create(tctx, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_STD_REQUIRED, + SEC_ACE_FLAG_CONTAINER_INHERIT); + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_add(sd, ace), + "failed to add ace"); + + /* FIXME: add further tests for these flags */ + sd->type |= SEC_DESC_DACL_AUTO_INHERIT_REQ | + SEC_DESC_SACL_AUTO_INHERITED; + + if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) { + return false; + } + + torture_assert(tctx, + test_dacl_ace_present(p, tctx, &new_handle, ace), + "new ACE not present!"); + + if (!test_CloseKey(b, tctx, &new_handle)) { + return false; + } + + test_subkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBKEY_SD); + + if (!test_CreateKey(b, tctx, handle, test_subkey_sd, NULL)) { + ret = false; + goto out; + } + + if (!test_OpenKey(b, tctx, handle, test_subkey_sd, &new_handle)) { + ret = false; + goto out; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + torture_comment(tctx, "inherited ACE not present!\n"); + ret = false; + goto out; + } + + test_subsubkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBSUBKEY_SD); + + test_CloseKey(b, tctx, &new_handle); + if (!test_CreateKey(b, tctx, handle, test_subsubkey_sd, NULL)) { + ret = false; + goto out; + } + + if (!test_OpenKey(b, tctx, handle, test_subsubkey_sd, &new_handle)) { + ret = false; + goto out; + } + + if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) { + torture_comment(tctx, "inherited ACE not present!\n"); + ret = false; + goto out; + } + + out: + test_CloseKey(b, tctx, &new_handle); + test_Cleanup(b, tctx, handle, test_subkey_sd); + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return ret; +} + +static bool test_SecurityDescriptorBlockInheritance(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + /* get sd + add ace SEC_ACE_FLAG_NO_PROPAGATE_INHERIT + set sd + add subkey/subkey + get sd + check ace + get sd from subkey + check ace + del subkey/subkey + del subkey + reset sd + */ + + struct security_descriptor *sd = NULL; + struct security_descriptor *sd_orig = NULL; + struct security_ace *ace = NULL; + struct policy_handle new_handle; + struct dom_sid *sid = NULL; + bool ret = true; + uint8_t ace_flags = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *test_subkey_sd; + const char *test_subsubkey_sd; + + torture_comment(tctx, "SecurityDescriptor inheritance block\n"); + + if (!test_OpenKey(b, tctx, handle, key, &new_handle)) { + return false; + } + + if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) { + return false; + } + + sd_orig = security_descriptor_copy(tctx, sd); + if (sd_orig == NULL) { + return false; + } + + ace = security_ace_create(tctx, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_STD_REQUIRED, + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT); + + torture_assert_ntstatus_ok(tctx, + security_descriptor_dacl_add(sd, ace), + "failed to add ace"); + + if (!_test_SetKeySecurity(p, tctx, &new_handle, NULL, sd, WERR_OK)) { + return false; + } + + torture_assert(tctx, + test_dacl_ace_present(p, tctx, &new_handle, ace), + "new ACE not present!"); + + if (!test_CloseKey(b, tctx, &new_handle)) { + return false; + } + + test_subkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBKEY_SD); + test_subsubkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBSUBKEY_SD); + + if (!test_CreateKey(b, tctx, handle, test_subsubkey_sd, NULL)) { + return false; + } + + if (!test_OpenKey(b, tctx, handle, test_subsubkey_sd, &new_handle)) { + ret = false; + goto out; + } + + if (test_dacl_ace_present(p, tctx, &new_handle, ace)) { + torture_comment(tctx, "inherited ACE present but should not!\n"); + ret = false; + goto out; + } + + sid = dom_sid_parse_talloc(tctx, TEST_SID); + if (sid == NULL) { + return false; + } + + if (test_dacl_trustee_present(p, tctx, &new_handle, sid)) { + torture_comment(tctx, "inherited trustee SID present but should not!\n"); + ret = false; + goto out; + } + + test_CloseKey(b, tctx, &new_handle); + + if (!test_OpenKey(b, tctx, handle, test_subkey_sd, &new_handle)) { + ret = false; + goto out; + } + + if (test_dacl_ace_present(p, tctx, &new_handle, ace)) { + torture_comment(tctx, "inherited ACE present but should not!\n"); + ret = false; + goto out; + } + + if (!test_dacl_trustee_flags_present(p, tctx, &new_handle, sid, ace_flags)) { + torture_comment(tctx, "inherited trustee SID with flags 0x%02x not present!\n", + ace_flags); + ret = false; + goto out; + } + + out: + test_CloseKey(b, tctx, &new_handle); + test_Cleanup(b, tctx, handle, test_subkey_sd); + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return ret; +} + +static bool test_SecurityDescriptorsMasks(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + bool ret = true; + int i; + + struct winreg_mask_result_table { + uint32_t access_mask; + WERROR open_werr; + WERROR get_werr; + WERROR set_werr; + } sd_mask_tests[] = { + { 0, + WERR_ACCESS_DENIED, WERR_FILE_NOT_FOUND, WERR_FOOBAR }, + { SEC_FLAG_MAXIMUM_ALLOWED, + WERR_OK, WERR_OK, WERR_OK }, + { SEC_STD_WRITE_DAC, + WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR }, + { SEC_FLAG_SYSTEM_SECURITY, + WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR } + }; + + /* FIXME: before this test can ever run successfully we need a way to + * correctly read a NULL security_descritpor in ndr, get the required + * length, requery, etc. + */ + + return true; + + for (i=0; i < ARRAY_SIZE(sd_mask_tests); i++) { + + torture_comment(tctx, + "SecurityDescriptor get & set with access_mask: 0x%08x\n", + sd_mask_tests[i].access_mask); + torture_comment(tctx, + "expecting: open %s, get: %s, set: %s\n", + win_errstr(sd_mask_tests[i].open_werr), + win_errstr(sd_mask_tests[i].get_werr), + win_errstr(sd_mask_tests[i].set_werr)); + + if (_test_SecurityDescriptor(p, tctx, handle, + sd_mask_tests[i].access_mask, key, + sd_mask_tests[i].open_werr, + sd_mask_tests[i].get_werr, + sd_mask_tests[i].set_werr)) { + ret = false; + } + } + + return ret; +} + +typedef bool (*secinfo_verify_fn)(struct dcerpc_pipe *, + struct torture_context *, + struct policy_handle *, + const char *, + const struct dom_sid *); + +static bool test_SetSecurityDescriptor_SecInfo(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + const char *test, + uint32_t access_mask, + uint32_t sec_info, + struct security_descriptor *sd, + WERROR set_werr, + bool expect_present, + bool (*fn) (struct dcerpc_pipe *, + struct torture_context *, + struct policy_handle *, + const char *, + const struct dom_sid *), + const struct dom_sid *sid) +{ + struct policy_handle new_handle; + struct dcerpc_binding_handle *b = p->binding_handle; + + torture_comment(tctx, "SecurityDescriptor (%s) sets for secinfo: " + "0x%08x, access_mask: 0x%08x\n", + test, sec_info, access_mask); + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, key, + REG_OPTION_NON_VOLATILE, + access_mask, + &new_handle, + WERR_OK), + "failed to open key"); + + if (!_test_SetKeySecurity(p, tctx, &new_handle, &sec_info, + sd, + set_werr)) { + torture_warning(tctx, + "SetKeySecurity with secinfo: 0x%08x has failed\n", + sec_info); + smb_panic(""); + test_CloseKey(b, tctx, &new_handle); + return false; + } + + test_CloseKey(b, tctx, &new_handle); + + if (W_ERROR_IS_OK(set_werr)) { + bool present; + present = fn(p, tctx, handle, key, sid); + if ((expect_present) && (!present)) { + torture_warning(tctx, + "%s sid is not present!\n", + test); + return false; + } + if ((!expect_present) && (present)) { + torture_warning(tctx, + "%s sid is present but not expected!\n", + test); + return false; + } + } + + return true; +} + +static bool test_SecurityDescriptorsSecInfo(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + struct security_descriptor *sd_orig = NULL; + struct dom_sid *sid = NULL; + bool ret = true; + int i, a; + + struct security_descriptor *sd_owner = + security_descriptor_dacl_create(tctx, + 0, + TEST_SID, NULL, NULL); + + struct security_descriptor *sd_group = + security_descriptor_dacl_create(tctx, + 0, + NULL, TEST_SID, NULL); + + struct security_descriptor *sd_dacl = + security_descriptor_dacl_create(tctx, + 0, + NULL, NULL, + TEST_SID, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + NULL); + + struct security_descriptor *sd_sacl = + security_descriptor_sacl_create(tctx, + 0, + NULL, NULL, + TEST_SID, + SEC_ACE_TYPE_SYSTEM_AUDIT, + SEC_GENERIC_ALL, + SEC_ACE_FLAG_SUCCESSFUL_ACCESS, + NULL); + + struct winreg_secinfo_table { + struct security_descriptor *sd; + uint32_t sec_info; + WERROR set_werr; + bool sid_present; + secinfo_verify_fn fn; + }; + + struct winreg_secinfo_table sec_info_owner_tests[] = { + { + .sd = sd_owner, + .sec_info = 0, + .set_werr = WERR_OK, + .sid_present = false, + .fn = (secinfo_verify_fn)_test_owner_present, + }, + { + .sd = sd_owner, + .sec_info = SECINFO_OWNER, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_owner_present, + }, + { + .sd = sd_owner, + .sec_info = SECINFO_GROUP, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_owner, + .sec_info = SECINFO_DACL, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_owner_present, + }, + { + .sd = sd_owner, + .sec_info = SECINFO_SACL, + .set_werr = WERR_ACCESS_DENIED, + .sid_present = false, + }, + }; + + uint32_t sd_owner_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + /* SEC_STD_WRITE_OWNER, */ + }; + + struct winreg_secinfo_table sec_info_group_tests[] = { + { + .sd = sd_group, + .sec_info = 0, + .set_werr = WERR_OK, + .sid_present = false, + .fn = (secinfo_verify_fn)_test_group_present, + }, + { + .sd = sd_group, + .sec_info = SECINFO_OWNER, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_group, + .sec_info = SECINFO_GROUP, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_group_present, + }, + { + .sd = sd_group, + .sec_info = SECINFO_DACL, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_group_present, + }, + { + .sd = sd_group, + .sec_info = SECINFO_SACL, + .set_werr = WERR_ACCESS_DENIED, + .sid_present = false, + }, + }; + + uint32_t sd_group_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + }; + + struct winreg_secinfo_table sec_info_dacl_tests[] = { + { + .sd = sd_dacl, + .sec_info = 0, + .set_werr = WERR_OK, + .sid_present = false, + .fn = (secinfo_verify_fn)_test_dacl_trustee_present, + }, + { + .sd = sd_dacl, + .sec_info = SECINFO_OWNER, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_dacl, + .sec_info = SECINFO_GROUP, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_dacl, + .sec_info = SECINFO_DACL, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_dacl_trustee_present + }, + { + .sd = sd_dacl, + .sec_info = SECINFO_SACL, + .set_werr = WERR_ACCESS_DENIED, + .sid_present = false, + }, + }; + + uint32_t sd_dacl_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED, + SEC_STD_WRITE_DAC, + }; + + struct winreg_secinfo_table sec_info_sacl_tests[] = { + { + .sd = sd_sacl, + .sec_info = 0, + .set_werr = WERR_OK, + .sid_present = false, + .fn = (secinfo_verify_fn)_test_sacl_trustee_present, + }, + { + .sd = sd_sacl, + .sec_info = SECINFO_OWNER, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_sacl, + .sec_info = SECINFO_GROUP, + .set_werr = WERR_INVALID_PARAMETER, + .sid_present = false, + }, + { + .sd = sd_sacl, + .sec_info = SECINFO_DACL, + .set_werr = WERR_OK, + .sid_present = false, + .fn = (secinfo_verify_fn)_test_sacl_trustee_present, + }, + { + .sd = sd_sacl, + .sec_info = SECINFO_SACL, + .set_werr = WERR_OK, + .sid_present = true, + .fn = (secinfo_verify_fn)_test_sacl_trustee_present, + }, + }; + + uint32_t sd_sacl_good_access_masks[] = { + SEC_FLAG_MAXIMUM_ALLOWED | SEC_FLAG_SYSTEM_SECURITY, + /* SEC_FLAG_SYSTEM_SECURITY, */ + }; + + sid = dom_sid_parse_talloc(tctx, TEST_SID); + if (sid == NULL) { + return false; + } + + if (!test_BackupSecurity(p, tctx, handle, key, &sd_orig)) { + return false; + } + + /* OWNER */ + + for (i=0; i < ARRAY_SIZE(sec_info_owner_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_owner_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "OWNER", + sd_owner_good_access_masks[a], + sec_info_owner_tests[i].sec_info, + sec_info_owner_tests[i].sd, + sec_info_owner_tests[i].set_werr, + sec_info_owner_tests[i].sid_present, + sec_info_owner_tests[i].fn, + sid)) + { + torture_comment(tctx, "test_SetSecurityDescriptor_SecInfo failed for OWNER\n"); + ret = false; + goto out; + } + } + } + + /* GROUP */ + + for (i=0; i < ARRAY_SIZE(sec_info_group_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_group_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "GROUP", + sd_group_good_access_masks[a], + sec_info_group_tests[i].sec_info, + sec_info_group_tests[i].sd, + sec_info_group_tests[i].set_werr, + sec_info_group_tests[i].sid_present, + sec_info_group_tests[i].fn, + sid)) + { + torture_comment(tctx, "test_SetSecurityDescriptor_SecInfo failed for GROUP\n"); + ret = false; + goto out; + } + } + } + + /* DACL */ + + for (i=0; i < ARRAY_SIZE(sec_info_dacl_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_dacl_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "DACL", + sd_dacl_good_access_masks[a], + sec_info_dacl_tests[i].sec_info, + sec_info_dacl_tests[i].sd, + sec_info_dacl_tests[i].set_werr, + sec_info_dacl_tests[i].sid_present, + sec_info_dacl_tests[i].fn, + sid)) + { + torture_comment(tctx, "test_SetSecurityDescriptor_SecInfo failed for DACL\n"); + ret = false; + goto out; + } + } + } + + /* SACL */ + + for (i=0; i < ARRAY_SIZE(sec_info_sacl_tests); i++) { + + for (a=0; a < ARRAY_SIZE(sd_sacl_good_access_masks); a++) { + + if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle, + key, + "SACL", + sd_sacl_good_access_masks[a], + sec_info_sacl_tests[i].sec_info, + sec_info_sacl_tests[i].sd, + sec_info_sacl_tests[i].set_werr, + sec_info_sacl_tests[i].sid_present, + sec_info_sacl_tests[i].fn, + sid)) + { + torture_comment(tctx, "test_SetSecurityDescriptor_SecInfo failed for SACL\n"); + ret = false; + goto out; + } + } + } + + out: + test_RestoreSecurity(p, tctx, handle, key, sd_orig); + + return ret; +} + +static bool test_SecurityDescriptors(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key) +{ + bool ret = true; + + if (!test_SecurityDescriptor(p, tctx, handle, key)) { + torture_comment(tctx, "test_SecurityDescriptor failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorInheritance(p, tctx, handle, key)) { + torture_comment(tctx, "test_SecurityDescriptorInheritance failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorBlockInheritance(p, tctx, handle, key)) { + torture_comment(tctx, "test_SecurityDescriptorBlockInheritance failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorsSecInfo(p, tctx, handle, key)) { + torture_comment(tctx, "test_SecurityDescriptorsSecInfo failed\n"); + ret = false; + } + + if (!test_SecurityDescriptorsMasks(p, tctx, handle, key)) { + torture_comment(tctx, "test_SecurityDescriptorsMasks failed\n"); + ret = false; + } + + return ret; +} + +static bool test_DeleteKey_opts(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *key, + WERROR expected_result) +{ + struct winreg_DeleteKey r; + + torture_comment(tctx, "Testing DeleteKey(%s)\n", key); + + r.in.handle = handle; + init_winreg_String(&r.in.key, key); + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_DeleteKey_r(b, tctx, &r), + "Delete Key failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_result, + "DeleteKey failed"); + + return true; +} + +static bool test_DeleteKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, const char *key) +{ + return test_DeleteKey_opts(b, tctx, handle, key, WERR_OK); +} + +static bool test_QueryInfoKey(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + char *kclass, + uint32_t *pmax_valnamelen, + uint32_t *pmax_valbufsize) +{ + struct winreg_QueryInfoKey r; + uint32_t num_subkeys, max_subkeylen, max_classlen, + num_values, max_valnamelen, max_valbufsize, + secdescsize; + NTTIME last_changed_time; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.out.num_subkeys = &num_subkeys; + r.out.max_subkeylen = &max_subkeylen; + r.out.max_classlen = &max_classlen; + r.out.num_values = &num_values; + r.out.max_valnamelen = &max_valnamelen; + r.out.max_valbufsize = &max_valbufsize; + r.out.secdescsize = &secdescsize; + r.out.last_changed_time = &last_changed_time; + + r.out.classname = talloc(tctx, struct winreg_String); + + r.in.classname = talloc(tctx, struct winreg_String); + init_winreg_String(r.in.classname, kclass); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryInfoKey_r(b, tctx, &r), + "QueryInfoKey failed"); + + torture_assert_werr_ok(tctx, r.out.result, "QueryInfoKey failed"); + + if (pmax_valnamelen) { + *pmax_valnamelen = max_valnamelen; + } + + if (pmax_valbufsize) { + *pmax_valbufsize = max_valbufsize; + } + + return true; +} + +static bool test_SetValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *value_name, + enum winreg_Type type, + uint8_t *data, + uint32_t size) +{ + struct winreg_SetValue r; + struct winreg_String name; + + torture_comment(tctx, "Testing SetValue(%s), type: %s, offered: 0x%08x)\n", + value_name, str_regtype(type), size); + + init_winreg_String(&name, value_name); + + r.in.handle = handle; + r.in.name = name; + r.in.type = type; + r.in.data = data; + r.in.size = size; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_SetValue_r(b, tctx, &r), + "winreg_SetValue failed"); + torture_assert_werr_ok(tctx, r.out.result, + "winreg_SetValue failed"); + + return true; +} + +static bool test_DeleteValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *value_name) +{ + struct winreg_DeleteValue r; + struct winreg_String value; + + torture_comment(tctx, "Testing DeleteValue(%s)\n", value_name); + + init_winreg_String(&value, value_name); + + r.in.handle = handle; + r.in.value = value; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_DeleteValue_r(b, tctx, &r), + "winreg_DeleteValue failed"); + torture_assert_werr_ok(tctx, r.out.result, + "winreg_DeleteValue failed"); + + return true; +} + +static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security); + +static bool test_EnumKey(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security) +{ + struct winreg_EnumKey r; + struct winreg_StringBuf kclass, name; + NTSTATUS status; + NTTIME t = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + kclass.name = ""; + kclass.size = 1024; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.in.keyclass = &kclass; + r.out.name = &name; + r.in.last_changed_time = &t; + + do { + name.name = NULL; + name.size = 1024; + + status = dcerpc_winreg_EnumKey_r(b, tctx, &r); + + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) { + struct policy_handle key_handle; + + torture_comment(tctx, "EnumKey: %d: %s\n", + r.in.enum_index, + r.out.name->name); + + if (!test_OpenKey(b, tctx, handle, r.out.name->name, + &key_handle)) { + } else { + test_key(p, tctx, &key_handle, + depth + 1, test_security); + } + } + + r.in.enum_index++; + + } while (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)); + + torture_assert_ntstatus_ok(tctx, status, "EnumKey failed"); + + if (!W_ERROR_IS_OK(r.out.result) && + !W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) { + torture_fail(tctx, "EnumKey failed"); + } + + return true; +} + +static bool test_QueryMultipleValues(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename) +{ + struct winreg_QueryMultipleValues r; + uint32_t bufsize=0; + + ZERO_STRUCT(r); + + r.in.key_handle = handle; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 1); + r.in.values_in[0].ve_valuename = talloc(tctx, struct winreg_ValNameBuf); + r.in.values_in[0].ve_valuename->name = valuename; + /* size needs to be set manually for winreg_ValNameBuf */ + r.in.values_in[0].ve_valuename->size = strlen_m_term(valuename)*2; + + r.in.num_values = 1; + r.in.buffer_size = r.out.buffer_size = talloc(tctx, uint32_t); + *r.in.buffer_size = bufsize; + do { + *r.in.buffer_size = bufsize; + r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, + *r.in.buffer_size); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r), + "QueryMultipleValues failed"); + + talloc_free(r.in.buffer); + bufsize += 0x20; + } while (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)); + + torture_assert_werr_ok(tctx, r.out.result, "QueryMultipleValues failed"); + + return true; +} + +static bool test_QueryMultipleValues_full(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t num_values, + const char * const *valuenames, + bool existing_value) +{ + struct winreg_QueryMultipleValues r; + uint32_t bufsize = 0; + int i; + + torture_comment(tctx, "Testing QueryMultipleValues\n"); + + ZERO_STRUCT(r); + + r.in.key_handle = handle; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 0); + r.in.buffer_size = r.out.buffer_size = &bufsize; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r), + "QueryMultipleValues failed"); + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues failed"); + + /* this test crashes w2k8 remote registry */ +#if 0 + r.in.num_values = num_values; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r), + "QueryMultipleValues failed"); + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues failed"); +#endif + r.in.num_values = num_values; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values); + for (i=0; i < r.in.num_values; i++) { + r.in.values_in[i].ve_valuename = talloc_zero(tctx, struct winreg_ValNameBuf); + r.in.values_in[i].ve_valuename->name = talloc_strdup(tctx, valuenames[i]); + r.in.values_in[i].ve_valuename->size = strlen_m_term(r.in.values_in[i].ve_valuename->name)*2; + } + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r), + "QueryMultipleValues failed"); + torture_assert_werr_equal(tctx, r.out.result, existing_value ? WERR_MORE_DATA : WERR_FILE_NOT_FOUND, + "QueryMultipleValues failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_FILE_NOT_FOUND)) { + return true; + } + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + *r.in.buffer_size = 0xff; + r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.buffer_size); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r), + "QueryMultipleValues failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues failed"); + + return true; +} + + +static bool test_QueryMultipleValues2_full(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + uint32_t num_values, + const char * const *valuenames, + bool existing_value) +{ + struct winreg_QueryMultipleValues2 r; + uint32_t offered = 0, needed; + int i; + + torture_comment(tctx, "Testing QueryMultipleValues2\n"); + + ZERO_STRUCT(r); + + r.in.key_handle = handle; + r.in.offered = &offered; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 0); + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues2 failed"); + + /* this test crashes w2k8 remote registry */ +#if 0 + r.in.num_values = num_values; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues2 failed"); +#endif + r.in.num_values = num_values; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values); + for (i=0; i < r.in.num_values; i++) { + r.in.values_in[i].ve_valuename = talloc_zero(tctx, struct winreg_ValNameBuf); + r.in.values_in[i].ve_valuename->name = talloc_strdup(tctx, valuenames[i]); + r.in.values_in[i].ve_valuename->size = strlen_m_term(r.in.values_in[i].ve_valuename->name)*2; + } + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + torture_assert_werr_equal(tctx, r.out.result, existing_value ? WERR_MORE_DATA : WERR_FILE_NOT_FOUND, + "QueryMultipleValues2 failed"); + + if (W_ERROR_EQUAL(r.out.result, WERR_FILE_NOT_FOUND)) { + return true; + } + + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + *r.in.offered = *r.out.needed; + r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.offered); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues2 failed"); + + return true; +} + +static bool test_QueryMultipleValues2(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename) +{ + struct winreg_QueryMultipleValues2 r; + uint32_t offered = 0, needed; + + ZERO_STRUCT(r); + + r.in.key_handle = handle; + r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 1); + r.in.values_in[0].ve_valuename = talloc(tctx, struct winreg_ValNameBuf); + r.in.values_in[0].ve_valuename->name = valuename; + /* size needs to be set manually for winreg_ValNameBuf */ + r.in.values_in[0].ve_valuename->size = strlen_m_term(valuename)*2; + + r.in.num_values = 1; + r.in.offered = &offered; + r.out.needed = &needed; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { + *r.in.offered = *r.out.needed; + r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.offered); + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r), + "QueryMultipleValues2 failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, + "QueryMultipleValues2 failed"); + + return true; +} + +static bool test_QueryValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename) +{ + struct winreg_QueryValue r; + NTSTATUS status; + enum winreg_Type zero_type = 0; + uint32_t offered = 0xfff; + uint32_t zero = 0; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.in.data = NULL; + r.in.value_name = talloc_zero(tctx, struct winreg_String); + r.in.value_name->name = valuename; + r.in.type = &zero_type; + r.in.data_size = &offered; + r.in.data_length = &zero; + + status = dcerpc_winreg_QueryValue_r(b, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_fail(tctx, "QueryValue failed"); + } + + torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed"); + + return true; +} + +static bool test_QueryValue_full(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + const char *valuename, + bool existing_value) +{ + struct winreg_QueryValue r; + struct winreg_String value_name; + enum winreg_Type type = REG_NONE; + uint32_t data_size = 0; + uint32_t real_data_size = 0; + uint32_t data_length = 0; + uint8_t *data = NULL; + WERROR expected_error = WERR_FILE_NOT_FOUND; + const char *errmsg_nonexisting = "expected WERR_FILE_NOT_FOUND for nonexisting value"; + + if (valuename == NULL) { + expected_error = WERR_INVALID_PARAMETER; + errmsg_nonexisting = "expected WERR_INVALID_PARAMETER for NULL valuename"; + } + + ZERO_STRUCT(r); + + init_winreg_String(&value_name, NULL); + + torture_comment(tctx, "Testing QueryValue(%s)\n", valuename); + + r.in.handle = handle; + r.in.value_name = &value_name; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "expected WERR_INVALID_PARAMETER for NULL winreg_String.name"); + + init_winreg_String(&value_name, valuename); + r.in.value_name = &value_name; + + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "expected WERR_INVALID_PARAMETER for missing type length and size"); + + r.in.type = &type; + r.out.type = &type; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "expected WERR_INVALID_PARAMETER for missing length and size"); + + r.in.data_length = &data_length; + r.out.data_length = &data_length; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, + "expected WERR_INVALID_PARAMETER for missing size"); + + r.in.data_size = &data_size; + r.out.data_size = &data_size; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + if (existing_value) { + torture_assert_werr_ok(tctx, r.out.result, + "QueryValue failed"); + } else { + torture_assert_werr_equal(tctx, r.out.result, expected_error, + errmsg_nonexisting); + } + + real_data_size = *r.out.data_size; + + data = talloc_zero_array(tctx, uint8_t, 0); + r.in.data = data; + r.out.data = data; + *r.in.data_size = 0; + *r.out.data_size = 0; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + if (existing_value) { + torture_assert_werr_equal(tctx, r.out.result, WERR_MORE_DATA, + "expected WERR_MORE_DATA for query with too small buffer"); + } else { + torture_assert_werr_equal(tctx, r.out.result, expected_error, + errmsg_nonexisting); + } + + data = talloc_zero_array(tctx, uint8_t, real_data_size); + r.in.data = data; + r.out.data = data; + r.in.data_size = &real_data_size; + r.out.data_size = &real_data_size; + torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), + "QueryValue failed"); + if (existing_value) { + torture_assert_werr_ok(tctx, r.out.result, + "QueryValue failed"); + } else { + torture_assert_werr_equal(tctx, r.out.result, expected_error, + errmsg_nonexisting); + } + + return true; +} + +static bool test_EnumValue_one(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, + int max_valnamelen) +{ + struct winreg_EnumValue r; + bool ret = true; + DATA_BLOB blob; + + blob = data_blob_string_const("data_1"); + torture_assert_goto(tctx, + test_SetValue(b, tctx, handle, "v1", REG_BINARY, blob.data, blob.length), + ret, done, + "test_SetValue failed"); + + blob = data_blob_string_const("data_2"); + torture_assert_goto(tctx, + test_SetValue(b, tctx, handle, "v2", REG_BINARY, blob.data, blob.length), + ret, done, + "test_SetValue failed"); + + ZERO_STRUCT(r); + + r.in.handle = handle; + r.in.enum_index = 0; + + do { + enum winreg_Type type = REG_NONE; + uint32_t size = 0, zero = 0; + struct winreg_ValNameBuf name; + char n = '\0'; + + r.in.name = &name; + r.in.type = &type; + r.in.length = &zero; + r.in.size = &size; + r.out.name = &name; + r.out.size = &size; + + name.name = &n; + name.size = max_valnamelen + 2; + name.length = 0; + + r.in.value = talloc_array(tctx, uint8_t, 0); + torture_assert(tctx, r.in.value, "nomem"); + + torture_assert_ntstatus_ok_goto(tctx, + dcerpc_winreg_EnumValue_r(b, tctx, &r), + ret, done, + "EnumValue failed"); + if (W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) { + break; + } + torture_assert_werr_equal_goto(tctx, + r.out.result, WERR_MORE_DATA, + ret, done, + "unexpected return code"); + + *r.in.size = *r.out.size; + r.in.value = talloc_zero_array(tctx, uint8_t, *r.in.size); + torture_assert(tctx, r.in.value, "nomem"); + + torture_assert_ntstatus_ok_goto(tctx, + dcerpc_winreg_EnumValue_r(b, tctx, &r), + ret, done, + "EnumValue failed"); + torture_assert_werr_ok_goto(tctx, r.out.result, + ret, done, + "unexpected return code"); + + r.in.enum_index++; + + } while (W_ERROR_IS_OK(r.out.result)); + + torture_assert_werr_equal_goto(tctx, r.out.result, WERR_NO_MORE_ITEMS, + ret, done, + "EnumValue failed"); + done: + test_DeleteValue(b, tctx, handle, "v1"); + test_DeleteValue(b, tctx, handle, "v2"); + + return ret; +} + +static bool test_EnumValue(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle, int max_valnamelen, + int max_valbufsize) +{ + struct winreg_EnumValue r; + enum winreg_Type type = 0; + uint32_t size = max_valbufsize, zero = 0; + bool ret = true; + uint8_t *data = NULL; + struct winreg_ValNameBuf name; + char n = '\0'; + + ZERO_STRUCT(r); + r.in.handle = handle; + r.in.enum_index = 0; + r.in.name = &name; + r.out.name = &name; + r.in.type = &type; + r.in.length = &zero; + r.in.size = &size; + + do { + name.name = &n; + name.size = max_valnamelen + 2; + name.length = 0; + + data = NULL; + if (size) { + data = talloc_array(tctx, uint8_t, size); + } + r.in.value = data; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_EnumValue_r(b, tctx, &r), + "EnumValue failed"); + + if (W_ERROR_IS_OK(r.out.result)) { + ret &= test_QueryValue(b, tctx, handle, + r.out.name->name); + ret &= test_QueryMultipleValues(b, tctx, handle, + r.out.name->name); + ret &= test_QueryMultipleValues2(b, tctx, handle, + r.out.name->name); + } + + talloc_free(data); + + r.in.enum_index++; + } while (W_ERROR_IS_OK(r.out.result)); + + torture_assert_werr_equal(tctx, r.out.result, WERR_NO_MORE_ITEMS, + "EnumValue failed"); + + return ret; +} + +static bool test_AbortSystemShutdown(struct dcerpc_binding_handle *b, + struct torture_context *tctx) +{ + struct winreg_AbortSystemShutdown r; + uint16_t server = 0x0; + + ZERO_STRUCT(r); + r.in.server = &server; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_AbortSystemShutdown_r(b, tctx, &r), + "AbortSystemShutdown failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "AbortSystemShutdown failed"); + + return true; +} + +static bool test_InitiateSystemShutdown(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct winreg_InitiateSystemShutdown r; + uint16_t hostname = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.do_reboot = 1; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_InitiateSystemShutdown_r(b, tctx, &r), + "InitiateSystemShutdown failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "InitiateSystemShutdown failed"); + + return test_AbortSystemShutdown(b, tctx); +} + + +static bool test_InitiateSystemShutdownEx(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct winreg_InitiateSystemShutdownEx r; + uint16_t hostname = 0x0; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + r.in.hostname = &hostname; + r.in.message = talloc(tctx, struct lsa_StringLarge); + init_lsa_StringLarge(r.in.message, "spottyfood"); + r.in.force_apps = 1; + r.in.timeout = 30; + r.in.do_reboot = 1; + r.in.reason = 0; + + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_InitiateSystemShutdownEx_r(b, tctx, &r), + "InitiateSystemShutdownEx failed"); + + torture_assert_werr_ok(tctx, r.out.result, + "InitiateSystemShutdownEx failed"); + + return test_AbortSystemShutdown(b, tctx); +} +#define MAX_DEPTH 2 /* Only go this far down the tree */ + +static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *handle, int depth, + bool test_security) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + uint32_t max_valnamelen = 0; + uint32_t max_valbufsize = 0; + + if (depth == MAX_DEPTH) + return true; + + if (!test_QueryInfoKey(b, tctx, handle, NULL, + &max_valnamelen, &max_valbufsize)) { + } + + if (!test_NotifyChangeKeyValue(b, tctx, handle)) { + } + + if (test_security && !test_GetKeySecurity(p, tctx, handle, NULL)) { + } + + if (!test_EnumKey(p, tctx, handle, depth, test_security)) { + } + + if (!test_EnumValue(b, tctx, handle, max_valnamelen, max_valbufsize)) { + } + + if (!test_EnumValue(b, tctx, handle, max_valnamelen, 0xFFFF)) { + } + + if (!test_EnumValue_one(b, tctx, handle, 0xff)) { + return false; + } + + test_CloseKey(b, tctx, handle); + + return true; +} + +static bool test_SetValue_simple(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + const char *value_name = TEST_VALUE; + uint32_t value = 0x12345678; + uint64_t value2 = 0x12345678; + const char *string = "torture"; + const char *array[2]; + DATA_BLOB blob; + enum winreg_Type types[] = { + REG_DWORD, + REG_DWORD_BIG_ENDIAN, + REG_QWORD, + REG_BINARY, + REG_SZ, + REG_MULTI_SZ + }; + int t; + + array[0] = "array0"; + array[1] = NULL; + + torture_comment(tctx, "Testing SetValue (standard formats)\n"); + + for (t=0; t < ARRAY_SIZE(types); t++) { + + enum winreg_Type w_type; + uint32_t w_size, w_length; + uint8_t *w_data; + + switch (types[t]) { + case REG_DWORD: + case REG_DWORD_BIG_ENDIAN: + blob = data_blob_talloc_zero(tctx, 4); + SIVAL(blob.data, 0, value); + break; + case REG_QWORD: + blob = data_blob_talloc_zero(tctx, 8); + SBVAL(blob.data, 0, value2); + break; + case REG_BINARY: + blob = data_blob_string_const("binary_blob"); + break; + case REG_SZ: + torture_assert(tctx, push_reg_sz(tctx, &blob, string), "failed to push REG_SZ"); + break; + case REG_MULTI_SZ: + torture_assert(tctx, push_reg_multi_sz(tctx, &blob, array), "failed to push REG_MULTI_SZ"); + break; + default: + break; + } + + torture_assert(tctx, + test_SetValue(b, tctx, handle, value_name, types[t], blob.data, blob.length), + "test_SetValue failed"); + torture_assert(tctx, + test_QueryValue_full(b, tctx, handle, value_name, true), + talloc_asprintf(tctx, "test_QueryValue_full for %s value failed", value_name)); + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, handle, value_name, &w_type, &w_size, &w_length, &w_data), + "test_winreg_QueryValue failed"); + torture_assert(tctx, + test_DeleteValue(b, tctx, handle, value_name), + "test_DeleteValue failed"); + + torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch"); + torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch"); + torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch"); + torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch"); + } + + torture_comment(tctx, "Testing SetValue (standard formats) succeeded\n"); + + return true; +} + +static bool test_SetValue_values(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + DATA_BLOB blob; + const char *values[] = { + "torture_value", + "torture value", + "torture,value", + "torture;value", + "torture/value", + "torture\\value", + "torture_value_name", + "torture value name", + "torture,value,name", + "torture;value;name", + "torture/value/name", + "torture\\value\\name", + }; + int i; + + torture_comment(tctx, "Testing SetValue (values)\n"); + + for (i=0; i < ARRAY_SIZE(values); i++) { + + enum winreg_Type w_type; + uint32_t w_size, w_length; + uint8_t *w_data; + + blob = data_blob_talloc(tctx, NULL, 32); + + generate_random_buffer(blob.data, 32); + + torture_assert(tctx, + test_SetValue(b, tctx, handle, values[i], REG_BINARY, blob.data, blob.length), + "test_SetValue failed"); + torture_assert(tctx, + test_QueryValue_full(b, tctx, handle, values[i], true), + talloc_asprintf(tctx, "test_QueryValue_full for %s value failed", values[i])); + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, handle, values[i], &w_type, &w_size, &w_length, &w_data), + "test_winreg_QueryValue failed"); + torture_assert(tctx, + test_DeleteValue(b, tctx, handle, values[i]), + "test_DeleteValue failed"); + + torture_assert_int_equal(tctx, w_type, REG_BINARY, "winreg type mismatch"); + torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch"); + torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch"); + torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch"); + } + + torture_comment(tctx, "Testing SetValue (values) succeeded\n"); + + return true; +} + +typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_binding_handle *, TALLOC_CTX *, void *); + +static bool test_SetValue_extended(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + const char *value_name = TEST_VALUE; + enum winreg_Type types[] = { + REG_NONE, + REG_SZ, + REG_EXPAND_SZ, + REG_BINARY, + REG_DWORD, + REG_DWORD_BIG_ENDIAN, + REG_LINK, + REG_MULTI_SZ, + REG_RESOURCE_LIST, + REG_FULL_RESOURCE_DESCRIPTOR, + REG_RESOURCE_REQUIREMENTS_LIST, + REG_QWORD, + 12, + 13, + 14, + 55, + 123456, + 653210, + __LINE__ + }; + int t, l; + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "skipping extended SetValue test against Samba4"); + } + + torture_comment(tctx, "Testing SetValue (extended formats)\n"); + + for (t=0; t < ARRAY_SIZE(types); t++) { + for (l=0; l < 16; l++) { + + enum winreg_Type w_type; + uint32_t w_size, w_length; + uint8_t *w_data; + + uint32_t size; + uint8_t *data; + + size = l; + data = talloc_array(tctx, uint8_t, size); + + generate_random_buffer(data, size); + + torture_assert(tctx, + test_SetValue(b, tctx, handle, value_name, types[t], data, size), + "test_SetValue failed"); + + torture_assert(tctx, + test_winreg_QueryValue(tctx, b, handle, value_name, &w_type, &w_size, &w_length, &w_data), + "test_winreg_QueryValue failed"); + + torture_assert(tctx, + test_DeleteValue(b, tctx, handle, value_name), + "test_DeleteValue failed"); + + torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch"); + torture_assert_int_equal(tctx, w_size, size, "winreg size mismatch"); + torture_assert_int_equal(tctx, w_length, size, "winreg length mismatch"); + torture_assert_mem_equal(tctx, w_data, data, size, "winreg buffer mismatch"); + } + } + + torture_comment(tctx, "Testing SetValue (extended formats) succeeded\n"); + + return true; +} + +static bool test_create_keynames(struct dcerpc_binding_handle *b, + struct torture_context *tctx, + struct policy_handle *handle) +{ + const char *keys[] = { + "torture_key", + "torture key", + "torture,key", + "torture/key", + "torture\\key", + }; + int i; + + for (i=0; i < ARRAY_SIZE(keys); i++) { + + enum winreg_CreateAction action_taken; + struct policy_handle new_handle; + char *q, *tmp; + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, handle, keys[i], NULL, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_OK, + &action_taken, + &new_handle), + talloc_asprintf(tctx, "failed to create '%s' key", keys[i])); + + torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action"); + + torture_assert(tctx, + test_DeleteKey_opts(b, tctx, handle, keys[i], WERR_OK), + "failed to delete key"); + + torture_assert(tctx, + test_DeleteKey_opts(b, tctx, handle, keys[i], WERR_FILE_NOT_FOUND), + "failed 2nd delete key"); + + tmp = talloc_strdup(tctx, keys[i]); + + q = strchr(tmp, '\\'); + if (q != NULL) { + *q = '\0'; + q++; + + torture_assert(tctx, + test_DeleteKey_opts(b, tctx, handle, tmp, WERR_OK), + "failed to delete key"); + + torture_assert(tctx, + test_DeleteKey_opts(b, tctx, handle, tmp, WERR_FILE_NOT_FOUND), + "failed 2nd delete key"); + } + } + + return true; +} + +#define KEY_CURRENT_VERSION "SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION" +#define VALUE_CURRENT_VERSION "CurrentVersion" +#define VALUE_SYSTEM_ROOT "SystemRoot" + +static const struct { + const char *values[3]; + uint32_t num_values; + bool existing_value; + const char *error_message; +} multiple_values_tests[] = { + { + .values = { VALUE_CURRENT_VERSION, NULL, NULL }, + .num_values = 1, + .existing_value = true, + .error_message = NULL + },{ + .values = { VALUE_SYSTEM_ROOT, NULL, NULL }, + .num_values = 1, + .existing_value = true, + .error_message = NULL + },{ + .values = { VALUE_CURRENT_VERSION, VALUE_SYSTEM_ROOT, NULL }, + .num_values = 2, + .existing_value = true, + .error_message = NULL + },{ + .values = { VALUE_CURRENT_VERSION, VALUE_SYSTEM_ROOT, + VALUE_CURRENT_VERSION }, + .num_values = 3, + .existing_value = true, + .error_message = NULL + },{ + .values = { VALUE_CURRENT_VERSION, NULL, VALUE_SYSTEM_ROOT }, + .num_values = 3, + .existing_value = false, + .error_message = NULL + },{ + .values = { VALUE_CURRENT_VERSION, "", VALUE_SYSTEM_ROOT }, + .num_values = 3, + .existing_value = false, + .error_message = NULL + },{ + .values = { "IDoNotExist", NULL, NULL }, + .num_values = 1, + .existing_value = false, + .error_message = NULL + },{ + .values = { "IDoNotExist", VALUE_CURRENT_VERSION, NULL }, + .num_values = 2, + .existing_value = false, + .error_message = NULL + },{ + .values = { VALUE_CURRENT_VERSION, "IDoNotExist", NULL }, + .num_values = 2, + .existing_value = false, + .error_message = NULL + } +}; + +static bool test_HKLM_wellknown(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle) +{ + struct policy_handle newhandle; + int i; + + /* FIXME: s3 does not support SEC_FLAG_MAXIMUM_ALLOWED yet */ + if (torture_setting_bool(tctx, "samba3", false)) { + torture_assert(tctx, test_OpenKey_opts(tctx, b, handle, + KEY_CURRENT_VERSION, + REG_OPTION_NON_VOLATILE, + KEY_QUERY_VALUE, + &newhandle, + WERR_OK), + "failed to open current version key"); + } else { + torture_assert(tctx, test_OpenKey(b, tctx, handle, KEY_CURRENT_VERSION, &newhandle), + "failed to open current version key"); + } + + torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, VALUE_CURRENT_VERSION, true), + "failed to query current version"); + torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, "IDoNotExist", false), + "succeeded to query nonexistent value"); + torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, NULL, false), + "succeeded to query value with NULL name"); + torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, "", false), + "succeeded to query nonexistent default value (\"\")"); + + if (torture_setting_bool(tctx, "samba4", false)) { + torture_comment(tctx, "skipping QueryMultipleValues{2} tests against Samba4\n"); + goto close_key; + } + + for (i=0; i < ARRAY_SIZE(multiple_values_tests); i++) { + const char *msg; + msg = talloc_asprintf(tctx, + "failed to query %d %sexisting values\n", + multiple_values_tests[i].num_values, + multiple_values_tests[i].existing_value ? "":"non"); + + torture_assert(tctx, + test_QueryMultipleValues_full(b, tctx, &newhandle, + multiple_values_tests[i].num_values, + multiple_values_tests[i].values, + multiple_values_tests[i].existing_value), + msg); + torture_assert(tctx, + test_QueryMultipleValues2_full(b, tctx, &newhandle, + multiple_values_tests[i].num_values, + multiple_values_tests[i].values, + multiple_values_tests[i].existing_value), + msg); + } + + close_key: + torture_assert(tctx, test_CloseKey(b, tctx, &newhandle), + "failed to close current version key"); + + return true; +} + +static bool test_OpenHive(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + int hkey) +{ + struct winreg_OpenHKLM r; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = handle; + + switch (hkey) { + case HKEY_LOCAL_MACHINE: + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenHKLM_r(b, tctx, &r), + "failed to open HKLM"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to open HKLM"); + break; + case HKEY_CURRENT_USER: + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenHKCU_r(b, tctx, (struct winreg_OpenHKCU *)(void *)&r), + "failed to open HKCU"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to open HKCU"); + break; + case HKEY_USERS: + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenHKU_r(b, tctx, (struct winreg_OpenHKU *)(void *)&r), + "failed to open HKU"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to open HKU"); + break; + case HKEY_CLASSES_ROOT: + torture_assert_ntstatus_ok(tctx, + dcerpc_winreg_OpenHKCR_r(b, tctx, (struct winreg_OpenHKCR *)(void *)&r), + "failed to open HKCR"); + torture_assert_werr_ok(tctx, r.out.result, + "failed to open HKCR"); + break; + default: + torture_warning(tctx, "unsupported hkey: 0x%08x\n", hkey); + return false; + } + + return true; +} + +static bool test_volatile_keys(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + int hkey) +{ + struct policy_handle new_handle, hive_handle; + enum winreg_CreateAction action_taken = REG_ACTION_NONE; + + ZERO_STRUCT(new_handle); + ZERO_STRUCT(hive_handle); + + torture_comment(tctx, "Testing VOLATILE key\n"); + + test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE); + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, NULL, + REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_OK, + &action_taken, + &new_handle), + "failed to create REG_OPTION_VOLATILE type key"); + + torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action"); + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, &new_handle, TEST_SUBKEY_VOLATILE, NULL, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_CHILD_MUST_BE_VOLATILE, + NULL, + NULL), + "failed to fail create REG_OPTION_VOLATILE type key"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_OK), + "failed to open volatile key"); + + torture_assert(tctx, + test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE), + "failed to delete key"); + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, NULL, + REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_OK, + &action_taken, + &new_handle), + "failed to create REG_OPTION_VOLATILE type key"); + + torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, + REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_OK), + "failed to open volatile key"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + torture_assert(tctx, + test_OpenHive(tctx, b, &hive_handle, hkey), + "failed top open hive"); + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, &hive_handle, TEST_KEY_VOLATILE, + REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_FILE_NOT_FOUND), + "failed to open volatile key"); + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, &hive_handle, TEST_KEY_VOLATILE, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_FILE_NOT_FOUND), + "failed to open volatile key"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &hive_handle), + "failed to close"); + + torture_assert(tctx, + test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE), + "failed to delete key"); + + + torture_comment(tctx, "Testing VOLATILE key succeeded\n"); + + return true; +} + +static const char *kernel_mode_registry_path(struct torture_context *tctx, + int hkey, + const char *sid_string, + const char *path) +{ + switch (hkey) { + case HKEY_LOCAL_MACHINE: + return talloc_asprintf(tctx, "\\Registry\\MACHINE\\%s", path); + case HKEY_CURRENT_USER: + return talloc_asprintf(tctx, "\\Registry\\USER\\%s\\%s", sid_string, path); + case HKEY_USERS: + return talloc_asprintf(tctx, "\\Registry\\USER\\%s", path); + case HKEY_CLASSES_ROOT: + return talloc_asprintf(tctx, "\\Registry\\MACHINE\\Software\\Classes\\%s", path); + default: + torture_warning(tctx, "unsupported hkey: 0x%08x\n", hkey); + return NULL; + } +} + +static bool test_symlink_keys(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key, + int hkey) +{ + struct policy_handle new_handle; + enum winreg_CreateAction action_taken; + DATA_BLOB blob; + uint32_t value = 42; + const char *test_key_symlink_dest; + const char *test_key_symlink; + const char *kernel_mode_path; + + /* disable until we know how to delete a symbolic link */ + torture_skip(tctx, "symlink test disabled"); + + torture_comment(tctx, "Testing REG_OPTION_CREATE_LINK key\n"); + + /* create destination key with testvalue */ + test_key_symlink = talloc_asprintf(tctx, "%s\\%s", + key, TEST_KEY_SYMLINK); + test_key_symlink_dest = talloc_asprintf(tctx, "%s\\%s", + key, TEST_KEY_SYMLINK_DEST); + + test_DeleteKey(b, tctx, handle, test_key_symlink); + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, handle, test_key_symlink_dest, NULL, + 0, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_OK, + &action_taken, + &new_handle), + "failed to create symlink destination"); + + blob = data_blob_talloc_zero(tctx, 4); + SIVAL(blob.data, 0, value); + + torture_assert(tctx, + test_SetValue(b, tctx, &new_handle, "TestValue", REG_DWORD, blob.data, blob.length), + "failed to create TestValue"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + /* create symlink */ + + torture_assert(tctx, + test_CreateKey_opts(tctx, b, handle, test_key_symlink, NULL, + REG_OPTION_CREATE_LINK | REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + NULL, + WERR_OK, + &action_taken, + &new_handle), + "failed to create REG_OPTION_CREATE_LINK type key"); + + torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action"); + + kernel_mode_path = kernel_mode_registry_path(tctx, hkey, NULL, test_key_symlink_dest); + + torture_assert(tctx, + convert_string_talloc(tctx, CH_UNIX, CH_UTF16, + kernel_mode_path, + strlen(kernel_mode_path), /* not NULL terminated */ + &blob.data, &blob.length), + "failed to convert"); + + torture_assert(tctx, + test_SetValue(b, tctx, &new_handle, "SymbolicLinkValue", REG_LINK, blob.data, blob.length), + "failed to create SymbolicLinkValue value"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + /* test follow symlink */ + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, test_key_symlink, + 0, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_OK), + "failed to follow symlink key"); + + torture_assert(tctx, + test_QueryValue(b, tctx, &new_handle, "TestValue"), + "failed to query value"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + /* delete link */ + + torture_assert(tctx, + test_OpenKey_opts(tctx, b, handle, test_key_symlink, + REG_OPTION_OPEN_LINK | REG_OPTION_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &new_handle, + WERR_OK), + "failed to open symlink key"); + + torture_assert(tctx, + test_DeleteValue(b, tctx, &new_handle, "SymbolicLinkValue"), + "failed to delete value SymbolicLinkValue"); + + torture_assert(tctx, + test_CloseKey(b, tctx, &new_handle), + "failed to close"); + + torture_assert(tctx, + test_DeleteKey(b, tctx, handle, test_key_symlink), + "failed to delete key"); + + /* delete destination */ + + torture_assert(tctx, + test_DeleteKey(b, tctx, handle, test_key_symlink_dest), + "failed to delete key"); + + return true; +} + +static bool test_CreateKey_keytypes(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *key, + int hkey) +{ + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "skipping CreateKey keytypes test against Samba"); + } + + torture_assert(tctx, + test_volatile_keys(tctx, b, handle, hkey), + "failed to test volatile keys"); + + torture_assert(tctx, + test_symlink_keys(tctx, b, handle, key, hkey), + "failed to test symlink keys"); + + return true; +} + +static bool test_key_base(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + struct policy_handle *handle, + const char *base_key, + int hkey) +{ + struct policy_handle newhandle; + bool ret = true, created = false, deleted = false; + bool created3 = false; + const char *test_key1; + const char *test_key3; + const char *test_subkey; + + test_Cleanup(b, tctx, handle, base_key); + + if (!test_CreateKey(b, tctx, handle, base_key, NULL)) { + torture_comment(tctx, + "CreateKey(%s) failed\n", base_key); + } + + test_key1 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY1); + + if (!test_CreateKey(b, tctx, handle, test_key1, NULL)) { + torture_comment(tctx, + "CreateKey failed - not considering a failure\n"); + } else { + created = true; + } + + if (created) { + if (!test_FlushKey(b, tctx, handle)) { + torture_comment(tctx, "FlushKey failed\n"); + ret = false; + } + + if (!test_OpenKey(b, tctx, handle, test_key1, &newhandle)) { + torture_fail(tctx, + "CreateKey failed (OpenKey after Create didn't work)\n"); + } + + if (hkey == HKEY_CURRENT_USER) { + torture_assert(tctx, test_SetValue_simple(b, tctx, &newhandle), + "simple SetValue test failed"); + torture_assert(tctx, test_SetValue_values(b, tctx, &newhandle), + "values SetValue test failed"); + torture_assert(tctx, test_SetValue_extended(b, tctx, &newhandle), + "extended SetValue test failed"); + torture_assert(tctx, test_create_keynames(b, tctx, &newhandle), + "keyname CreateKey test failed"); + } else { + torture_assert(tctx, test_CreateKey_keytypes(tctx, b, &newhandle, test_key1, hkey), + "keytype test failed"); + torture_assert(tctx, test_EnumValue_one(b, tctx, &newhandle, 0xff), + "simple EnumValue test failed"); + } + + if (!test_CloseKey(b, tctx, &newhandle)) { + torture_fail(tctx, + "CreateKey failed (CloseKey after Open didn't work)\n"); + } + + if (!test_DeleteKey(b, tctx, handle, test_key1)) { + torture_comment(tctx, "DeleteKey(%s) failed\n", + test_key1); + ret = false; + } else { + deleted = true; + } + + if (!test_FlushKey(b, tctx, handle)) { + torture_comment(tctx, "FlushKey failed\n"); + ret = false; + } + + if (deleted) { + if (!test_OpenKey_opts(tctx, b, handle, test_key1, + REG_OPTION_NON_VOLATILE, + SEC_FLAG_MAXIMUM_ALLOWED, + &newhandle, + WERR_FILE_NOT_FOUND)) { + torture_comment(tctx, + "DeleteKey failed (OpenKey after Delete " + "did not return WERR_FILE_NOT_FOUND)\n"); + ret = false; + } + } + + test_key3 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY3); + + if (test_CreateKey(b, tctx, handle, test_key3, NULL)) { + created3 = true; + } + + test_subkey = talloc_asprintf(tctx, "%s\\%s", test_key3, TEST_SUBKEY); + + if (created3) { + if (test_CreateKey(b, tctx, handle, test_subkey, NULL)) { + if (!test_DeleteKey(b, tctx, handle, test_subkey)) { + torture_comment(tctx, "DeleteKey(%s) failed\n", test_subkey); + ret = false; + } + } + + if (!test_DeleteKey(b, tctx, handle, test_key3)) { + torture_comment(tctx, "DeleteKey(%s) failed\n", test_key3); + ret = false; + } + } + } + + test_Cleanup(b, tctx, handle, base_key); + + return ret; +} + +static bool test_key_base_sd(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *handle, + const char *base_key) +{ + struct policy_handle newhandle; + bool ret = true, created2 = false, created4 = false; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *test_key2; + const char *test_key4; + + torture_skip(tctx, "security descriptor test disabled\n"); + + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "skipping security descriptor tests against Samba"); + } + + test_Cleanup(b, tctx, handle, base_key); + + if (!test_CreateKey(b, tctx, handle, base_key, NULL)) { + torture_comment(tctx, + "CreateKey(%s) failed\n", base_key); + } + + test_key2 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY2); + + if (test_CreateKey_sd(b, tctx, handle, test_key2, + NULL, &newhandle)) { + created2 = true; + } + + if (created2 && !test_CloseKey(b, tctx, &newhandle)) { + torture_comment(tctx, "CloseKey failed\n"); + ret = false; + } + + test_key4 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY4); + + if (test_CreateKey_sd(b, tctx, handle, test_key4, NULL, &newhandle)) { + created4 = true; + } + + if (created4 && !test_CloseKey(b, tctx, &newhandle)) { + torture_comment(tctx, "CloseKey failed\n"); + ret = false; + } + + if (created4 && !test_SecurityDescriptors(p, tctx, handle, test_key4)) { + ret = false; + } + + if (created4 && !test_DeleteKey(b, tctx, handle, test_key4)) { + torture_comment(tctx, "DeleteKey(%s) failed\n", test_key4); + ret = false; + } + + if (created2 && !test_DeleteKey(b, tctx, handle, test_key4)) { + torture_comment(tctx, "DeleteKey(%s) failed\n", test_key4); + ret = false; + } + + test_Cleanup(b, tctx, handle, base_key); + + return ret; +} + +static bool test_Open(struct torture_context *tctx, struct dcerpc_pipe *p, + void *userdata) +{ + struct policy_handle handle; + bool ret = true; + struct winreg_OpenHKLM r; + struct dcerpc_binding_handle *b = p->binding_handle; + const char *torture_base_key; + int hkey = 0; + + winreg_open_fn open_fn = (winreg_open_fn)userdata; + + r.in.system_name = 0; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &handle; + + torture_assert_ntstatus_ok(tctx, open_fn(b, tctx, &r), + "open"); + + if (!test_GetVersion(b, tctx, &handle)) { + torture_comment(tctx, "GetVersion failed\n"); + ret = false; + } + + if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKLM_r) { + hkey = HKEY_LOCAL_MACHINE; + torture_base_key = "SOFTWARE\\Samba\\" TEST_KEY_BASE; + } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKU_r) { + hkey = HKEY_USERS; + torture_base_key = TEST_KEY_BASE; + } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKCR_r) { + hkey = HKEY_CLASSES_ROOT; + torture_base_key = TEST_KEY_BASE; + } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKCU_r) { + hkey = HKEY_CURRENT_USER; + torture_base_key = TEST_KEY_BASE; + } else { + torture_fail(tctx, "unsupported hkey"); + } + + if (hkey == HKEY_LOCAL_MACHINE) { + torture_assert(tctx, + test_HKLM_wellknown(tctx, b, &handle), + "failed to test HKLM wellknown keys"); + } + + if (!test_key_base(tctx, b, &handle, torture_base_key, hkey)) { + torture_warning(tctx, "failed to test TEST_KEY_BASE(%s)", + torture_base_key); + ret = false; + } + + if (!test_key_base_sd(tctx, p, &handle, torture_base_key)) { + torture_warning(tctx, "failed to test TEST_KEY_BASE(%s) sd", + torture_base_key); + ret = false; + } + + /* The HKCR hive has a very large fanout */ + if (hkey == HKEY_CLASSES_ROOT) { + if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, false)) { + ret = false; + } + } else if (hkey == HKEY_LOCAL_MACHINE) { + /* FIXME we are not allowed to enum values in the HKLM root */ + } else { + if (!test_key(p, tctx, &handle, 0, false)) { + ret = false; + } + } + + return ret; +} + +struct torture_suite *torture_rpc_winreg(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "winreg"); + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "winreg", + &ndr_table_winreg); + + test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdown", + test_InitiateSystemShutdown); + test->dangerous = true; + + test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdownEx", + test_InitiateSystemShutdownEx); + test->dangerous = true; + + torture_rpc_tcase_add_test_ex(tcase, "HKLM", + test_Open, + (void *)dcerpc_winreg_OpenHKLM_r); + torture_rpc_tcase_add_test_ex(tcase, "HKU", + test_Open, + (void *)dcerpc_winreg_OpenHKU_r); + torture_rpc_tcase_add_test_ex(tcase, "HKCR", + test_Open, + (void *)dcerpc_winreg_OpenHKCR_r); + torture_rpc_tcase_add_test_ex(tcase, "HKCU", + test_Open, + (void *)dcerpc_winreg_OpenHKCU_r); + + return suite; +} diff --git a/source4/torture/rpc/witness.c b/source4/torture/rpc/witness.c new file mode 100644 index 0000000..602e8ca --- /dev/null +++ b/source4/torture/rpc/witness.c @@ -0,0 +1,911 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc witness operations + + Copyright (C) Guenther Deschner 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "torture/rpc/torture_rpc.h" +#include "librpc/gen_ndr/ndr_witness_c.h" +#include "librpc/gen_ndr/ndr_srvsvc_c.h" +#include "librpc/gen_ndr/ndr_clusapi_c.h" +#include "param/param.h" +#include +#include "lib/cmdline/cmdline.h" + +struct torture_test_clusapi_state { + struct dcerpc_pipe *p; +}; + +struct torture_test_witness_state { + const char *net_name; + const char *share_name; + struct witness_interfaceList *list; + struct policy_handle context_handle; + struct torture_test_clusapi_state clusapi; +}; + +static bool test_witness_GetInterfaceList(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_GetInterfaceList r; + struct witness_interfaceList *l; + struct torture_test_witness_state *state = + (struct torture_test_witness_state *)data; + + r.out.interface_list = &l; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_GetInterfaceList_r(b, tctx, &r), + "GetInterfaceList failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "GetInterfaceList failed"); + + state->list = l; + + return true; +} + +static bool find_sofs_share(struct torture_context *tctx, + const char **sofs_sharename) +{ + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *b; + struct srvsvc_NetShareEnumAll r; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr1 ctr1; + uint32_t resume_handle = 0; + uint32_t totalentries = 0; + int i; + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection_transport(tctx, &p, &ndr_table_srvsvc, + NCACN_NP, 0, 0), + "failed to setup srvsvc connection"); + + b = p->binding_handle; + + ZERO_STRUCT(ctr1); + + info_ctr.level = 1; + info_ctr.ctr.ctr1 = &ctr1; + + r.in.server_unc = dcerpc_server_name(p); + r.in.max_buffer = -1; + r.in.info_ctr = &info_ctr; + r.in.resume_handle = &resume_handle; + r.out.totalentries = &totalentries; + r.out.info_ctr = &info_ctr; + r.out.resume_handle = &resume_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_srvsvc_NetShareEnumAll_r(b, tctx, &r), + "failed to call srvsvc_NetShareEnumAll"); + + torture_assert_werr_ok(tctx, + r.out.result, + "failed to call srvsvc_NetShareEnumAll"); + + for (i=0; i < r.out.info_ctr->ctr.ctr1->count; i++) { + + if (r.out.info_ctr->ctr.ctr1->array[i].type == STYPE_CLUSTER_SOFS) { + *sofs_sharename = talloc_strdup(tctx, r.out.info_ctr->ctr.ctr1->array[i].name); + if (*sofs_sharename == NULL) { + return false; + } + torture_comment(tctx, "using SOFS share: %s\n", *sofs_sharename); + return true; + } + if (r.out.info_ctr->ctr.ctr1->array[i].type == STYPE_DISKTREE) { + *sofs_sharename = talloc_strdup(tctx, r.out.info_ctr->ctr.ctr1->array[i].name); + if (*sofs_sharename == NULL) { + return false; + } + torture_comment(tctx, "assuming SOFS share: %s\n", *sofs_sharename); + return true; + } + } + + return false; +} + +static bool init_witness_test_state(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct torture_test_witness_state *state) +{ + if (state->net_name == NULL) { + state->net_name = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "net_name"); + } + + if (state->list == NULL) { + torture_assert(tctx, + test_witness_GetInterfaceList(tctx, p, state), + "failed to retrieve GetInterfaceList"); + } + + if (state->share_name == NULL) { + find_sofs_share(tctx, &state->share_name); + } + + return true; +} + +static bool test_witness_UnRegister_with_handle(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *context_handle) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_UnRegister r; + + r.in.context_handle = *context_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_UnRegister_r(b, tctx, &r), + "UnRegister failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "UnRegister failed"); + + /* make sure we are not able/allowed to reuse context handles after they + * have been unregistered */ + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_UnRegister_r(b, tctx, &r), + "UnRegister failed"); + + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAMETER, + "UnRegister failed"); + + return true; +} + +static bool test_witness_UnRegister(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + /* acquire handle and free afterwards */ + return true; +} + +static bool get_ip_address_from_interface(struct torture_context *tctx, + struct witness_interfaceInfo *i, + const char **ip_address) +{ + if (i->flags & WITNESS_INFO_IPv4_VALID) { + *ip_address = talloc_strdup(tctx, i->ipv4); + torture_assert(tctx, *ip_address, "talloc_strdup failed"); + return true; + } + + if (i->flags & WITNESS_INFO_IPv6_VALID) { + *ip_address = talloc_strdup(tctx, i->ipv6); + torture_assert(tctx, *ip_address, "talloc_strdup failed"); + return true; + } + + return false; +} + +static bool check_valid_interface(struct torture_context *tctx, + struct witness_interfaceInfo *i) +{ + /* continue looking for an interface that allows witness + * registration */ + if (!(i->flags & WITNESS_INFO_WITNESS_IF)) { + return false; + } + + /* witness should be available of course */ + if (i->state != WITNESS_STATE_AVAILABLE) { + return false; + } + + return true; +} + +static bool test_witness_Register(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_Register r; + struct policy_handle context_handle; + struct torture_test_witness_state *state = + (struct torture_test_witness_state *)data; + int i; + + struct { + enum witness_version version; + const char *net_name; + const char *ip_address; + const char *client_computer_name; + NTSTATUS expected_status; + WERROR expected_result; + } tests[] = { + { + .version = 0, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = 1, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = 123456, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = -1, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V2, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V1, + .net_name = "", + .ip_address = "", + .client_computer_name = "", + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + },{ + .version = WITNESS_V1, + .net_name = NULL, + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + },{ + .version = WITNESS_V2, + .net_name = NULL, + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V1, + .net_name = dcerpc_server_name(p), + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + } + + }; + + for (i=0; i < ARRAY_SIZE(tests); i++) { + + ZERO_STRUCT(r); + + r.out.context_handle = &context_handle; + + r.in.version = tests[i].version; + r.in.net_name = tests[i].net_name; + r.in.ip_address = tests[i].ip_address; + r.in.client_computer_name = tests[i].client_computer_name; + + torture_assert_ntstatus_equal(tctx, + dcerpc_witness_Register_r(b, tctx, &r), + tests[i].expected_status, + "Register failed"); + + torture_assert_werr_equal(tctx, + r.out.result, + tests[i].expected_result, + "Register failed"); + + if (W_ERROR_IS_OK(r.out.result)) { + + /* we have a handle, make sure to unregister it */ + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle), + "Failed to unregister"); + } + } + + init_witness_test_state(tctx, p, state); + + for (i=0; state->list && i < state->list->num_interfaces; i++) { + + const char *ip_address; + struct witness_interfaceInfo interface = state->list->interfaces[i]; + + if (!check_valid_interface(tctx, &interface)) { + continue; + } + + torture_assert(tctx, + get_ip_address_from_interface(tctx, &interface, &ip_address), + "failed to get ip_address from interface"); + + r.in.version = WITNESS_V1; + r.in.net_name = state->net_name; + r.in.ip_address = ip_address; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_Register_r(b, tctx, &r), + "Register failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "Register failed"); + + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle), + "Failed to unregister"); + } + + return true; +} + +static bool test_witness_RegisterEx(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_RegisterEx r; + struct policy_handle context_handle; + struct torture_test_witness_state *state = + (struct torture_test_witness_state *)data; + int i; + + struct { + enum witness_version version; + const char *net_name; + const char *ip_address; + const char *client_computer_name; + NTSTATUS expected_status; + WERROR expected_result; + } tests[] = { + { + .version = 0, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = 1, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = 123456, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = -1, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V1, + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V2, + .net_name = "", + .ip_address = "", + .client_computer_name = "", + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + },{ + .version = WITNESS_V2, + .net_name = NULL, + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + },{ + .version = WITNESS_V1, + .net_name = NULL, + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_REVISION_MISMATCH + },{ + .version = WITNESS_V2, + .net_name = dcerpc_server_name(p), + .ip_address = NULL, + .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx), + .expected_status = NT_STATUS_OK, + .expected_result = WERR_INVALID_PARAMETER + } + + }; + + for (i=0; i < ARRAY_SIZE(tests); i++) { + + ZERO_STRUCT(r); + + r.out.context_handle = &context_handle; + + r.in.version = tests[i].version; + r.in.net_name = tests[i].net_name; + r.in.ip_address = tests[i].ip_address; + r.in.client_computer_name = tests[i].client_computer_name; + + torture_assert_ntstatus_equal(tctx, + dcerpc_witness_RegisterEx_r(b, tctx, &r), + tests[i].expected_status, + "RegisterEx failed"); + + torture_assert_werr_equal(tctx, + r.out.result, + tests[i].expected_result, + "RegisterEx failed"); + + if (W_ERROR_IS_OK(r.out.result)) { + + /* we have a handle, make sure to unregister it */ + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle), + "Failed to unregister"); + } + } + + init_witness_test_state(tctx, p, state); + + for (i=0; state->list && i < state->list->num_interfaces; i++) { + + const char *ip_address; + struct witness_interfaceInfo interface = state->list->interfaces[i]; + + if (!check_valid_interface(tctx, &interface)) { + continue; + } + + torture_assert(tctx, + get_ip_address_from_interface(tctx, &interface, &ip_address), + "failed to get ip_address from interface"); + + r.in.version = WITNESS_V2; + r.in.net_name = state->net_name; + r.in.ip_address = ip_address; + + /* + * a valid request with an invalid sharename fails with + * WERR_INVALID_STATE + */ + r.in.share_name = "any_invalid_share_name"; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_RegisterEx_r(b, tctx, &r), + "RegisterEx failed"); + + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_STATE, + "RegisterEx failed"); + + r.in.share_name = NULL; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_RegisterEx_r(b, tctx, &r), + "RegisterEx failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "RegisterEx failed"); + + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle), + "Failed to unregister"); + } + + return true; +} + +static bool setup_clusapi_connection(struct torture_context *tctx, + struct torture_test_witness_state *s) +{ + struct dcerpc_binding *binding; + + if (s->clusapi.p) { + return true; + } + + torture_assert_ntstatus_ok(tctx, + torture_rpc_binding(tctx, &binding), + "failed to retrieve torture binding"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_transport(binding, NCACN_IP_TCP), + "failed to set transport"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_binding_set_flags(binding, DCERPC_SEAL, 0), + "failed to set dcerpc flags"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &s->clusapi.p, binding, + &ndr_table_clusapi, + samba_cmdline_get_creds(), + tctx->ev, tctx->lp_ctx), + "failed to connect dcerpc pipe"); + + return true; +} + +#if 0 +static bool cluster_get_nodes(struct torture_context *tctx, + struct torture_test_witness_state *s) +{ + struct clusapi_CreateEnum r; + struct ENUM_LIST *ReturnEnum; + WERROR rpc_status; + struct dcerpc_binding_handle *b; + + torture_assert(tctx, + setup_clusapi_connection(tctx, s), + "failed to setup clusapi connection"); + + b = s->clusapi.p->binding_handle; + + r.in.dwType = CLUSTER_ENUM_NODE; + r.out.ReturnEnum = &ReturnEnum; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_CreateEnum_r(b, tctx, &r), + "failed to enumerate nodes"); + + return true; +} +#endif + +static bool test_GetResourceState_int(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct policy_handle *hResource, + enum clusapi_ClusterResourceState *State) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct clusapi_GetResourceState r; + const char *NodeName; + const char *GroupName; + WERROR rpc_status; + + r.in.hResource = *hResource; + r.out.State = State; + r.out.NodeName = &NodeName; + r.out.GroupName = &GroupName; + r.out.rpc_status = &rpc_status; + + torture_assert_ntstatus_ok(tctx, + dcerpc_clusapi_GetResourceState_r(b, tctx, &r), + "GetResourceState failed"); + torture_assert_werr_ok(tctx, + r.out.result, + "GetResourceState failed"); + + return true; +} + +static bool toggle_cluster_resource_state(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *resource_name, + enum clusapi_ClusterResourceState *old_state, + enum clusapi_ClusterResourceState *new_state) +{ + struct policy_handle hResource; + enum clusapi_ClusterResourceState State; + + torture_assert(tctx, + test_OpenResource_int(tctx, p, resource_name, &hResource), + "failed to open resource"); + torture_assert(tctx, + test_GetResourceState_int(tctx, p, &hResource, &State), + "failed to query resource state"); + + if (old_state) { + *old_state = State; + } + + switch (State) { + case ClusterResourceOffline: + if (!test_OnlineResource_int(tctx, p, &hResource)) { + test_CloseResource_int(tctx, p, &hResource); + torture_warning(tctx, "failed to set resource online"); + return false; + } + break; + case ClusterResourceOnline: + if (!test_OfflineResource_int(tctx, p, &hResource)) { + test_CloseResource_int(tctx, p, &hResource); + torture_warning(tctx, "failed to set resource offline"); + return false; + } + break; + + default: + break; + } + + torture_assert(tctx, + test_GetResourceState_int(tctx, p, &hResource, &State), + "failed to query resource state"); + + if (new_state) { + *new_state = State; + } + + test_CloseResource_int(tctx, p, &hResource); + + return true; +} + +static bool test_witness_AsyncNotify(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_AsyncNotify r; + struct witness_notifyResponse *response; + struct torture_test_witness_state *state = + (struct torture_test_witness_state *)data; + int i; + + init_witness_test_state(tctx, p, state); + + setup_clusapi_connection(tctx, state); + + for (i=0; state->list && i < state->list->num_interfaces; i++) { + + const char *ip_address; + struct witness_interfaceInfo interface = state->list->interfaces[i]; + struct witness_Register reg; + struct tevent_req *req; + enum clusapi_ClusterResourceState old_state, new_state; + + if (!check_valid_interface(tctx, &interface)) { + continue; + } + + torture_assert(tctx, + get_ip_address_from_interface(tctx, &interface, &ip_address), + "failed to get ip_address from interface"); + + reg.in.version = WITNESS_V1; + reg.in.net_name = state->net_name; + reg.in.ip_address = ip_address; + reg.in.client_computer_name = lpcfg_netbios_name(tctx->lp_ctx); + reg.out.context_handle = &state->context_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_Register_r(b, tctx, ®), + "Register failed"); + + torture_assert_werr_ok(tctx, + reg.out.result, + "Register failed"); + + r.in.context_handle = state->context_handle; + r.out.response = &response; + + req = dcerpc_witness_AsyncNotify_r_send(tctx, tctx->ev, b, &r); + torture_assert(tctx, req, "failed to create request"); + + torture_assert(tctx, + toggle_cluster_resource_state(tctx, state->clusapi.p, state->net_name, &old_state, &new_state), + "failed to toggle cluster resource state"); + torture_assert(tctx, old_state != new_state, "failed to change cluster resource state"); + + torture_assert(tctx, + tevent_req_poll(req, tctx->ev), + "failed to call event loop"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_AsyncNotify_r_recv(req, tctx), + "failed to receive reply"); + + torture_assert_int_equal(tctx, response->num, 1, "num"); + torture_assert_int_equal(tctx, response->type, WITNESS_NOTIFY_RESOURCE_CHANGE, "type"); + + /* + * TODO: find out how ClusterResourceOfflinePending and + * ClusterResourceOnlinePending are represented as witness + * types. + */ + + if (new_state == ClusterResourceOffline) { + torture_assert_int_equal(tctx, response->messages[0].resource_change.type, WITNESS_RESOURCE_STATE_UNAVAILABLE, "resource_change.type"); + } + if (new_state == ClusterResourceOnline) { + torture_assert_int_equal(tctx, response->messages[0].resource_change.type, WITNESS_RESOURCE_STATE_AVAILABLE, "resource_change.type"); + } + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, &state->context_handle), + "Failed to unregister"); + + ZERO_STRUCT(state->context_handle); + + torture_assert(tctx, + toggle_cluster_resource_state(tctx, state->clusapi.p, state->net_name, &old_state, &new_state), + "failed to toggle cluster resource state"); + torture_assert(tctx, old_state != new_state, "failed to change cluster resource state"); + } + + return true; +} + +static bool test_do_witness_RegisterEx(struct torture_context *tctx, + struct dcerpc_binding_handle *b, + uint32_t version, + const char *net_name, + const char *share_name, + const char *ip_address, + const char *client_computer_name, + uint32_t flags, + uint32_t timeout, + struct policy_handle *context_handle) +{ + struct witness_RegisterEx r; + + r.in.version = version; + r.in.net_name = net_name; + r.in.share_name = NULL; + r.in.ip_address = ip_address; + r.in.client_computer_name = client_computer_name; + r.in.flags = flags; + r.in.timeout = timeout; + r.out.context_handle = context_handle; + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_RegisterEx_r(b, tctx, &r), + "RegisterEx failed"); + + torture_assert_werr_ok(tctx, + r.out.result, + "RegisterEx failed"); + + return true; +} + +static void torture_subunit_report_time(struct torture_context *tctx) +{ + struct timespec tp; + struct tm *tmp; + char timestr[200]; + + if (clock_gettime(CLOCK_REALTIME, &tp) != 0) { + torture_comment(tctx, "failed to call clock_gettime"); + return; + } + + tmp = gmtime(&tp.tv_sec); + if (!tmp) { + torture_comment(tctx, "failed to call gmtime"); + return; + } + + if (strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tmp) <= 0) { + torture_comment(tctx, "failed to call strftime"); + return; + } + + torture_comment(tctx, "time: %s.%06ld\n", timestr, tp.tv_nsec / 1000); +} + +static bool test_witness_AsyncNotify_timeouts(struct torture_context *tctx, + struct dcerpc_pipe *p, + void *data) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + struct witness_AsyncNotify r; + struct witness_notifyResponse *response; + struct torture_test_witness_state *state = + (struct torture_test_witness_state *)data; + int i; + + init_witness_test_state(tctx, p, state); + + setup_clusapi_connection(tctx, state); + + for (i=0; state->list && i < state->list->num_interfaces; i++) { + + const char *ip_address; + struct witness_interfaceInfo interface = state->list->interfaces[i]; + uint32_t timeouts[] = { + 0, 1, 10, 100, 120 + }; + int t; + uint32_t old_timeout; + + if (!check_valid_interface(tctx, &interface)) { + continue; + } + + torture_assert(tctx, + get_ip_address_from_interface(tctx, &interface, &ip_address), + "failed to get ip_address from interface"); + + for (t=0; t < ARRAY_SIZE(timeouts); t++) { + + torture_comment(tctx, "Testing Async Notify with timeout of %d milliseconds", timeouts[t]); + + torture_assert(tctx, + test_do_witness_RegisterEx(tctx, b, + WITNESS_V2, + state->net_name, + NULL, + ip_address, + lpcfg_netbios_name(tctx->lp_ctx), + 0, + timeouts[t], + &state->context_handle), + "failed to RegisterEx"); + + r.in.context_handle = state->context_handle; + r.out.response = &response; + + old_timeout = dcerpc_binding_handle_set_timeout(b, UINT_MAX); + + torture_subunit_report_time(tctx); + + torture_assert_ntstatus_ok(tctx, + dcerpc_witness_AsyncNotify_r(b, tctx, &r), + "AsyncNotify failed"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_TIMEOUT, + "AsyncNotify failed"); + + torture_subunit_report_time(tctx); + + dcerpc_binding_handle_set_timeout(b, old_timeout); + + torture_assert(tctx, + test_witness_UnRegister_with_handle(tctx, p, &state->context_handle), + "Failed to unregister"); + + ZERO_STRUCT(state->context_handle); + } + } + + return true; +} + +struct torture_suite *torture_rpc_witness(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "witness"); + struct torture_test_witness_state *state; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "witness", + &ndr_table_witness); + + state = talloc_zero(tcase, struct torture_test_witness_state); + + torture_rpc_tcase_add_test_ex(tcase, "GetInterfaceList", + test_witness_GetInterfaceList, state); + torture_rpc_tcase_add_test_ex(tcase, "Register", + test_witness_Register, state); + torture_rpc_tcase_add_test_ex(tcase, "UnRegister", + test_witness_UnRegister, state); + torture_rpc_tcase_add_test_ex(tcase, "RegisterEx", + test_witness_RegisterEx, state); + torture_rpc_tcase_add_test_ex(tcase, "AsyncNotify", + test_witness_AsyncNotify, state); + torture_rpc_tcase_add_test_ex(tcase, "AsyncNotify_timeouts", + test_witness_AsyncNotify_timeouts, state); + + return suite; +} diff --git a/source4/torture/rpc/wkssvc.c b/source4/torture/rpc/wkssvc.c new file mode 100644 index 0000000..c43e16f --- /dev/null +++ b/source4/torture/rpc/wkssvc.c @@ -0,0 +1,1458 @@ +/* + Unix SMB/CIFS implementation. + test suite for wkssvc rpc operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Günther Deschner 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_wkssvc_c.h" +#include "torture/rpc/torture_rpc.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" +#include "libcli/auth/libcli_auth.h" + +#define SMBTORTURE_MACHINE_NAME "smbtrt_name" +#define SMBTORTURE_ALTERNATE_NAME "smbtrt_altname" +#define SMBTORTURE_TRANSPORT_NAME "\\Device\\smbtrt_transport_name" +#define SMBTORTURE_USE_NAME "S:" +#define SMBTORTURE_MESSAGE "You are currently tortured by Samba" + +static bool test_NetWkstaGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetWkstaGetInfo r; + union wkssvc_NetWkstaInfo info; + uint16_t levels[] = {100, 101, 102, 502}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.out.info = &info; + + for (i=0;ibinding_handle; + + ZERO_STRUCT(ctr0); + ctr.ctr0 = &ctr0; + + info.level = 0; + info.ctr = ctr; + + r.in.server_name = dcerpc_server_name(p); + r.in.info = &info; + r.in.max_buffer = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.total_entries = &total_entries; + r.out.info = &info; + r.out.resume_handle = &resume_handle; + + torture_comment(tctx, "Testing NetWkstaTransportEnum level 0\n"); + + status = dcerpc_wkssvc_NetWkstaTransportEnum_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetWkstaTransportEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, talloc_asprintf(tctx, + "NetWkstaTransportEnum level %u failed", + info.level)); + + return true; +} + +static bool test_NetrWkstaTransportAdd(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWkstaTransportAdd r; + struct wkssvc_NetWkstaTransportInfo0 info0; + uint32_t parm_err = 0; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info0); + + info0.quality_of_service = 0xffff; + info0.vc_count = 0; + info0.name = SMBTORTURE_TRANSPORT_NAME; + info0.address = "000000000000"; + info0.wan_link = 0x400; + + r.in.server_name = dcerpc_server_name(p); + r.in.level = 0; + r.in.info0 = &info0; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "Testing NetrWkstaTransportAdd level 0\n"); + + status = dcerpc_wkssvc_NetrWkstaTransportAdd_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWkstaTransportAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_INVALID_PARAMETER, + "NetrWkstaTransportAdd level 0 failed"); + + return true; +} + +static bool test_NetrWkstaTransportDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWkstaTransportDel r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.transport_name = SMBTORTURE_TRANSPORT_NAME; + r.in.unknown3 = 0; + + torture_comment(tctx, "Testing NetrWkstaTransportDel\n"); + + status = dcerpc_wkssvc_NetrWkstaTransportDel_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWkstaTransportDel failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrWkstaTransportDel"); + + return true; +} + +static bool test_NetWkstaEnumUsers(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetWkstaEnumUsers r; + uint32_t handle = 0; + uint32_t entries_read = 0; + struct wkssvc_NetWkstaEnumUsersInfo info; + struct wkssvc_NetWkstaEnumUsersCtr0 *user0; + struct wkssvc_NetWkstaEnumUsersCtr1 *user1; + uint32_t levels[] = { 0, 1 }; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0; ilp_ctx); + struct cli_credentials *creds = samba_cmdline_get_creds(); + const char *user = cli_credentials_get_username(creds); + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + const struct { + const char *unknown; + uint32_t level; + WERROR result; + } tests[] = { + { NULL, 0, WERR_NO_SUCH_LOGON_SESSION }, + { NULL, 1, WERR_NO_SUCH_LOGON_SESSION }, + { NULL, 1101, WERR_OK }, + { dom, 0, WERR_INVALID_PARAMETER }, + { dom, 1, WERR_INVALID_PARAMETER }, + { dom, 1101, WERR_INVALID_PARAMETER }, + { user, 0, WERR_INVALID_PARAMETER }, + { user, 1, WERR_INVALID_PARAMETER }, + { user, 1101, WERR_INVALID_PARAMETER }, + }; + + for (i=0; ibinding_handle; + + for (i=0; ibinding_handle; + + ctr = talloc(tctx, union wkssvc_NetrUseGetInfoCtr); + + ZERO_STRUCT(info0); + + info0.local = SMBTORTURE_USE_NAME; + info0.remote = "\\\\localhost\\c$"; + + ctr->info0 = &info0; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.level = 0; + r.in.ctr = ctr; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "Testing NetrUseAdd level %u\n", + r.in.level); + + status = dcerpc_wkssvc_NetrUseAdd_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, + "NetrUseAdd failed"); + + ZERO_STRUCT(r); + ZERO_STRUCT(info1); + + info1.local = SMBTORTURE_USE_NAME; + info1.remote = "\\\\localhost\\sysvol"; + info1.password = NULL; + + ctr->info1 = &info1; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.level = 1; + r.in.ctr = ctr; + r.in.parm_err = r.out.parm_err = &parm_err; + + torture_comment(tctx, "Testing NetrUseAdd level %u\n", + r.in.level); + + status = dcerpc_wkssvc_NetrUseAdd_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseAdd failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseAdd failed"); + + return true; +} + +static bool test_NetrUseDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseDel r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.use_name = SMBTORTURE_USE_NAME; + r.in.force_cond = 0; + + torture_comment(tctx, "Testing NetrUseDel\n"); + + status = dcerpc_wkssvc_NetrUseDel_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseDel failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseDel failed"); + return true; +} + +static bool test_NetrUseGetInfo_level(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *use_name, + uint32_t level, + WERROR werr) +{ + NTSTATUS status; + struct wkssvc_NetrUseGetInfo r; + union wkssvc_NetrUseGetInfoCtr ctr; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(ctr); + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.use_name = use_name; + r.in.level = level; + r.out.ctr = &ctr; + status = dcerpc_wkssvc_NetrUseGetInfo_r(b, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, + "NetrUseGetInfo failed"); + torture_assert_werr_equal(tctx, r.out.result, werr, + "NetrUseGetInfo failed"); + return true; +} + +static bool test_NetrUseGetInfo(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUseEnum r; + uint32_t handle = 0; + uint32_t entries_read = 0; + struct wkssvc_NetrUseEnumInfo info; + struct wkssvc_NetrUseEnumCtr0 *use0; + uint32_t levels[] = { 0, 1, 2 }; + const char *use_name = NULL; + int i, k; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(info); + + info.level = 0; + use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0); + info.ctr.ctr0 = use0; + + r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.prefmaxlen = (uint32_t)-1; + r.in.info = r.out.info = &info; + r.in.resume_handle = r.out.resume_handle = &handle; + r.out.entries_read = &entries_read; + + status = dcerpc_wkssvc_NetrUseEnum_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUseEnum failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrUseEnum failed"); + + for (k=0; k < r.out.info->ctr.ctr0->count; k++) { + + use_name = r.out.info->ctr.ctr0->array[k].local; + + for (i=0; ictr.ctr0->array[k].remote; + + for (i=0; ibinding_handle; + + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + + torture_comment(tctx, "Testing NetrLogonDomainNameAdd\n"); + + status = dcerpc_wkssvc_NetrLogonDomainNameAdd_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrLogonDomainNameAdd failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrLogonDomainNameAdd failed"); + return true; +} + +static bool test_NetrLogonDomainNameDel(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrLogonDomainNameDel r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + + torture_comment(tctx, "Testing NetrLogonDomainNameDel\n"); + + status = dcerpc_wkssvc_NetrLogonDomainNameDel_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrLogonDomainNameDel failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrLogonDomainNameDel failed"); + return true; +} + +static bool test_NetrEnumerateComputerNames_level(struct torture_context *tctx, + struct dcerpc_pipe *p, + uint16_t level, + const char ***names, + size_t *num_names) +{ + NTSTATUS status; + struct wkssvc_NetrEnumerateComputerNames r; + struct wkssvc_ComputerNamesCtr *ctr; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + ctr = talloc_zero(tctx, struct wkssvc_ComputerNamesCtr); + + r.in.server_name = dcerpc_server_name(p); + r.in.name_type = level; + r.in.Reserved = 0; + r.out.ctr = &ctr; + + torture_comment(tctx, "Testing NetrEnumerateComputerNames level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrEnumerateComputerNames_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrEnumerateComputerNames failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrEnumerateComputerNames failed"); + + if ((level == NetPrimaryComputerName) && ctr->count != 1) { + torture_comment(tctx, + "NetrEnumerateComputerNames did not return one " + "name but %u\n", ctr->count); + return false; + } + + if (names && num_names) { + *num_names = 0; + *names = NULL; + for (i=0; icount; i++) { + if (!add_string_to_array(tctx, + ctr->computer_name[i].string, + names, + num_names)) + { + return false; + } + } + } + + return true; +} + +static bool test_NetrEnumerateComputerNames(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + uint16_t levels[] = {0,1,2}; + int i; + + for (i=0; ibinding_handle; + + for (i=0; ilp_ctx); + r.in.Account = NULL; + r.in.Password = NULL; + r.in.name_type = levels[i]; + + torture_comment(tctx, "Testing NetrValidateName level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrValidateName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrValidateName failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_NOT_SUPPORTED, + "NetrValidateName failed"); + } + + return true; +} + +static bool test_NetrValidateName2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrValidateName2 r; + uint16_t levels[] = {0,1,2,3,4,5}; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + for (i=0; ilp_ctx); + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.name_type = levels[i]; + + torture_comment(tctx, "Testing NetrValidateName2 level %u\n", + r.in.name_type); + + status = dcerpc_wkssvc_NetrValidateName2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrValidateName2 failed"); + torture_assert_werr_equal(tctx, r.out.result, + W_ERROR(HRES_ERROR_V(HRES_RPC_E_REMOTE_DISABLED)), + "NetrValidateName2 failed"); + } + + return true; +} + +static bool test_NetrAddAlternateComputerName(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrAddAlternateComputerName r; + const char **names = NULL; + size_t num_names = 0; + int i; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewAlternateMachineName = SMBTORTURE_ALTERNATE_NAME; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + torture_comment(tctx, "Testing NetrAddAlternateComputerName\n"); + + status = dcerpc_wkssvc_NetrAddAlternateComputerName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrAddAlternateComputerName failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrAddAlternateComputerName failed"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetAlternateComputerNames, + &names, &num_names)) + { + return false; + } + + for (i=0; ibinding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.AlternateMachineNameToRemove = SMBTORTURE_ALTERNATE_NAME; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + torture_comment(tctx, "Testing NetrRemoveAlternateComputerName\n"); + + status = dcerpc_wkssvc_NetrRemoveAlternateComputerName_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRemoveAlternateComputerName failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrRemoveAlternateComputerName failed"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetAlternateComputerNames, + &names, &num_names)) + { + return false; + } + + for (i=0; ibinding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.primary_name = name; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.Reserved = 0; + + status = dcerpc_wkssvc_NetrSetPrimaryComputername_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrSetPrimaryComputername failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrSetPrimaryComputername failed"); + return true; +} + + +static bool test_NetrSetPrimaryComputername(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + /* + add alternate, + check if there + store old primary + set new primary (alternate) + check if there + later: check if del is possible + set primary back to origin + check if there + del alternate + */ + + const char **names_o = NULL, **names = NULL; + size_t num_names_o = 0, num_names = 0; + + torture_comment(tctx, "Testing NetrSetPrimaryComputername\n"); + + if (!test_NetrAddAlternateComputerName(tctx, p)) { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names_o, &num_names_o)) + { + return false; + } + + if (num_names_o != 1) { + return false; + } + + if (!test_NetrSetPrimaryComputername_name(tctx, p, + SMBTORTURE_ALTERNATE_NAME)) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (!strequal(names[0], SMBTORTURE_ALTERNATE_NAME)) { + torture_comment(tctx, + "name mismatch (%s != %s) after NetrSetPrimaryComputername!\n", + names[0], SMBTORTURE_ALTERNATE_NAME); + /*return false */; + } + + if (!test_NetrSetPrimaryComputername_name(tctx, p, + names_o[0])) + { + return false; + } + + if (!test_NetrRemoveAlternateComputerName(tctx, p)) { + return false; + } + + + return true; +} + +static bool test_NetrRenameMachineInDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrRenameMachineInDomain r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewMachineName = SMBTORTURE_MACHINE_NAME; + r.in.Account = NULL; + r.in.password = NULL; + r.in.RenameOptions = 0; + + torture_comment(tctx, "Testing NetrRenameMachineInDomain\n"); + + status = dcerpc_wkssvc_NetrRenameMachineInDomain_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRenameMachineInDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrRenameMachineInDomain failed"); + return true; +} + +static bool test_NetrRenameMachineInDomain2_name(struct torture_context *tctx, + struct dcerpc_pipe *p, + const char *new_name) +{ + NTSTATUS status; + struct wkssvc_NetrRenameMachineInDomain2 r; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.NewMachineName = new_name; + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.RenameOptions = 0; + + status = dcerpc_wkssvc_NetrRenameMachineInDomain2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrRenameMachineInDomain2 failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrRenameMachineInDomain2 failed"); + return true; +} + +static bool test_NetrRenameMachineInDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + const char **names_o = NULL, **names = NULL; + size_t num_names_o = 0, num_names = 0; + + torture_comment(tctx, "Testing NetrRenameMachineInDomain2\n"); + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names_o, &num_names_o)) + { + return false; + } + + if (num_names_o != 1) { + return false; + } + + if (!test_NetrRenameMachineInDomain2_name(tctx, p, + SMBTORTURE_MACHINE_NAME)) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (strequal(names[0], names_o[0])) { + test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]); + return false; + } + + if (!strequal(names[0], SMBTORTURE_MACHINE_NAME)) { + test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]); + return false; + } + + if (!test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0])) + { + return false; + } + + if (!test_NetrEnumerateComputerNames_level(tctx, p, + NetPrimaryComputerName, + &names, &num_names)) + { + return false; + } + + if (num_names != 1) { + return false; + } + + if (!strequal(names[0], names_o[0])) { + return false; + } + + return true; +} + +static bool test_NetrWorkstationStatisticsGet(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrWorkstationStatisticsGet r; + struct wkssvc_NetrWorkstationStatistics *info; + struct dcerpc_binding_handle *b = p->binding_handle; + + ZERO_STRUCT(r); + + info = talloc_zero(tctx, struct wkssvc_NetrWorkstationStatistics); + + r.in.server_name = dcerpc_server_name(p); + r.out.info = &info; + + torture_comment(tctx, "Testing NetrWorkstationStatisticsGet\n"); + + status = dcerpc_wkssvc_NetrWorkstationStatisticsGet_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrWorkstationStatisticsGet failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrWorkstationStatisticsGet failed"); + return true; +} + +/* only succeeds as long as the local messenger service is running - Guenther */ + +static bool test_NetrMessageBufferSend(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrMessageBufferSend r; + const char *message = SMBTORTURE_MESSAGE; + size_t size; + uint16_t *msg; + struct dcerpc_binding_handle *b = p->binding_handle; + + if (!push_ucs2_talloc(tctx, &msg, message, &size)) { + return false; + } + + r.in.server_name = dcerpc_server_name(p); + r.in.message_name = dcerpc_server_name(p); + r.in.message_sender_name = dcerpc_server_name(p); + r.in.message_buffer = (uint8_t *)msg; + r.in.message_size = size; + + torture_comment(tctx, "Testing NetrMessageBufferSend\n"); + + status = dcerpc_wkssvc_NetrMessageBufferSend_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrMessageBufferSend failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrMessageBufferSend failed"); + return true; +} + +static bool test_NetrGetJoinInformation(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinInformation r; + enum wkssvc_NetJoinStatus join_status; + const char *name_buffer = ""; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.name_buffer = r.out.name_buffer = &name_buffer; + r.out.name_type = &join_status; + + torture_comment(tctx, "Testing NetrGetJoinInformation\n"); + + status = dcerpc_wkssvc_NetrGetJoinInformation_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrGetJoinInformation failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrGetJoinInformation failed"); + return true; +} + +static bool test_GetJoinInformation(struct torture_context *tctx, + struct dcerpc_pipe *p, + enum wkssvc_NetJoinStatus *join_status_p, + const char **name) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinInformation r; + enum wkssvc_NetJoinStatus join_status; + const char *name_buffer = ""; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.name_buffer = r.out.name_buffer = &name_buffer; + r.out.name_type = &join_status; + + status = dcerpc_wkssvc_NetrGetJoinInformation_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrGetJoinInformation failed"); + torture_assert_werr_ok(tctx, r.out.result, + "NetrGetJoinInformation failed"); + + if (join_status_p) { + *join_status_p = join_status; + } + + if (*name) { + *name = talloc_strdup(tctx, name_buffer); + } + + return true; + +} + +static bool test_NetrGetJoinableOus(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinableOus r; + uint32_t num_ous = 0; + const char **ous = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.unknown = NULL; + r.in.num_ous = r.out.num_ous = &num_ous; + r.out.ous = &ous; + + torture_comment(tctx, "Testing NetrGetJoinableOus\n"); + + status = dcerpc_wkssvc_NetrGetJoinableOus_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus failed"); + torture_assert_werr_equal(tctx, r.out.result, + WERR_NOT_SUPPORTED, + "NetrGetJoinableOus failed"); + + return true; +} + +static bool test_NetrGetJoinableOus2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrGetJoinableOus2 r; + uint32_t num_ous = 0; + const char **ous = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx); + r.in.Account = NULL; + r.in.EncryptedPassword = NULL; + r.in.num_ous = r.out.num_ous = &num_ous; + r.out.ous = &ous; + + torture_comment(tctx, "Testing NetrGetJoinableOus2\n"); + + status = dcerpc_wkssvc_NetrGetJoinableOus2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus2 failed"); + torture_assert_werr_equal(tctx, r.out.result, + W_ERROR(HRES_ERROR_V(HRES_RPC_E_REMOTE_DISABLED)), + "NetrGetJoinableOus2 failed"); + + return true; +} + +static bool test_NetrUnjoinDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUnjoinDomain r; + struct cli_credentials *creds = samba_cmdline_get_creds(); + const char *user = cli_credentials_get_username(creds); + const char *admin_account = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + admin_account = talloc_asprintf(tctx, "%s\\%s", + lpcfg_workgroup(tctx->lp_ctx), + user); + + r.in.server_name = dcerpc_server_name(p); + r.in.Account = admin_account; + r.in.password = NULL; + r.in.unjoin_flags = 0; + + torture_comment(tctx, "Testing NetrUnjoinDomain\n"); + + status = dcerpc_wkssvc_NetrUnjoinDomain_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUnjoinDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrUnjoinDomain failed"); + return true; +} + +static bool test_NetrJoinDomain(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrJoinDomain r; + struct cli_credentials *creds = samba_cmdline_get_creds(); + const char *user = cli_credentials_get_username(creds); + const char *admin_account = NULL; + struct dcerpc_binding_handle *b = p->binding_handle; + + admin_account = talloc_asprintf(tctx, "%s\\%s", + lpcfg_workgroup(tctx->lp_ctx), + user); + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx); + r.in.account_ou = NULL; + r.in.Account = admin_account; + r.in.password = NULL; + r.in.join_flags = 0; + + torture_comment(tctx, "Testing NetrJoinDomain\n"); + + status = dcerpc_wkssvc_NetrJoinDomain_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrJoinDomain failed"); + torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, + "NetrJoinDomain failed"); + return true; +} + +/* + * prerequisites for remotely joining an unjoined XP SP2 workstation: + * - firewall needs to be disabled (or open for ncacn_np access) + * - HKLM\System\CurrentControlSet\Control\Lsa\forceguest needs to 0 + * see also: + * http://support.microsoft.com/kb/294355/EN-US/ and + * http://support.microsoft.com/kb/290403/EN-US/ + */ + +static bool test_NetrJoinDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrJoinDomain2 r; + const char *domain_admin_account = NULL; + const char *domain_admin_password = NULL; + const char *domain_name = NULL; + struct wkssvc_PasswordBuffer *pwd_buf; + enum wkssvc_NetJoinStatus join_status; + const char *join_name = NULL; + WERROR expected_err; + WERROR werr; + DATA_BLOB session_key; + struct dcerpc_binding_handle *b = p->binding_handle; + + /* FIXME: this test assumes to join workstations / servers and does not + * handle DCs (WERR_NERR_SETUPDOMAINCONTROLLER) */ + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NET_SETUP_DOMAIN_NAME: + expected_err = WERR_NERR_SETUPALREADYJOINED; + break; + case NET_SETUP_UNKNOWN_STATUS: + case NET_SETUP_UNJOINED: + case NET_SETUP_WORKGROUP_NAME: + default: + expected_err = WERR_OK; + break; + } + + domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL); + + domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL); + + domain_name = torture_setting_string(tctx, "domain_name", NULL); + + if ((domain_admin_account == NULL) || + (domain_admin_password == NULL) || + (domain_name == NULL)) { + torture_comment(tctx, "not enough input parameter\n"); + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + werr = encode_wkssvc_join_password_buffer(tctx, + domain_admin_password, + &session_key, + &pwd_buf); + if (!W_ERROR_IS_OK(werr)) { + return false; + } + + r.in.server_name = dcerpc_server_name(p); + r.in.domain_name = domain_name; + r.in.account_ou = NULL; + r.in.admin_account = domain_admin_account; + r.in.encrypted_password = pwd_buf; + r.in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE; + + torture_comment(tctx, "Testing NetrJoinDomain2 (assuming non-DC)\n"); + + status = dcerpc_wkssvc_NetrJoinDomain2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrJoinDomain2 failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_err, + "NetrJoinDomain2 failed"); + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + if (join_status != NET_SETUP_DOMAIN_NAME) { + torture_comment(tctx, + "Join verify failed: got %d\n", join_status); + return false; + } + + return true; +} + +static bool test_NetrUnjoinDomain2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct wkssvc_NetrUnjoinDomain2 r; + const char *domain_admin_account = NULL; + const char *domain_admin_password = NULL; + struct wkssvc_PasswordBuffer *pwd_buf; + enum wkssvc_NetJoinStatus join_status; + const char *join_name = NULL; + WERROR expected_err; + WERROR werr; + DATA_BLOB session_key; + struct dcerpc_binding_handle *b = p->binding_handle; + + /* FIXME: this test assumes to join workstations / servers and does not + * handle DCs (WERR_NERR_SETUPDOMAINCONTROLLER) */ + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NET_SETUP_UNJOINED: + expected_err = WERR_NERR_SETUPNOTJOINED; + break; + case NET_SETUP_DOMAIN_NAME: + case NET_SETUP_UNKNOWN_STATUS: + case NET_SETUP_WORKGROUP_NAME: + default: + expected_err = WERR_OK; + break; + } + + domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL); + + domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL); + + if ((domain_admin_account == NULL) || + (domain_admin_password == NULL)) { + torture_comment(tctx, "not enough input parameter\n"); + return false; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + werr = encode_wkssvc_join_password_buffer(tctx, + domain_admin_password, + &session_key, + &pwd_buf); + if (!W_ERROR_IS_OK(werr)) { + return false; + } + + r.in.server_name = dcerpc_server_name(p); + r.in.account = domain_admin_account; + r.in.encrypted_password = pwd_buf; + r.in.unjoin_flags = 0; + + torture_comment(tctx, "Testing NetrUnjoinDomain2 (assuming non-DC)\n"); + + status = dcerpc_wkssvc_NetrUnjoinDomain2_r(b, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "NetrUnjoinDomain2 failed"); + torture_assert_werr_equal(tctx, r.out.result, expected_err, + "NetrUnjoinDomain2 failed"); + + if (!test_GetJoinInformation(tctx, p, &join_status, &join_name)) + { + return false; + } + + switch (join_status) { + case NET_SETUP_UNJOINED: + case NET_SETUP_WORKGROUP_NAME: + break; + case NET_SETUP_UNKNOWN_STATUS: + case NET_SETUP_DOMAIN_NAME: + default: + torture_comment(tctx, + "Unjoin verify failed: got %d\n", join_status); + return false; + } + + return true; +} + + +struct torture_suite *torture_rpc_wkssvc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + struct torture_rpc_tcase *tcase; + struct torture_test *test; + + suite = torture_suite_create(mem_ctx, "wkssvc"); + tcase = torture_suite_add_rpc_iface_tcase(suite, "wkssvc", + &ndr_table_wkssvc); + + torture_rpc_tcase_add_test(tcase, "NetWkstaGetInfo", + test_NetWkstaGetInfo); + + torture_rpc_tcase_add_test(tcase, "NetWkstaTransportEnum", + test_NetWkstaTransportEnum); + torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportDel", + test_NetrWkstaTransportDel); + torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportAdd", + test_NetrWkstaTransportAdd); + + torture_rpc_tcase_add_test(tcase, "NetWkstaEnumUsers", + test_NetWkstaEnumUsers); + torture_rpc_tcase_add_test(tcase, "NetrWkstaUserGetInfo", + test_NetrWkstaUserGetInfo); + + torture_rpc_tcase_add_test(tcase, "NetrUseDel", + test_NetrUseDel); + torture_rpc_tcase_add_test(tcase, "NetrUseGetInfo", + test_NetrUseGetInfo); + torture_rpc_tcase_add_test(tcase, "NetrUseEnum", + test_NetrUseEnum); + torture_rpc_tcase_add_test(tcase, "NetrUseAdd", + test_NetrUseAdd); + + torture_rpc_tcase_add_test(tcase, "NetrValidateName", + test_NetrValidateName); + torture_rpc_tcase_add_test(tcase, "NetrValidateName2", + test_NetrValidateName2); + torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameDel", + test_NetrLogonDomainNameDel); + torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameAdd", + test_NetrLogonDomainNameAdd); + torture_rpc_tcase_add_test(tcase, "NetrRemoveAlternateComputerName", + test_NetrRemoveAlternateComputerName); + torture_rpc_tcase_add_test(tcase, "NetrAddAlternateComputerName", + test_NetrAddAlternateComputerName); + test = torture_rpc_tcase_add_test(tcase, "NetrSetPrimaryComputername", + test_NetrSetPrimaryComputername); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain", + test_NetrRenameMachineInDomain); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain2", + test_NetrRenameMachineInDomain2); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrEnumerateComputerNames", + test_NetrEnumerateComputerNames); + + test = torture_rpc_tcase_add_test(tcase, "NetrJoinDomain2", + test_NetrJoinDomain2); + test->dangerous = true; + test = torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain2", + test_NetrUnjoinDomain2); + test->dangerous = true; + + torture_rpc_tcase_add_test(tcase, "NetrJoinDomain", + test_NetrJoinDomain); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain", + test_NetrUnjoinDomain); + test->dangerous = true; + torture_rpc_tcase_add_test(tcase, "NetrGetJoinInformation", + test_NetrGetJoinInformation); + torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus", + test_NetrGetJoinableOus); + torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus2", + test_NetrGetJoinableOus2); + + torture_rpc_tcase_add_test(tcase, "NetrWorkstationStatisticsGet", + test_NetrWorkstationStatisticsGet); + torture_rpc_tcase_add_test(tcase, "NetrMessageBufferSend", + test_NetrMessageBufferSend); + + return suite; +} diff --git a/source4/torture/shell.c b/source4/torture/shell.c new file mode 100644 index 0000000..d34267d --- /dev/null +++ b/source4/torture/shell.c @@ -0,0 +1,326 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006-2008 + Copyright (C) James Peach 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/readline.h" +#include "../libcli/smbreadline/smbreadline.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "torture/smbtorture.h" +#include "param/param.h" + +struct shell_command; + +typedef void (*shell_function)(const struct shell_command *, + struct torture_context *, int, const char **); + +static void shell_quit(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_help(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_set(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_run(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_list(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_auth(const struct shell_command *, + struct torture_context *, int, const char **); +static void shell_target(const struct shell_command *, + struct torture_context *, int, const char **); + +static void shell_usage(const struct shell_command *); +static bool match_command(const char *, const struct shell_command *); + +struct shell_command +{ + shell_function handler; + const char * name; + const char * usage; + const char * help; +} shell_command; + +static const struct shell_command commands[] = +{ + { + shell_auth, "auth", + "[[username | principal | domain | realm | password] STRING]", + "set authentication parameters" + }, + + { + shell_help, "help", NULL, + "print this help message" + }, + + { + shell_list, "list", NULL, + "list the available tests" + }, + + { + shell_quit, "quit", NULL, + "exit smbtorture" + }, + + { + shell_run, "run", "[TESTNAME]", + "run the specified test" + }, + + { + shell_set, "set", "[NAME VALUE]", + "print or set test configuration parameters" + }, + + { + shell_target, "target", "[TARGET]", + "print or set the test target" + } + +}; + +void torture_shell(struct torture_context *tctx) +{ + char *cline; + int argc; + const char **argv; + int ret; + int i; + + /* If we don't have a specified password, specify it as empty. This + * stops the credentials system prompting when we use the "auth" + * command to display the current auth parameters. + */ + cli_credentials_set_password(samba_cmdline_get_creds(), + "", CRED_GUESS_ENV); + + while (1) { + cline = smb_readline("torture> ", NULL, NULL); + + if (cline == NULL) + return; + +#ifdef HAVE_ADD_HISTORY + add_history(cline); +#endif + + ret = poptParseArgvString(cline, &argc, &argv); + if (ret != 0) { + fprintf(stderr, "Error parsing line\n"); + continue; + } + + for (i = 0; i < ARRAY_SIZE(commands); i++) { + if (match_command(argv[0], &commands[i])) { + argc--; + argv++; + commands[i].handler(&commands[i], + tctx, argc, argv); + break; + } + } + + free(cline); + } +} + +static void shell_quit(const struct shell_command * command, + struct torture_context *tctx, int argc, const char **argv) +{ + exit(0); +} + +static void shell_help(const struct shell_command * command, + struct torture_context *tctx, int argc, const char **argv) +{ + int i; + + if (argc == 1) { + for (i = 0; i < ARRAY_SIZE(commands); i++) { + if (match_command(argv[0], &commands[i])) { + shell_usage(&commands[i]); + return; + } + } + } else { + fprintf(stdout, "Available commands:\n"); + for (i = 0; i < ARRAY_SIZE(commands); i++) { + fprintf(stdout, "\t%s - %s\n", + commands[i].name, commands[i].help); + } + } +} + +static void shell_set(const struct shell_command *command, + struct torture_context *tctx, int argc, const char **argv) +{ + switch (argc) { + case 0: + lpcfg_dump(tctx->lp_ctx, stdout, + false /* show_defaults */, + 0 /* skip services */); + break; + + case 2: + /* We want to allow users to set any config option. Top level + * options will get checked against their static definition, but + * parametric options can't be checked and will just get stashed + * as they are provided. + */ + lpcfg_set_cmdline(tctx->lp_ctx, argv[0], argv[1]); + break; + + default: + shell_usage(command); + } +} + +static void shell_run(const struct shell_command * command, + struct torture_context *tctx, int argc, const char **argv) +{ + if (argc != 1) { + shell_usage(command); + return; + } + + torture_run_named_tests(tctx, argv[0], NULL /* restricted */); +} + +static void shell_list(const struct shell_command * command, + struct torture_context *tctx, int argc, const char **argv) +{ + if (argc != 0) { + shell_usage(command); + return; + } + + torture_print_testsuites(true); +} + +static void shell_auth(const struct shell_command * command, + struct torture_context *tctx, int argc, const char **argv) +{ + + if (argc == 0) { + const char * username; + const char * domain; + const char * realm; + const char * password; + const char * principal; + + username = cli_credentials_get_username( + samba_cmdline_get_creds()); + principal = cli_credentials_get_principal( + samba_cmdline_get_creds(), tctx); + domain = cli_credentials_get_domain(samba_cmdline_get_creds()); + realm = cli_credentials_get_realm(samba_cmdline_get_creds()); + password = cli_credentials_get_password( + samba_cmdline_get_creds()); + + printf("Username: %s\n", username ? username : ""); + printf("User Principal: %s\n", principal ? principal : ""); + printf("Domain: %s\n", domain ? domain : ""); + printf("Realm: %s\n", realm ? realm : ""); + printf("Password: %s\n", password ? password : ""); + } else if (argc == 2) { + bool result; + + if (!strcmp(argv[0], "username")) { + result = cli_credentials_set_username( + samba_cmdline_get_creds(), + argv[1], CRED_SPECIFIED); + } else if (!strcmp(argv[0], "principal")) { + result = cli_credentials_set_principal( + samba_cmdline_get_creds(), + argv[1], CRED_SPECIFIED); + } else if (!strcmp(argv[0], "domain")) { + result = cli_credentials_set_domain( + samba_cmdline_get_creds(), + argv[1], CRED_SPECIFIED); + } else if (!strcmp(argv[0], "realm")) { + result = cli_credentials_set_realm( + samba_cmdline_get_creds(), + argv[1], CRED_SPECIFIED); + } else if (!strcmp(argv[0], "password")) { + result = cli_credentials_set_password( + samba_cmdline_get_creds(), + argv[1], CRED_SPECIFIED); + } else { + shell_usage(command); + return; + } + + if (!result) { + printf("failed to set %s\n", argv[0]); + } + } else { + shell_usage(command); + } + +} + +static void shell_target(const struct shell_command *command, + struct torture_context *tctx, int argc, const char **argv) +{ + if (argc == 0) { + const char * host; + const char * share; + const char * binding; + + host = torture_setting_string(tctx, "host", NULL); + share = torture_setting_string(tctx, "share", NULL); + binding = torture_setting_string(tctx, "binding", NULL); + + printf("Target host: %s\n", host ? host : ""); + printf("Target share: %s\n", share ? share : ""); + printf("Target binding: %s\n", binding ? binding : ""); + } else if (argc == 1) { + torture_parse_target(tctx, tctx->lp_ctx, argv[0]); + } else { + shell_usage(command); + } +} + +static void shell_usage(const struct shell_command * command) +{ + if (command->usage) { + fprintf(stderr, "Usage: %s %s\n", + command->name, command->usage); + } else { + fprintf(stderr, "Usage: %s\n", + command->name); + } +} + +static bool match_command(const char * name, + const struct shell_command * command) +{ + if (!strcmp(name, command->name)) { + return true; + } + + if (name[0] == command->name[0] && name[1] == '\0') { + return true; + } + + return false; +} diff --git a/source4/torture/smb2/acls.c b/source4/torture/smb2/acls.c new file mode 100644 index 0000000..019886e --- /dev/null +++ b/source4/torture/smb2/acls.c @@ -0,0 +1,3340 @@ +/* + Unix SMB/CIFS implementation. + + test security descriptor operations for SMB2 + + Copyright (C) Zack Kirsch 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "libcli/resolve/resolve.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "lib/param/param.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define BASEDIR "smb2-testsd" + +#define CHECK_ACCESS_IGNORE SEC_STD_SYNCHRONIZE + +#define CHECK_ACCESS_FLAGS(_fh, flags) do { \ + union smb_fileinfo _q; \ + _q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; \ + _q.access_information.in.file.handle = (_fh); \ + status = smb2_getinfo_file(tree, tctx, &_q); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + /* Handle a Vista bug where SEC_STD_SYNCHRONIZE doesn't come back. */ \ + if ((((flags) & CHECK_ACCESS_IGNORE) == CHECK_ACCESS_IGNORE) && \ + ((_q.access_information.out.access_flags & CHECK_ACCESS_IGNORE) != CHECK_ACCESS_IGNORE)) { \ + torture_comment(tctx, "SKIPPING (Vista bug): (%s) Incorrect access_flags 0x%08x - should be 0x%08x\n", \ + __location__, _q.access_information.out.access_flags, (flags)); \ + } \ + if ((_q.access_information.out.access_flags & ~CHECK_ACCESS_IGNORE) != \ + (((flags) & ~CHECK_ACCESS_IGNORE))) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect access_flags 0x%08x - should be 0x%08x\n", \ + __location__, _q.access_information.out.access_flags, (flags)); \ + ret = false; \ + goto done; \ + } \ +} while (0) + +#define FAIL_UNLESS(__cond) \ + do { \ + if (__cond) {} else { \ + torture_result(tctx, TORTURE_FAIL, "%s) condition violated: %s\n", \ + __location__, #__cond); \ + ret = false; goto done; \ + } \ + } while(0) + +#define CHECK_SECURITY_DESCRIPTOR(_sd1, _sd2) do { \ + if (!security_descriptor_equal(_sd1, _sd2)) { \ + torture_warning(tctx, "security descriptors don't match!\n"); \ + torture_warning(tctx, "got:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd1); \ + torture_warning(tctx, "expected:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd2); \ + torture_result(tctx, TORTURE_FAIL, \ + "%s: security descriptors don't match!\n", \ + __location__); \ + ret = false; \ + } \ +} while (0) + +/* + test the behaviour of the well known SID_CREATOR_OWNER sid, and some generic + mapping bits + Note: This test was copied from raw/acls.c. +*/ +static bool test_creator_sid(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *fname = BASEDIR "\\creator.txt"; + bool ret = true; + struct smb2_handle handle = {{0}}; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig, *sd2; + const char *owner_sid; + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING SID_CREATOR_OWNER\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "try open for write\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic write\n"); + io.in.desired_access = SEC_GENERIC_WRITE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.in.desired_access = SEC_GENERIC_READ; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "set a sec desc allowing no write by owner\n"); + sd = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "check that sd has been mapped correctly\n"); + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + torture_comment(tctx, "try open for write\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_FILE_READ_DATA); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for generic write\n"); + io.in.desired_access = SEC_GENERIC_WRITE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.in.desired_access = SEC_GENERIC_READ; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_RIGHTS_FILE_READ); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "set a sec desc allowing generic read by owner\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_READ | SEC_STD_ALL, + 0, + NULL); + + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "check that generic read has been mapped correctly\n"); + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + torture_comment(tctx, "try open for write\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for read\n"); + io.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_FILE_READ_DATA); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for generic write\n"); + io.in.desired_access = SEC_GENERIC_WRITE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "try open for generic read\n"); + io.in.desired_access = SEC_GENERIC_READ; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_RIGHTS_FILE_READ); + smb2_util_close(tree, io.out.file.handle); + + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.in.sd = sd_orig; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + +done: + smb2_util_close(tree, handle); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + + +/* + test the mapping of the SEC_GENERIC_xx bits to SEC_STD_xx and + SEC_FILE_xx bits + Note: This test was copied from raw/acls.c. +*/ +static bool test_generic_bits(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *fname = BASEDIR "\\generic.txt"; + bool ret = true; + struct smb2_handle handle = {{0}}; + int i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig, *sd2; + const char *owner_sid; + const struct { + uint32_t gen_bits; + uint32_t specific_bits; + } file_mappings[] = { + { 0, 0 }, + { SEC_GENERIC_READ, SEC_RIGHTS_FILE_READ }, + { SEC_GENERIC_WRITE, SEC_RIGHTS_FILE_WRITE }, + { SEC_GENERIC_EXECUTE, SEC_RIGHTS_FILE_EXECUTE }, + { SEC_GENERIC_ALL, SEC_RIGHTS_FILE_ALL }, + { SEC_FILE_READ_DATA, SEC_FILE_READ_DATA }, + { SEC_FILE_READ_ATTRIBUTE, SEC_FILE_READ_ATTRIBUTE } + }; + const struct { + uint32_t gen_bits; + uint32_t specific_bits; + } dir_mappings[] = { + { 0, 0 }, + { SEC_GENERIC_READ, SEC_RIGHTS_DIR_READ }, + { SEC_GENERIC_WRITE, SEC_RIGHTS_DIR_WRITE }, + { SEC_GENERIC_EXECUTE, SEC_RIGHTS_DIR_EXECUTE }, + { SEC_GENERIC_ALL, SEC_RIGHTS_DIR_ALL } + }; + bool has_restore_privilege = false; + bool has_take_ownership_privilege = false; + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING FILE GENERIC BITS\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + +/* + * XXX: The smblsa calls use SMB as their transport - need to get rid of + * dependency. + */ +/* + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); +*/ + + for (i=0;iowner_sid); + +/* + * XXX: The smblsa calls use SMB as their transport - need to get rid of + * dependency. + */ +/* + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); + +*/ + for (i=0;isession); + return ret; +} + + +/* + see what access bits the owner of a file always gets + Note: This test was copied from raw/acls.c. +*/ +static bool test_owner_bits(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *fname = BASEDIR "\\test_owner_bits.txt"; + bool ret = true; + struct smb2_handle handle = {{0}}; + int i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig; + const char *owner_sid; + uint32_t expected_bits; + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING FILE OWNER BITS\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + +/* + * XXX: The smblsa calls use SMB as their transport - need to get rid of + * dependency. + */ +/* + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No"); + + status = smblsa_sid_check_privilege(cli, + owner_sid, + sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP)); + has_take_ownership_privilege = NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "smblsa_sid_check_privilege - %s\n", nt_errstr(status)); + } + torture_comment(tctx, "SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No"); +*/ + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + expected_bits = SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE; + + for (i=0;i<16;i++) { + uint32_t bit = (1<session); + return ret; +} + + + +/* + test the inheritance of ACL flags onto new files and directories + Note: This test was copied from raw/acls.c. +*/ +static bool test_inheritance(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + const char *fname2 = BASEDIR "\\inheritance\\testdir"; + bool ret = true; + struct smb2_handle handle = {{0}}; + struct smb2_handle handle2 = {{0}}; + int i; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd2, *sd_orig=NULL, *sd_def1, *sd_def2; + const char *owner_sid; + const struct dom_sid *creator_owner; + const struct { + uint32_t parent_flags; + uint32_t file_flags; + uint32_t dir_flags; + } test_flags[] = { + { + 0, + 0, + 0 + }, + { + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY, + }, + { + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT, + 0, + 0, + }, + { + SEC_ACE_FLAG_INHERIT_ONLY | + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + SEC_ACE_FLAG_OBJECT_INHERIT, + 0, + 0, + } + }; + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACL INHERITANCE\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = 0; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = dname; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + /* + * The Windows Default ACL for a new file, when there is no ACL to be + * inherited: FullControl for the owner and SYSTEM. + */ + sd_def1 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + SID_NT_SYSTEM, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + NULL); + + /* + * Use this in the case the system being tested does not add an ACE for + * the SYSTEM SID. + */ + sd_def2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, + 0, + NULL); + + creator_owner = dom_sid_parse_talloc(tctx, SID_CREATOR_OWNER); + + for (i=0;idacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid)) { + torture_warning(tctx, "Bad sd in child file at %d\n", i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + ret = false; + goto check_dir; + } + + if (q.query_secdesc.out.sd->dacl->aces[0].flags != + test_flags[i].file_flags) { + torture_warning(tctx, "incorrect file_flags 0x%x - expected 0x%x for parent 0x%x with (i=%d)\n", + q.query_secdesc.out.sd->dacl->aces[0].flags, + test_flags[i].file_flags, + test_flags[i].parent_flags, + i); + ret = false; + } + + check_dir: + io.in.fname = fname2; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + + q.query_secdesc.in.file.handle = handle2; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, handle2); + smb2_util_rmdir(tree, fname2); + + if (!(test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) && + (!(test_flags[i].parent_flags & SEC_ACE_FLAG_OBJECT_INHERIT) || + (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT))) { + if (!security_descriptor_equal(q.query_secdesc.out.sd, sd_def1) && + !security_descriptor_equal(q.query_secdesc.out.sd, sd_def2)) { + torture_warning(tctx, "Expected default sd for dir at %d:\n", i); + NDR_PRINT_DEBUG(security_descriptor, sd_def1); + torture_warning(tctx, "got:\n"); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + } + continue; + } + + if ((test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) && + (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid) || + q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) { + torture_warning(tctx, "(CI & NP) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_warning(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } else if (test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 2 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + sd_orig->owner_sid) || + q.query_secdesc.out.sd->dacl->aces[1].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[1].trustee, + creator_owner) || + q.query_secdesc.out.sd->dacl->aces[0].flags != 0 || + q.query_secdesc.out.sd->dacl->aces[1].flags != + (test_flags[i].dir_flags | SEC_ACE_FLAG_INHERIT_ONLY)) { + torture_warning(tctx, "(CI) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_warning(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } else { + if (q.query_secdesc.out.sd->dacl == NULL || + q.query_secdesc.out.sd->dacl->num_aces != 1 || + q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA || + !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee, + creator_owner) || + q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) { + torture_warning(tctx, "(0) Bad sd in child dir - expected 0x%x for parent 0x%x (i=%d)\n", + test_flags[i].dir_flags, + test_flags[i].parent_flags, i); + NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd); + torture_warning(tctx, "FYI, here is the parent sd:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + continue; + } + } + } + + torture_comment(tctx, "Testing access checks on inherited create with %s\n", fname1); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Check DACL we just set. */ + torture_comment(tctx, "checking new sd\n"); + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + io.in.fname = fname1; + io.in.create_options = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_RIGHTS_FILE_ALL); + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, handle2); + + sd2 = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + 0, + NULL); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + status = smb2_create(tree, tctx, &io); + if (NT_STATUS_IS_OK(status)) { + torture_warning(tctx, "failed: w2k3 ACL bug (allowed open when ACL should deny)\n"); + ret = false; + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_RIGHTS_FILE_ALL); + smb2_util_close(tree, handle2); + } else { + if (torture_setting_bool(tctx, "hide_on_access_denied", + false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + } + + torture_comment(tctx, "trying without execute\n"); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.desired_access = SEC_RIGHTS_FILE_ALL & ~SEC_FILE_EXECUTE; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "and with full permissions again\n"); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_FILE_WRITE_DATA); + smb2_util_close(tree, handle2); + + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, handle); + + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_FILE_WRITE_DATA); + smb2_util_close(tree, handle2); + + smb2_util_unlink(tree, fname1); + smb2_util_rmdir(tree, dname); + +done: + if (sd_orig != NULL) { + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb2_setinfo_file(tree, &set); + } + + smb2_util_close(tree, handle); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_inheritance_flags(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + bool ret = true; + struct smb2_handle handle = {{0}}; + struct smb2_handle handle2 = {{0}}; + int i, j; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd2, *sd_orig=NULL; + const char *owner_sid; + struct { + uint32_t parent_set_sd_type; /* 3 options */ + uint32_t parent_set_ace_inherit; /* 1 option */ + uint32_t parent_get_sd_type; + uint32_t parent_get_ace_inherit; + uint32_t child_get_sd_type; + uint32_t child_get_ace_inherit; + } tflags[16] = {{0}}; /* 2^4 */ + + for (i = 0; i < 15; i++) { + torture_comment(tctx, "i=%d:", i); + + if (i & 1) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + torture_comment(tctx, "AUTO_INHERITED, "); + } + if (i & 2) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERIT_REQ; + torture_comment(tctx, "AUTO_INHERIT_REQ, "); + } + if (i & 4) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_PROTECTED; + torture_comment(tctx, "PROTECTED, "); + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_PROTECTED; + } + if (i & 8) { + tflags[i].parent_set_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, "INHERITED, "); + tflags[i].parent_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + } + + if ((tflags[i].parent_set_sd_type & + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) == + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) { + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent is AUTO INHERITED"); + } + + if (tflags[i].parent_set_ace_inherit & + SEC_ACE_FLAG_INHERITED_ACE) { + tflags[i].parent_get_ace_inherit = + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent ACE is INHERITED"); + } + + torture_comment(tctx, "\n"); + } + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACL INHERITANCE FLAGS\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = dname; + + torture_comment(tctx, "creating initial directory %s\n", dname); + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "getting original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + for (i=0; i < ARRAY_SIZE(tflags); i++) { + torture_comment(tctx, "setting a new sd on directory, pass #%d\n", i); + + sd = security_descriptor_dacl_create(tctx, + tflags[i].parent_set_sd_type, + NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + tflags[i].parent_set_ace_inherit, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check DACL we just set, except change the bits to what they + * should be. + */ + torture_comment(tctx, " checking new sd\n"); + + /* REQ bit should always be false. */ + sd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + + if ((tflags[i].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + /* Create file. */ + torture_comment(tctx, " creating file %s\n", fname1); + io.in.fname = fname1; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_RIGHTS_FILE_ALL); + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " checking sd on file %s\n", fname1); + sd2 = security_descriptor_dacl_create(tctx, + tflags[i].child_get_sd_type, + owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + tflags[i].child_get_ace_inherit, + NULL); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + /* + * Set new sd on file ... prove that the bits have nothing to + * do with the parents bits when manually setting an ACL. The + * _AUTO_INHERITED bit comes directly from the ACL set. + */ + for (j = 0; j < ARRAY_SIZE(tflags); j++) { + torture_comment(tctx, " setting new file sd, pass #%d\n", j); + + /* Change sd type. */ + sd2->type &= ~(SEC_DESC_DACL_AUTO_INHERITED | + SEC_DESC_DACL_AUTO_INHERIT_REQ | + SEC_DESC_DACL_PROTECTED); + sd2->type |= tflags[j].parent_set_sd_type; + + sd2->dacl->aces[0].flags &= + ~SEC_ACE_FLAG_INHERITED_ACE; + sd2->dacl->aces[0].flags |= + tflags[j].parent_set_ace_inherit; + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle2; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd2; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Check DACL we just set. */ + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + if ((tflags[j].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + } + + smb2_util_close(tree, handle2); + smb2_util_unlink(tree, fname1); + } + +done: + smb2_util_close(tree, handle); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +/* + * This is basically a copy of test_inheritance_flags() with an additional twist + * to change the owner of the testfile, verifying that the security descriptor + * flags are not altered. + */ +static bool test_sd_flags_vs_chown(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + bool ret = true; + struct smb2_handle handle = {{0}}; + struct smb2_handle handle2 = {{0}}; + int i, j; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd2, *sd_orig=NULL; + struct security_descriptor *owner_sd = NULL; + const char *owner_sid_string = NULL; + struct dom_sid *owner_sid = NULL; + struct dom_sid world_sid = global_sid_World; + struct { + uint32_t parent_set_sd_type; /* 3 options */ + uint32_t parent_set_ace_inherit; /* 1 option */ + uint32_t parent_get_sd_type; + uint32_t parent_get_ace_inherit; + uint32_t child_get_sd_type; + uint32_t child_get_ace_inherit; + } tflags[16] = {{0}}; /* 2^4 */ + + owner_sd = security_descriptor_dacl_create(tctx, + 0, + SID_WORLD, + NULL, + NULL); + torture_assert_not_null_goto(tctx, owner_sd, ret, done, + "security_descriptor_dacl_create failed\n"); + + for (i = 0; i < 15; i++) { + torture_comment(tctx, "i=%d:", i); + + if (i & 1) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + torture_comment(tctx, "AUTO_INHERITED, "); + } + if (i & 2) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_AUTO_INHERIT_REQ; + torture_comment(tctx, "AUTO_INHERIT_REQ, "); + } + if (i & 4) { + tflags[i].parent_set_sd_type |= + SEC_DESC_DACL_PROTECTED; + torture_comment(tctx, "PROTECTED, "); + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_PROTECTED; + } + if (i & 8) { + tflags[i].parent_set_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, "INHERITED, "); + tflags[i].parent_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + } + + if ((tflags[i].parent_set_sd_type & + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) == + (SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ)) { + tflags[i].parent_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_sd_type |= + SEC_DESC_DACL_AUTO_INHERITED; + tflags[i].child_get_ace_inherit |= + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent is AUTO INHERITED"); + } + + if (tflags[i].parent_set_ace_inherit & + SEC_ACE_FLAG_INHERITED_ACE) { + tflags[i].parent_get_ace_inherit = + SEC_ACE_FLAG_INHERITED_ACE; + torture_comment(tctx, " ... parent ACE is INHERITED"); + } + + torture_comment(tctx, "\n"); + } + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACL INHERITANCE FLAGS\n"); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = dname; + + torture_comment(tctx, "creating initial directory %s\n", dname); + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "getting original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = sd_orig->owner_sid; + owner_sid_string = dom_sid_string(tctx, sd_orig->owner_sid); + torture_comment(tctx, "owner_sid is %s\n", owner_sid_string); + + for (i=0; i < ARRAY_SIZE(tflags); i++) { + torture_comment(tctx, "setting a new sd on directory, pass #%d\n", i); + + sd = security_descriptor_dacl_create(tctx, + tflags[i].parent_set_sd_type, + NULL, NULL, + owner_sid_string, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT | + tflags[i].parent_set_ace_inherit, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check DACL we just set, except change the bits to what they + * should be. + */ + torture_comment(tctx, " checking new sd\n"); + + /* REQ bit should always be false. */ + sd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + + if ((tflags[i].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd); + + /* Create file. */ + torture_comment(tctx, " creating file %s\n", fname1); + io.in.fname = fname1; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + CHECK_ACCESS_FLAGS(handle2, SEC_RIGHTS_FILE_ALL); + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, " checking sd on file %s\n", fname1); + sd2 = security_descriptor_dacl_create(tctx, + tflags[i].child_get_sd_type, + owner_sid_string, NULL, + owner_sid_string, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + tflags[i].child_get_ace_inherit, + NULL); + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + /* + * Set new sd on file ... prove that the bits have nothing to + * do with the parents bits when manually setting an ACL. The + * _AUTO_INHERITED bit comes directly from the ACL set. + */ + for (j = 0; j < ARRAY_SIZE(tflags); j++) { + torture_comment(tctx, " setting new file sd, pass #%d\n", j); + + /* Change sd type. */ + sd2->type &= ~(SEC_DESC_DACL_AUTO_INHERITED | + SEC_DESC_DACL_AUTO_INHERIT_REQ | + SEC_DESC_DACL_PROTECTED); + sd2->type |= tflags[j].parent_set_sd_type; + + sd2->dacl->aces[0].flags &= + ~SEC_ACE_FLAG_INHERITED_ACE; + sd2->dacl->aces[0].flags |= + tflags[j].parent_set_ace_inherit; + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle2; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd2; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Check DACL we just set. */ + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; + if ((tflags[j].parent_get_sd_type & SEC_DESC_DACL_AUTO_INHERITED) == 0) + sd2->type &= ~SEC_DESC_DACL_AUTO_INHERITED; + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + + /* + * Check that changing owner doesn't affect SD flags. + * + * Do this by first changing owner to world and then + * back to the original owner. Afterwards compare SD, + * should be the same. + */ + owner_sd->owner_sid = &world_sid; + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle2; + set.set_secdesc.in.secinfo_flags = SECINFO_OWNER; + set.set_secdesc.in.sd = owner_sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + owner_sd->owner_sid = owner_sid; + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle2; + set.set_secdesc.in.secinfo_flags = SECINFO_OWNER; + set.set_secdesc.in.sd = owner_sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + q.query_secdesc.in.file.handle = handle2; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_SECURITY_DESCRIPTOR(q.query_secdesc.out.sd, sd2); + torture_assert_goto(tctx, ret, ret, done, "CHECK_SECURITY_DESCRIPTOR failed\n"); + } + + smb2_util_close(tree, handle2); + smb2_util_unlink(tree, fname1); + } + +done: + smb2_util_close(tree, handle); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +/* + test dynamic acl inheritance + Note: This test was copied from raw/acls.c. +*/ +static bool test_inheritance_dynamic(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *dname = BASEDIR "\\inheritance"; + const char *fname1 = BASEDIR "\\inheritance\\testfile"; + bool ret = true; + struct smb2_handle handle = {{0}}; + struct smb2_handle handle2 = {{0}}; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig=NULL; + const char *owner_sid; + + torture_comment(tctx, "TESTING DYNAMIC ACL INHERITANCE\n"); + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = 0; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = dname; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ; + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "create a file with an inherited acl\n"); + io.in.fname = fname1; + io.in.create_options = 0; + io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + smb2_util_close(tree, handle2); + + torture_comment(tctx, "try and access file with base rights - should be OK\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + smb2_util_close(tree, handle2); + + torture_comment(tctx, "try and access file with extra rights - should be denied\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(tctx, "update parent sd\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ; + + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "try and access file with base rights - should be OK\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle2 = io.out.file.handle; + smb2_util_close(tree, handle2); + + + torture_comment(tctx, "try and access now - should be OK if dynamic inheritance works\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE; + status = smb2_create(tree, tctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, "Server does not have dynamic inheritance\n"); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Server does have dynamic inheritance\n"); + } + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + smb2_util_unlink(tree, fname1); + +done: + torture_comment(tctx, "put back original sd\n"); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + status = smb2_setinfo_file(tree, &set); + + smb2_util_close(tree, handle); + smb2_util_rmdir(tree, dname); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + + return ret; +} + +#define CHECK_STATUS_FOR_BIT_ACTION(status, bits, action) do { \ + if (!(bits & desired_64)) {\ + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); \ + action; \ + } else { \ + CHECK_STATUS(status, NT_STATUS_OK); \ + } \ +} while (0) + +#define CHECK_STATUS_FOR_BIT(status, bits, access) do { \ + if (NT_STATUS_IS_OK(status)) { \ + if (!(granted & access)) {\ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s but flags 0x%08X are not granted! granted[0x%08X] desired[0x%08X]\n", \ + __location__, nt_errstr(status), access, granted, desired); \ + goto done; \ + } \ + } else { \ + if (granted & access) {\ + ret = false; \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s but flags 0x%08X are granted! granted[0x%08X] desired[0x%08X]\n", \ + __location__, nt_errstr(status), access, granted, desired); \ + goto done; \ + } \ + } \ + CHECK_STATUS_FOR_BIT_ACTION(status, bits, do {} while (0)); \ +} while (0) + +#if 0 +/* test what access mask is needed for getting and setting security_descriptors */ +/* Note: This test was copied from raw/acls.c. */ +static bool test_sd_get_set(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_create io; + union smb_fileinfo fi; + union smb_setfileinfo si; + struct security_descriptor *sd; + struct security_descriptor *sd_owner = NULL; + struct security_descriptor *sd_group = NULL; + struct security_descriptor *sd_dacl = NULL; + struct security_descriptor *sd_sacl = NULL; + struct smb2_handle handle; + const char *fname = BASEDIR "\\sd_get_set.txt"; + uint64_t desired_64; + uint32_t desired = 0, granted; + int i = 0; +#define NO_BITS_HACK (((uint64_t)1)<<32) + uint64_t open_bits = + SEC_MASK_GENERIC | + SEC_FLAG_SYSTEM_SECURITY | + SEC_FLAG_MAXIMUM_ALLOWED | + SEC_STD_ALL | + SEC_FILE_ALL | + NO_BITS_HACK; + uint64_t get_owner_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_owner_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER; + uint64_t get_group_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_group_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER; + uint64_t get_dacl_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL; + uint64_t set_dacl_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_DAC; + uint64_t get_sacl_bits = SEC_FLAG_SYSTEM_SECURITY; + uint64_t set_sacl_bits = SEC_FLAG_SYSTEM_SECURITY; + + if (!smb2_util_setup_dir(tctx, tree, BASEDIR)) + return false; + + torture_comment(tctx, "TESTING ACCESS MASKS FOR SD GET/SET\n"); + + /* first create a file with full access for everyone */ + sd = security_descriptor_dacl_create(tctx, + 0, SID_NT_ANONYMOUS, SID_BUILTIN_USERS, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + NULL); + sd->type |= SEC_DESC_SACL_PRESENT; + sd->sacl = NULL; + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_GENERIC_ALL; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + io.in.sec_desc = sd; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + status = smb2_util_close(tree, handle); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * now try each access_mask bit and no bit at all in a loop + * and see what's allowed + * NOTE: if i == 32 it means access_mask = 0 (see NO_BITS_HACK above) + */ + for (i=0; i <= 32; i++) { + desired_64 = ((uint64_t)1) << i; + desired = (uint32_t)desired_64; + + /* first open the file with the desired access */ + io.level = RAW_OPEN_SMB2; + io.in.desired_access = desired; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS_FOR_BIT_ACTION(status, open_bits, goto next); + handle = io.out.file.handle; + + /* then check what access was granted */ + fi.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + fi.access_information.in.file.handle = handle; + status = smb2_getinfo_file(tree, tctx, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + granted = fi.access_information.out.access_flags; + + /* test the owner */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.handle = handle; + fi.query_secdesc.in.secinfo_flags = SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_owner_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_owner = fi.query_secdesc.out.sd; + } else if (!sd_owner) { + sd_owner = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.handle = handle; + si.set_secdesc.in.secinfo_flags = SECINFO_OWNER; + si.set_secdesc.in.sd = sd_owner; + status = smb2_setinfo_file(tree, &si); + CHECK_STATUS_FOR_BIT(status, set_owner_bits, SEC_STD_WRITE_OWNER); + + /* test the group */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.handle = handle; + fi.query_secdesc.in.secinfo_flags = SECINFO_GROUP; + status = smb2_getinfo_file(tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_group_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_group = fi.query_secdesc.out.sd; + } else if (!sd_group) { + sd_group = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.handle = handle; + si.set_secdesc.in.secinfo_flags = SECINFO_GROUP; + si.set_secdesc.in.sd = sd_group; + status = smb2_setinfo_file(tree, &si); + CHECK_STATUS_FOR_BIT(status, set_group_bits, SEC_STD_WRITE_OWNER); + + /* test the DACL */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.handle = handle; + fi.query_secdesc.in.secinfo_flags = SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_dacl_bits, SEC_STD_READ_CONTROL); + if (fi.query_secdesc.out.sd) { + sd_dacl = fi.query_secdesc.out.sd; + } else if (!sd_dacl) { + sd_dacl = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.handle = handle; + si.set_secdesc.in.secinfo_flags = SECINFO_DACL; + si.set_secdesc.in.sd = sd_dacl; + status = smb2_setinfo_file(tree, &si); + CHECK_STATUS_FOR_BIT(status, set_dacl_bits, SEC_STD_WRITE_DAC); + + /* test the SACL */ + ZERO_STRUCT(fi); + fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + fi.query_secdesc.in.file.handle = handle; + fi.query_secdesc.in.secinfo_flags = SECINFO_SACL; + status = smb2_getinfo_file(tree, tctx, &fi); + CHECK_STATUS_FOR_BIT(status, get_sacl_bits, SEC_FLAG_SYSTEM_SECURITY); + if (fi.query_secdesc.out.sd) { + sd_sacl = fi.query_secdesc.out.sd; + } else if (!sd_sacl) { + sd_sacl = sd; + } + si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + si.set_secdesc.in.file.handle = handle; + si.set_secdesc.in.secinfo_flags = SECINFO_SACL; + si.set_secdesc.in.sd = sd_sacl; + status = smb2_setinfo_file(tree, &si); + CHECK_STATUS_FOR_BIT(status, set_sacl_bits, SEC_FLAG_SYSTEM_SECURITY); + + /* close the handle */ + status = smb2_util_close(tree, handle); + CHECK_STATUS(status, NT_STATUS_OK); +next: + continue; + } + +done: + smb2_util_close(tree, handle); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + smb2_logoff(tree->session); + + return ret; +} +#endif + +static bool test_access_based(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree *tree1 = NULL; + NTSTATUS status; + struct smb2_create io; + const char *fname = BASEDIR "\\testfile"; + bool ret = true; + struct smb2_handle fhandle, dhandle; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig=NULL; + const char *owner_sid; + uint32_t flags = 0; + /* + * Can't test without SEC_STD_READ_CONTROL as we + * own the file and implicitly have SEC_STD_READ_CONTROL. + */ + uint32_t access_masks[] = { + /* Full READ access. */ + SEC_STD_READ_CONTROL|FILE_READ_DATA| + FILE_READ_ATTRIBUTES|FILE_READ_EA, + + /* Missing FILE_READ_EA. */ + SEC_STD_READ_CONTROL|FILE_READ_DATA| + FILE_READ_ATTRIBUTES, + + /* Missing FILE_READ_ATTRIBUTES. */ + SEC_STD_READ_CONTROL|FILE_READ_DATA| + FILE_READ_EA, + + /* Missing FILE_READ_DATA. */ + SEC_STD_READ_CONTROL| + FILE_READ_ATTRIBUTES|FILE_READ_EA, + }; + unsigned int i; + unsigned int count; + struct smb2_find f; + union smb_search_data *d; + + ZERO_STRUCT(fhandle); + ZERO_STRUCT(dhandle); + + if (!torture_smb2_con_share(tctx, "hideunread", &tree1)) { + torture_result(tctx, TORTURE_FAIL, "(%s) Unable to connect " + "to share 'hideunread'\n", + __location__); + ret = false; + goto done; + } + + flags = smb2cli_tcon_flags(tree1->smbXcli); + + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + + torture_comment(tctx, "TESTING ACCESS BASED ENUMERATION\n"); + + if ((flags & SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM)==0) { + torture_result(tctx, TORTURE_FAIL, "(%s) No access enumeration " + "on share 'hideunread'\n", + __location__); + ret = false; + goto done; + } + + if (!smb2_util_setup_dir(tctx, tree1, BASEDIR)) { + torture_result(tctx, TORTURE_FAIL, "(%s) Unable to setup %s\n", + __location__, BASEDIR); + ret = false; + goto done; + } + + /* Get a handle to the BASEDIR directory. */ + status = torture_smb2_testdir(tree1, BASEDIR, &dhandle); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, dhandle); + ZERO_STRUCT(dhandle); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = 0; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + + status = smb2_create(tree1, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fhandle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = fhandle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree1, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + torture_comment(tctx, "owner_sid is %s\n", owner_sid); + + /* Setup for the search. */ + ZERO_STRUCT(f); + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_REOPEN; + f.in.max_response_size = 0x1000; + f.in.level = SMB2_FIND_DIRECTORY_INFO; + + for (i = 0; i < ARRAY_SIZE(access_masks); i++) { + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + access_masks[i]|SEC_STD_SYNCHRONIZE, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = fhandle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree1, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now see if we can see the file in a directory listing. */ + + /* Re-open dhandle. */ + status = torture_smb2_testdir(tree1, BASEDIR, &dhandle); + CHECK_STATUS(status, NT_STATUS_OK); + f.in.file.handle = dhandle; + + count = 0; + d = NULL; + status = smb2_find_level(tree1, tree1, &f, &count, &d); + TALLOC_FREE(d); + + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree1, dhandle); + ZERO_STRUCT(dhandle); + + if (i == 0) { + /* We should see the first sd. */ + if (count != 3) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Normal SD - Unable " + "to see file %s\n", + __location__, + BASEDIR); + ret = false; + goto done; + } + } else { + /* But no others. */ + if (count != 2) { + torture_result(tctx, TORTURE_FAIL, + "(%s) SD 0x%x - can " + "see file %s\n", + __location__, + access_masks[i], + BASEDIR); + ret = false; + goto done; + } + } + } + +done: + + if (tree1) { + smb2_util_close(tree1, fhandle); + smb2_util_close(tree1, dhandle); + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + smb2_tdis(tree1); + smb2_logoff(tree1->session); + } + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +/* + * test Owner Rights, S-1-3-4 + */ +static bool test_owner_rights(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\owner_right.txt"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + const char *owner_sid = NULL; + NTSTATUS mxac_status; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, + "smb2_util_setup_dir failed\n"); + + torture_comment(tctx, "TESTING OWNER RIGHTS\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC |SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL|SECINFO_OWNER, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * Add a 2 element ACL + * SEC_RIGHTS_FILE_READ for the owner, + * SEC_FILE_WRITE_DATA for SID_OWNER_RIGHTS. + * + * Proves that the owner and SID_OWNER_RIGHTS + * ACE entries are additive. + */ + sd = security_descriptor_dacl_create(tctx, 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ, + 0, + SID_OWNER_RIGHTS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.query_maximal_access = true, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + handle = cr.out.file.handle; + + mxac_status = NT_STATUS(cr.out.maximal_access_status); + torture_assert_ntstatus_ok_goto(tctx, mxac_status, ret, done, + "smb2_setinfo_file failed\n"); + + /* + * For some reasons Windows 2016 doesn't set SEC_STD_DELETE but we + * do. Mask it out so the test passes against Samba and Windows. + */ + torture_assert_int_equal_goto(tctx, + cr.out.maximal_access & ~SEC_STD_DELETE, + SEC_RIGHTS_FILE_READ | + SEC_FILE_WRITE_DATA, + ret, done, + "Wrong maximum access\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + * test Owner Rights with a leading DENY ACE, S-1-3-4 + */ +static bool test_owner_rights_deny(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\owner_right_deny.txt"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + const char *owner_sid = NULL; + NTSTATUS mxac_status; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, + "smb2_util_setup_dir failed\n"); + + torture_comment(tctx, "TESTING OWNER RIGHTS DENY\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC |SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL|SECINFO_OWNER, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * Add a 2 element ACL + * DENY SEC_FILE_DATA_READ for SID_OWNER_RIGHTS + * SEC_FILE_READ_DATA for the owner. + * + * Proves that the owner and SID_OWNER_RIGHTS + * ACE entries are additive. + */ + sd = security_descriptor_dacl_create(tctx, 0, NULL, NULL, + SID_OWNER_RIGHTS, + SEC_ACE_TYPE_ACCESS_DENIED, + SEC_FILE_READ_DATA, + 0, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.query_maximal_access = true, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + handle = cr.out.file.handle; + + mxac_status = NT_STATUS(cr.out.maximal_access_status); + torture_assert_ntstatus_ok_goto(tctx, mxac_status, ret, done, + "smb2_setinfo_file failed\n"); + + /* + * For some reasons Windows 2016 doesn't set SEC_STD_DELETE but we + * do. Mask it out so the test passes against Samba and Windows. + */ + torture_assert_int_equal_goto(tctx, + cr.out.maximal_access & ~SEC_STD_DELETE, + SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA, + ret, done, + "Wrong maximum access\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + * test Owner Rights with a trailing DENY ACE, S-1-3-4 + */ +static bool test_owner_rights_deny1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\owner_right_deny1.txt"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + const char *owner_sid = NULL; + NTSTATUS mxac_status; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, + "smb2_util_setup_dir failed\n"); + + torture_comment(tctx, "TESTING OWNER RIGHTS DENY1\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC |SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL|SECINFO_OWNER, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * Add a 3 element ACL + * + * SEC_RIGHTS_FILE_READ allow for owner. + * SEC_FILE_WRITE_DATA allow for SID-OWNER-RIGHTS. + * SEC_FILE_WRITE_DATA|SEC_FILE_READ_DATA) deny for SID-OWNER-RIGHTS. + * + * Shows on Windows that trailing DENY entries don't + * override granted permissions in max access calculations. + */ + sd = security_descriptor_dacl_create(tctx, 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ, + 0, + SID_OWNER_RIGHTS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA, + 0, + SID_OWNER_RIGHTS, + SEC_ACE_TYPE_ACCESS_DENIED, + (SEC_FILE_WRITE_DATA| + SEC_FILE_READ_DATA), + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.query_maximal_access = true, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + handle = cr.out.file.handle; + + mxac_status = NT_STATUS(cr.out.maximal_access_status); + torture_assert_ntstatus_ok_goto(tctx, mxac_status, ret, done, + "smb2_setinfo_file failed\n"); + + /* + * For some reasons Windows 2016 doesn't set SEC_STD_DELETE but we + * do. Mask it out so the test passes against Samba and Windows. + */ + torture_assert_int_equal_goto(tctx, + cr.out.maximal_access & ~SEC_STD_DELETE, + SEC_RIGHTS_FILE_READ | SEC_FILE_WRITE_DATA, + ret, done, + "Wrong maximum access\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + * test that shows that a DENY ACE doesn't remove rights granted + * by a previous ALLOW ACE. + */ +static bool test_deny1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_deny1.txt"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + const char *owner_sid = NULL; + NTSTATUS mxac_status; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, + "smb2_util_setup_dir failed\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC |SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL|SECINFO_OWNER, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * Add a 2 element ACL + * + * SEC_RIGHTS_FILE_READ|SEC_FILE_WRITE_DATA allow for owner. + * SEC_FILE_WRITE_DATA deny for owner + * + * Shows on Windows that trailing DENY entries don't + * override granted permissions. + */ + sd = security_descriptor_dacl_create(tctx, 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ|SEC_FILE_WRITE_DATA, + 0, + owner_sid, + SEC_ACE_TYPE_ACCESS_DENIED, + SEC_FILE_WRITE_DATA, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_WRITE_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.query_maximal_access = true, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + mxac_status = NT_STATUS(cr.out.maximal_access_status); + torture_assert_ntstatus_ok_goto(tctx, mxac_status, ret, done, + "Wrong maximum access status\n"); + + /* + * For some reasons Windows 2016 doesn't set SEC_STD_DELETE but we + * do. Mask it out so the test passes against Samba and Windows. + * SEC_STD_WRITE_DAC comes from being the owner. + */ + torture_assert_int_equal_goto(tctx, + cr.out.maximal_access & ~SEC_STD_DELETE, + SEC_RIGHTS_FILE_READ | + SEC_FILE_WRITE_DATA | + SEC_STD_WRITE_DAC, + ret, done, + "Wrong maximum access\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + * test SEC_FLAG_MAXIMUM_ALLOWED with not-granted access + * + * When access_mask contains SEC_FLAG_MAXIMUM_ALLOWED, the server must still + * process other bits from access_mask. Eg if access_mask contains a right that + * the requester doesn't have, the function must validate that against the + * effective permissions. + */ +static bool test_mxac_not_granted(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_mxac_not_granted.txt"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + const char *owner_sid = NULL; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, + "smb2_util_setup_dir failed\n"); + + torture_comment(tctx, "TESTING OWNER RIGHTS DENY\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC |SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL|SECINFO_OWNER, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + sd = security_descriptor_dacl_create(tctx, 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_READ_DATA, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED | + SEC_FILE_WRITE_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, + "Wrong smb2_create result\n"); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_overwrite_read_only_file(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create c; + const char *fname = BASEDIR "\\test_overwrite_read_only_file.txt"; + struct smb2_handle handle = {{0}}; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd = NULL, *sd_orig = NULL; + const char *owner_sid = NULL; + int i; + bool ret = true; + + struct tcase { + int disposition; + const char *disposition_string; + NTSTATUS expected_status; + } tcases[] = { +#define TCASE(d, s) { \ + .disposition = d, \ + .disposition_string = #d, \ + .expected_status = s, \ + } + TCASE(NTCREATEX_DISP_OPEN, NT_STATUS_OK), + TCASE(NTCREATEX_DISP_SUPERSEDE, NT_STATUS_ACCESS_DENIED), + TCASE(NTCREATEX_DISP_OVERWRITE, NT_STATUS_ACCESS_DENIED), + TCASE(NTCREATEX_DISP_OVERWRITE_IF, NT_STATUS_ACCESS_DENIED), + }; +#undef TCASE + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, "smb2_util_setup_dir not ok"); + + c = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = c.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_READ_DATA, + 0, + NULL); + + ZERO_STRUCT(set); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + smb2_util_close(tree, handle); + ZERO_STRUCT(handle); + + for (i = 0; i < ARRAY_SIZE(tcases); i++) { + torture_comment(tctx, "Verify open with %s disposition\n", + tcases[i].disposition_string); + + c = (struct smb2_create) { + .in.create_disposition = tcases[i].disposition, + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &c); + smb2_util_close(tree, c.out.file.handle); + torture_assert_ntstatus_equal_goto( + tctx, status, tcases[i].expected_status, ret, done, + "smb2_create failed\n"); + }; + + torture_comment(tctx, "put back original sd\n"); + + c = (struct smb2_create) { + .in.desired_access = SEC_STD_WRITE_DAC, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = c.out.file.handle; + + ZERO_STRUCT(set); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd_orig; + + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + smb2_util_close(tree, handle); + ZERO_STRUCT(handle); + +done: + smb2_util_close(tree, handle); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + basic testing of SMB2 ACLs +*/ +struct torture_suite *torture_smb2_acls_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "acls"); + + torture_suite_add_1smb2_test(suite, "CREATOR", test_creator_sid); + torture_suite_add_1smb2_test(suite, "GENERIC", test_generic_bits); + torture_suite_add_1smb2_test(suite, "OWNER", test_owner_bits); + torture_suite_add_1smb2_test(suite, "INHERITANCE", test_inheritance); + torture_suite_add_1smb2_test(suite, "INHERITFLAGS", test_inheritance_flags); + torture_suite_add_1smb2_test(suite, "SDFLAGSVSCHOWN", test_sd_flags_vs_chown); + torture_suite_add_1smb2_test(suite, "DYNAMIC", test_inheritance_dynamic); +#if 0 + /* XXX This test does not work against XP or Vista. */ + torture_suite_add_1smb2_test(suite, "GETSET", test_sd_get_set); +#endif + torture_suite_add_1smb2_test(suite, "ACCESSBASED", test_access_based); + torture_suite_add_1smb2_test(suite, "OWNER-RIGHTS", test_owner_rights); + torture_suite_add_1smb2_test(suite, "OWNER-RIGHTS-DENY", + test_owner_rights_deny); + torture_suite_add_1smb2_test(suite, "OWNER-RIGHTS-DENY1", + test_owner_rights_deny1); + torture_suite_add_1smb2_test(suite, "DENY1", + test_deny1); + torture_suite_add_1smb2_test(suite, "MXAC-NOT-GRANTED", + test_mxac_not_granted); + torture_suite_add_1smb2_test(suite, "OVERWRITE_READ_ONLY_FILE", test_overwrite_read_only_file); + + suite->description = talloc_strdup(suite, "SMB2-ACLS tests"); + + return suite; +} + +static bool test_acls_non_canonical_flags(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_acls_non_canonical_flags.txt"; + struct smb2_create cr; + struct smb2_handle testdirh = {{0}}; + struct smb2_handle handle = {{0}}; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct security_descriptor *sd_orig = NULL; + struct security_descriptor *sd = NULL; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + sd = security_descriptor_dacl_create(tctx, + SEC_DESC_DACL_AUTO_INHERITED + | SEC_DESC_DACL_AUTO_INHERIT_REQ, + NULL, + NULL, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_DIR_ALL, + SEC_ACE_FLAG_OBJECT_INHERIT + | SEC_ACE_FLAG_CONTAINER_INHERIT, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = testdirh, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = testdirh, + .query_secdesc.in.secinfo_flags = SECINFO_DACL, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + + torture_assert_goto(tctx, sd_orig->type & SEC_DESC_DACL_AUTO_INHERITED, + ret, done, "Missing SEC_DESC_DACL_AUTO_INHERITED\n"); + + /* + * SD with SEC_DESC_DACL_AUTO_INHERITED but without + * SEC_DESC_DACL_AUTO_INHERITED_REQ, so the resulting SD should not have + * SEC_DESC_DACL_AUTO_INHERITED on a Windows box. + * + * But as we're testing against a share with + * + * "acl flag inherited canonicalization = no" + * + * the resulting SD should have acl flag inherited canonicalization set. + */ + sd = security_descriptor_dacl_create(tctx, + SEC_DESC_DACL_AUTO_INHERITED, + NULL, + NULL, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "SD create failed\n"); + + si = (union smb_setfileinfo) { + .set_secdesc.level = RAW_SFILEINFO_SEC_DESC, + .set_secdesc.in.file.handle = handle, + .set_secdesc.in.secinfo_flags = SECINFO_DACL, + .set_secdesc.in.sd = sd, + }; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED , + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + gi = (union smb_fileinfo) { + .query_secdesc.level = RAW_FILEINFO_SEC_DESC, + .query_secdesc.in.file.handle = handle, + .query_secdesc.in.secinfo_flags = SECINFO_DACL, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + sd_orig = gi.query_secdesc.out.sd; + torture_assert_goto(tctx, sd_orig->type & SEC_DESC_DACL_AUTO_INHERITED, + ret, done, "Missing SEC_DESC_DACL_AUTO_INHERITED\n"); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, testdirh); + } + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +struct torture_suite *torture_smb2_acls_non_canonical_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "acls_non_canonical"); + + torture_suite_add_1smb2_test(suite, "flags", test_acls_non_canonical_flags); + return suite; +} diff --git a/source4/torture/smb2/attr.c b/source4/torture/smb2/attr.c new file mode 100644 index 0000000..bc474d2 --- /dev/null +++ b/source4/torture/smb2/attr.c @@ -0,0 +1,710 @@ +/* + Unix SMB/CIFS implementation. + + openattr tester + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) David Mulder 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "libcli/security/security_descriptor.h" +#include "torture/smb2/proto.h" + +static const uint32_t open_attrs_table[] = { + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_SYSTEM, + + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM, +}; + +struct trunc_open_results { + unsigned int num; + uint32_t init_attr; + uint32_t trunc_attr; + uint32_t result_attr; +}; + +static const struct trunc_open_results attr_results[] = { + { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE }, + { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE }, + { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY }, + { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE }, + { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE }, + { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY }, + { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }, + { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }, + { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN }, + { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM }, + { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN }, + { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN }, + { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM }, + { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM } +}; + +static NTSTATUS smb2_setatr(struct smb2_tree *tree, const char *name, + uint32_t attrib) +{ + NTSTATUS status; + struct smb2_create create_io = {0}; + union smb_setfileinfo io; + + create_io.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_ATTRIBUTE; + create_io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = NTCREATEX_DISP_OPEN; + create_io.in.fname = name; + status = smb2_create(tree, tree, &create_io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(io); + io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + io.basic_info.in.file.handle = create_io.out.file.handle; + io.basic_info.in.attrib = attrib; + status = smb2_setinfo_file(tree, &io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = smb2_util_close(tree, create_io.out.file.handle); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return status; +} + +bool torture_smb2_openattrtest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + const char *fname = "openattr.file"; + uint16_t attr; + unsigned int i, j, k, l; + int ret = true; + + for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32_t); i++) { + struct smb2_create create_io = {0}; + smb2_setatr(tree, fname, FILE_ATTRIBUTE_NORMAL); + smb2_util_unlink(tree, fname); + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_WRITE_DATA; + create_io.in.file_attributes = open_attrs_table[i]; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "open %d (1) of %s failed (%s)", + i, fname, nt_errstr(status))); + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "close %d (1) of %s failed (%s)", + i, fname, nt_errstr(status))); + + for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) { + create_io = (struct smb2_create){0}; + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_READ_DATA| + SEC_FILE_WRITE_DATA; + create_io.in.file_attributes = open_attrs_table[j]; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + + if (!NT_STATUS_IS_OK(status)) { + for (l = 0; l < ARRAY_SIZE(attr_results); l++) { + torture_assert_goto(tctx, + attr_results[l].num != k, + ret, error_exit, + talloc_asprintf(tctx, + "[%d] trunc open 0x%x " + "-> 0x%x of %s failed " + "- should have " + "succeeded !(%s)", + k, open_attrs_table[i], + open_attrs_table[j], + fname, + nt_errstr(status))); + } + torture_assert_ntstatus_equal_goto(tctx, + status, NT_STATUS_ACCESS_DENIED, + ret, error_exit, + talloc_asprintf(tctx, + "[%d] trunc open 0x%x " + "-> 0x%x failed with " + "wrong error code %s", + k, open_attrs_table[i], + open_attrs_table[j], + nt_errstr(status))); + k++; + continue; + } + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, talloc_asprintf(tctx, + "close %d (2) of %s failed (%s)", j, + fname, nt_errstr(status))); + + status = smb2_util_getatr(tree, fname, &attr, NULL, NULL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, talloc_asprintf(tctx, + "getatr(2) failed (%s)", + nt_errstr(status))); + + for (l = 0; l < ARRAY_SIZE(attr_results); l++) { + if (attr_results[l].num == k) { + if (attr != attr_results[l].result_attr || + open_attrs_table[i] != attr_results[l].init_attr || + open_attrs_table[j] != attr_results[l].trunc_attr) { + ret = false; + torture_fail_goto(tctx, error_exit, + talloc_asprintf(tctx, + "[%d] getatr check " + "failed. [0x%x] trunc " + "[0x%x] got attr 0x%x," + " should be 0x%x", + k, open_attrs_table[i], + open_attrs_table[j], + (unsigned int)attr, + attr_results[l].result_attr)); + } + break; + } + } + k++; + } + } +error_exit: + smb2_setatr(tree, fname, FILE_ATTRIBUTE_NORMAL); + smb2_util_unlink(tree, fname); + + + return ret; +} + +bool torture_smb2_winattrtest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "winattr1.file"; + const char *dname = "winattr1.dir"; + uint16_t attr; + uint16_t j; + uint32_t aceno; + bool ret = true; + union smb_fileinfo query, query_org; + NTSTATUS status; + struct security_descriptor *sd1 = NULL, *sd2 = NULL; + struct smb2_create create_io = {0}; + ZERO_STRUCT(query); + ZERO_STRUCT(query_org); + + /* Test winattrs for file */ + smb2_util_unlink(tree, fname); + + /* Open a file*/ + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | + SEC_STD_READ_CONTROL; + create_io.in.file_attributes = 0; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = FILE_SUPERSEDE; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "open(1) of %s failed (%s)\n", + fname, nt_errstr(status))); + + /* Get security descriptor and store it*/ + query_org.generic.level = RAW_FILEINFO_SEC_DESC; + query_org.generic.in.file.handle = create_io.out.file.handle; + query_org.query_secdesc.in.secinfo_flags = SECINFO_OWNER| + SECINFO_GROUP| + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &query_org); + if(!NT_STATUS_IS_OK(status)){ + NTSTATUS s = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, error_exit, + talloc_asprintf(tctx, + "close(1) of %s failed (%s)\n", + fname, nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, talloc_asprintf(tctx, + "smb2_getinfo_file(1) of %s failed (%s)\n", + fname, nt_errstr(status))); + } + sd1 = query_org.query_secdesc.out.sd; + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "close(1) of %s failed (%s)\n", + fname, nt_errstr(status))); + + /*Set and get attributes*/ + for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) { + status = smb2_setatr(tree, fname, open_attrs_table[j]); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, + talloc_asprintf(tctx, "setatr(2) failed (%s)", + nt_errstr(status))); + + status = smb2_util_getatr(tree, fname, &attr, NULL, NULL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, + talloc_asprintf(tctx, "getatr(2) failed (%s)", + nt_errstr(status))); + + /* Check the result */ + torture_assert_goto(tctx, attr == open_attrs_table[j], ret, + error_exit, talloc_asprintf(tctx, + "getatr check failed. \ + Attr applied [0x%x],got attr 0x%x, \ + should be 0x%x ", open_attrs_table[j], + (uint16_t)attr, open_attrs_table[j])); + + create_io = (struct smb2_create){0}; + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE| + SEC_STD_READ_CONTROL; + create_io.in.file_attributes = 0; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = FILE_OPEN_IF; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, + talloc_asprintf(tctx, "open(2) of %s failed (%s)\n", + fname, nt_errstr(status))); + /*Get security descriptor */ + query.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + query.query_secdesc.in.file.handle = create_io.out.file.handle; + query.query_secdesc.in.secinfo_flags = SECINFO_OWNER| + SECINFO_GROUP| + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &query); + if(!NT_STATUS_IS_OK(status)){ + NTSTATUS s = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, + error_exit, + talloc_asprintf(tctx, + "close(2) of %s failed (%s)\n", + fname, nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, + talloc_asprintf(tctx, + "smb2_getinfo_file(2) of %s failed (%s)\n", + fname, nt_errstr(status))); + } + sd2 = query.query_secdesc.out.sd; + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "close(2) of %s failed (%s)\n", + fname, nt_errstr(status))); + + /*Compare security descriptors -- Must be same*/ + for (aceno=0;(sd1->dacl&&aceno < sd1->dacl->num_aces);aceno++){ + struct security_ace *ace1 = &sd1->dacl->aces[aceno]; + struct security_ace *ace2 = &sd2->dacl->aces[aceno]; + + torture_assert_goto(tctx, security_ace_equal(ace1, ace2), + ret, error_exit, + "ACLs changed! Not expected!\n"); + } + + torture_comment(tctx, "[%d] setattr = [0x%x] got attr 0x%x\n", + j, open_attrs_table[j], attr ); + + } + + +/* Check for Directory. */ + + smb2_deltree(tree, dname); + smb2_util_rmdir(tree, dname); + + /* Open a directory */ + create_io = (struct smb2_create){0}; + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_RIGHTS_DIR_ALL; + create_io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create_io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create_io.in.security_flags = 0; + create_io.in.fname = dname; + status = smb2_create(tree, tctx, &create_io); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, + "open (1) of %s failed (%s)", + dname, nt_errstr(status))); + + + /* Get Security Descriptor */ + query_org.generic.level = RAW_FILEINFO_SEC_DESC; + query_org.generic.in.file.handle = create_io.out.file.handle; + status = smb2_getinfo_file(tree, tctx, &query_org); + if(!NT_STATUS_IS_OK(status)){ + NTSTATUS s = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, error_exit, + talloc_asprintf(tctx, + "close(1) of %s failed (%s)\n", + dname, nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, talloc_asprintf(tctx, + "smb2_getinfo_file(1) of %s failed (%s)\n", dname, + nt_errstr(status))); + } + sd1 = query_org.query_secdesc.out.sd; + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, + "close (1) of %s failed (%s)", dname, + nt_errstr(status))); + + /* Set and get win attributes*/ + for (j = 1; j < ARRAY_SIZE(open_attrs_table); j++) { + + status = smb2_setatr(tree, dname, open_attrs_table[j]); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "setatr(2) failed (%s)", + nt_errstr(status))); + + status = smb2_util_getatr(tree, dname, &attr, NULL, NULL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "getatr(2) failed (%s)", + nt_errstr(status))); + + torture_comment(tctx, "[%d] setatt = [0x%x] got attr 0x%x\n", + j, open_attrs_table[j], attr ); + + /* Check the result */ + torture_assert_goto(tctx, + attr == (open_attrs_table[j]|FILE_ATTRIBUTE_DIRECTORY), + ret, error_exit, talloc_asprintf(tctx, + "getatr check failed. set attr " + "[0x%x], got attr 0x%x, should be 0x%x\n", + open_attrs_table[j], (uint16_t)attr, + (unsigned int)(open_attrs_table[j]|FILE_ATTRIBUTE_DIRECTORY))); + + create_io = (struct smb2_create){0}; + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_RIGHTS_DIR_READ; + create_io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = NTCREATEX_DISP_OPEN; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = dname; + status = smb2_create(tree, tctx, &create_io); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, + "open (2) of %s failed (%s)", + dname, nt_errstr(status))); + /* Get security descriptor */ + query.generic.level = RAW_FILEINFO_SEC_DESC; + query.generic.in.file.handle = create_io.out.file.handle; + status = smb2_getinfo_file(tree, tctx, &query); + if(!NT_STATUS_IS_OK(status)){ + NTSTATUS s = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, error_exit, + talloc_asprintf(tctx, + "close (2) of %s failed (%s)", dname, + nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, + talloc_asprintf(tctx, + "smb2_getinfo_file(2) of %s failed(%s)\n", + dname, nt_errstr(status))); + } + sd2 = query.query_secdesc.out.sd; + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, + "close (2) of %s failed (%s)", dname, + nt_errstr(status))); + + /* Security descriptor must be same*/ + for (aceno=0;(sd1->dacl&&aceno < sd1->dacl->num_aces);aceno++){ + struct security_ace *ace1 = &sd1->dacl->aces[aceno]; + struct security_ace *ace2 = &sd2->dacl->aces[aceno]; + + torture_assert_goto(tctx, security_ace_equal(ace1, ace2), + ret, error_exit, + "ACLs changed! Not expected!\n"); + } + + } + +error_exit: + smb2_setatr(tree, fname, FILE_ATTRIBUTE_NORMAL); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, dname); + smb2_util_rmdir(tree, dname); + + return ret; +} + +bool torture_smb2_winattr2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "winattr2.file"; + struct smb2_create c = {0}; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(tree, fname); + + /* Create a file with FILE_ATTRIBUTE_ARCHIVE */ + c = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_ARCHIVE, + .in.share_access = NTCREATEX_SHARE_ACCESS_NONE, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + status = smb2_util_close(tree, c.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + + /* Reopen file with different attributes */ + c = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_ARCHIVE | + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_READONLY, + .in.share_access = NTCREATEX_SHARE_ACCESS_NONE, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + status = smb2_util_close(tree, c.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + + torture_assert_int_equal_goto(tctx, + c.out.file_attr, + FILE_ATTRIBUTE_ARCHIVE, + ret, done, + "Wrong attributes\n"); + +done: + smb2_util_unlink(tree, fname); + return ret; +} + +bool torture_smb2_sdreadtest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "sdread.file"; + bool ret = true; + union smb_fileinfo query; + NTSTATUS status; + struct security_descriptor *sd = NULL; + struct smb2_create create_io = {0}; + uint32_t sd_bits[] = { SECINFO_OWNER, + SECINFO_GROUP, + SECINFO_DACL }; + size_t i; + + ZERO_STRUCT(query); + + smb2_util_unlink(tree, fname); + + /* Create then close a file*/ + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + create_io.in.file_attributes = 0; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = FILE_SUPERSEDE; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "open(1) of %s failed (%s)\n", + fname, nt_errstr(status))); + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, error_exit, + talloc_asprintf(tctx, "close(1) of %s failed (%s)\n", + fname, nt_errstr(status))); + + /* + * Open the file with READ_ATTRIBUTES *only*, + * no READ_CONTROL. + * + * This should deny access for any attempt to + * get a security descriptor if we ask for + * any of OWNER|GROUP|DACL, but if + * we ask for *NO* info but still ask for + * the security descriptor, then Windows + * returns an ACL but with zero entries + * for OWNER|GROUP|DACL. + */ + + create_io = (struct smb2_create){0}; + create_io.in.create_flags = 0; + create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create_io.in.file_attributes = 0; + create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create_io.in.create_disposition = FILE_OPEN; + create_io.in.create_options = 0; + create_io.in.security_flags = 0; + create_io.in.fname = fname; + status = smb2_create(tree, tctx, &create_io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, + error_exit, + talloc_asprintf(tctx, "open(2) of %s failed (%s)\n", + fname, nt_errstr(status))); + + /* Check asking for SD fails ACCESS_DENIED with actual bits set. */ + for (i = 0; i < ARRAY_SIZE(sd_bits); i++) { + query.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + query.query_secdesc.in.file.handle = create_io.out.file.handle; + query.query_secdesc.in.secinfo_flags = sd_bits[i]; + + status = smb2_getinfo_file(tree, tctx, &query); + + /* Must return ACESS_DENIED. */ + if(!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)){ + NTSTATUS s = smb2_util_close(tree, + create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, + error_exit, + talloc_asprintf(tctx, + "close(2) of %s failed (%s)\n", + fname, nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, + talloc_asprintf(tctx, + "smb2_getinfo_file(2) of %s failed (%s)\n", + fname, nt_errstr(status))); + } + } + + /* + * Get security descriptor whilst asking for *NO* bits. + * This succeeds even though we don't have READ_CONTROL + * access but returns an SD with zero data. + */ + query.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + query.query_secdesc.in.file.handle = create_io.out.file.handle; + query.query_secdesc.in.secinfo_flags = 0; + + status = smb2_getinfo_file(tree, tctx, &query); + if(!NT_STATUS_IS_OK(status)){ + NTSTATUS s = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, s, ret, error_exit, + talloc_asprintf(tctx, + "close(3) of %s failed (%s)\n", + fname, nt_errstr(s))); + ret = false; + torture_fail_goto(tctx, error_exit, talloc_asprintf(tctx, + "smb2_getinfo_file(3) of %s failed (%s)\n", + fname, nt_errstr(status))); + } + + sd = query.query_secdesc.out.sd; + + /* Check it's empty. */ + torture_assert_goto(tctx, + (sd->owner_sid == NULL), + ret, + error_exit, + "sd->owner_sid != NULL\n"); + + torture_assert_goto(tctx, + (sd->group_sid == NULL), + ret, + error_exit, + "sd->group_sid != NULL\n"); + + torture_assert_goto(tctx, + (sd->dacl == NULL), + ret, + error_exit, + "sd->dacl != NULL\n"); + + status = smb2_util_close(tree, create_io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, + status, + ret, + error_exit, + talloc_asprintf(tctx, "close(4) of %s failed (%s)\n", + fname, + nt_errstr(status))); + +error_exit: + + smb2_setatr(tree, fname, FILE_ATTRIBUTE_NORMAL); + smb2_util_unlink(tree, fname); + + return ret; +} diff --git a/source4/torture/smb2/bench.c b/source4/torture/smb2/bench.c new file mode 100644 index 0000000..a91ca6c --- /dev/null +++ b/source4/torture/smb2/bench.c @@ -0,0 +1,1376 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 bench test suite + + Copyright (C) Stefan Metzmacher 2022 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" + +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/security.h" +#include "lib/events/events.h" + +#define FNAME "test_create.dat" +#define DNAME "smb2_open" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + return false; \ + }} while (0) + +#define CHECK_EQUAL(v, correct) do { \ + if (v != correct) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value for %s 0x%08llx - " \ + "should be 0x%08llx\n", \ + __location__, #v, \ + (unsigned long long)v, \ + (unsigned long long)correct); \ + return false; \ + }} while (0) + +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \ + if (abs(t1-t2) > 2) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + timestring(tctx, t1), \ + timestring(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (llabs((int64_t)(t-t2)) > 20000) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + nt_time_string(tctx, t), \ + nt_time_string(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != (finfo.all_info.out.field)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for field %s 0x%x - 0x%x\n", \ + __location__, #field, (int)v,\ + (int)(finfo.all_info.out.field)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)correct); \ + ret = false; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.basic_info.in.file.handle = h1; \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, \ + "(%s) Failed to set attrib 0x%x on %s\n", \ + __location__, (unsigned int)(sattrib), fname); \ + }} while (0) + +/* + stress testing keepalive iops + */ + +struct test_smb2_bench_echo_conn; +struct test_smb2_bench_echo_loop; + +struct test_smb2_bench_echo_state { + struct torture_context *tctx; + size_t num_conns; + struct test_smb2_bench_echo_conn *conns; + size_t num_loops; + struct test_smb2_bench_echo_loop *loops; + size_t pending_loops; + struct timeval starttime; + int timecount; + int timelimit; + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + bool ok; + bool stop; +}; + +struct test_smb2_bench_echo_conn { + struct test_smb2_bench_echo_state *state; + int idx; + struct smb2_tree *tree; +}; + +struct test_smb2_bench_echo_loop { + struct test_smb2_bench_echo_state *state; + struct test_smb2_bench_echo_conn *conn; + int idx; + struct tevent_immediate *im; + struct tevent_req *req; + struct timeval starttime; + uint64_t num_started; + uint64_t num_finished; + uint64_t total_finished; + uint64_t max_finished; + double total_latency; + double min_latency; + double max_latency; + NTSTATUS error; +}; + +static void test_smb2_bench_echo_loop_do( + struct test_smb2_bench_echo_loop *loop); + +static void test_smb2_bench_echo_loop_start(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_smb2_bench_echo_loop *loop = + (struct test_smb2_bench_echo_loop *) + private_data; + + test_smb2_bench_echo_loop_do(loop); +} + +static void test_smb2_bench_echo_loop_done(struct tevent_req *req); + +static void test_smb2_bench_echo_loop_do( + struct test_smb2_bench_echo_loop *loop) +{ + struct test_smb2_bench_echo_state *state = loop->state; + + loop->num_started += 1; + loop->starttime = timeval_current(); + loop->req = smb2cli_echo_send(state->loops, + state->tctx->ev, + loop->conn->tree->session->transport->conn, + 1000); + torture_assert_goto(state->tctx, loop->req != NULL, + state->ok, asserted, "smb2cli_echo_send"); + + tevent_req_set_callback(loop->req, + test_smb2_bench_echo_loop_done, + loop); + return; +asserted: + state->stop = true; +} + +static void test_smb2_bench_echo_loop_done(struct tevent_req *req) +{ + struct test_smb2_bench_echo_loop *loop = + (struct test_smb2_bench_echo_loop *) + _tevent_req_callback_data(req); + struct test_smb2_bench_echo_state *state = loop->state; + double latency = timeval_elapsed(&loop->starttime); + TALLOC_CTX *frame = talloc_stackframe(); + + torture_assert_goto(state->tctx, loop->req == req, + state->ok, asserted, __location__); + loop->error = smb2cli_echo_recv(req); + torture_assert_ntstatus_ok_goto(state->tctx, loop->error, + state->ok, asserted, __location__); + SMB_ASSERT(latency >= 0.000001); + + if (loop->num_finished == 0) { + /* first round */ + loop->min_latency = latency; + loop->max_latency = latency; + } + + loop->num_finished += 1; + loop->total_finished += 1; + loop->total_latency += latency; + + if (latency < loop->min_latency) { + loop->min_latency = latency; + } + + if (latency > loop->max_latency) { + loop->max_latency = latency; + } + + if (loop->total_finished >= loop->max_finished) { + if (state->pending_loops > 0) { + state->pending_loops -= 1; + } + if (state->pending_loops == 0) { + goto asserted; + } + } + + TALLOC_FREE(frame); + test_smb2_bench_echo_loop_do(loop); + return; +asserted: + state->stop = true; + TALLOC_FREE(frame); +} + +static void test_smb2_bench_echo_progress(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct test_smb2_bench_echo_state *state = + (struct test_smb2_bench_echo_state *)private_data; + uint64_t num_echos = 0; + double total_echo_latency = 0; + double min_echo_latency = 0; + double max_echo_latency = 0; + double avs_echo_latency = 0; + size_t i; + + state->timecount += 1; + + for (i=0;inum_loops;i++) { + struct test_smb2_bench_echo_loop *loop = + &state->loops[i]; + + num_echos += loop->num_finished; + total_echo_latency += loop->total_latency; + if (min_echo_latency == 0.0 && loop->min_latency != 0.0) { + min_echo_latency = loop->min_latency; + } + if (loop->min_latency < min_echo_latency) { + min_echo_latency = loop->min_latency; + } + if (max_echo_latency == 0.0) { + max_echo_latency = loop->max_latency; + } + if (loop->max_latency > max_echo_latency) { + max_echo_latency = loop->max_latency; + } + loop->num_finished = 0; + loop->total_latency = 0.0; + } + + state->num_finished += num_echos; + state->total_latency += total_echo_latency; + if (state->min_latency == 0.0 && min_echo_latency != 0.0) { + state->min_latency = min_echo_latency; + } + if (min_echo_latency < state->min_latency) { + state->min_latency = min_echo_latency; + } + if (state->max_latency == 0.0) { + state->max_latency = max_echo_latency; + } + if (max_echo_latency > state->max_latency) { + state->max_latency = max_echo_latency; + } + + if (state->timecount < state->timelimit) { + te = tevent_add_timer(state->tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_echo_progress, + state); + torture_assert_goto(state->tctx, te != NULL, + state->ok, asserted, "tevent_add_timer"); + + if (!torture_setting_bool(state->tctx, "progress", true)) { + return; + } + + avs_echo_latency = total_echo_latency / num_echos; + + torture_comment(state->tctx, + "%.2f second: " + "echo[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f] \r", + timeval_elapsed(&state->starttime), + (unsigned long long)num_echos, + avs_echo_latency, + min_echo_latency, + max_echo_latency); + return; + } + + avs_echo_latency = state->total_latency / state->num_finished; + num_echos = state->num_finished / state->timelimit; + + torture_comment(state->tctx, + "%.2f second: " + "echo[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f]\n", + timeval_elapsed(&state->starttime), + (unsigned long long)num_echos, + avs_echo_latency, + state->min_latency, + state->max_latency); + +asserted: + state->stop = true; +} + +static bool test_smb2_bench_echo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct test_smb2_bench_echo_state *state = NULL; + bool ret = true; + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int torture_qdepth = torture_setting_int(tctx, "qdepth", 1); + size_t i; + size_t li = 0; + int looplimit = torture_setting_int(tctx, "looplimit", -1); + int timelimit = torture_setting_int(tctx, "timelimit", 10); + struct tevent_timer *te = NULL; + uint32_t timeout_msec; + + state = talloc_zero(tctx, struct test_smb2_bench_echo_state); + torture_assert(tctx, state != NULL, __location__); + state->tctx = tctx; + state->num_conns = torture_nprocs; + state->conns = talloc_zero_array(state, + struct test_smb2_bench_echo_conn, + state->num_conns); + torture_assert(tctx, state->conns != NULL, __location__); + state->num_loops = torture_nprocs * torture_qdepth; + state->loops = talloc_zero_array(state, + struct test_smb2_bench_echo_loop, + state->num_loops); + torture_assert(tctx, state->loops != NULL, __location__); + state->ok = true; + state->timelimit = MAX(timelimit, 1); + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + torture_comment(tctx, "Opening %zu connections\n", state->num_conns); + + for (i=0;inum_conns;i++) { + struct smb2_tree *ct = NULL; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + size_t pcli; + + state->conns[i].state = state; + state->conns[i].idx = i; + + if (!torture_smb2_connection(tctx, &ct)) { + torture_comment(tctx, "Failed opening %zu/%zu connections\n", i, state->num_conns); + return false; + } + state->conns[i].tree = talloc_steal(state->conns, ct); + + smb2cli_conn_set_max_credits(ct->session->transport->conn, 8192); + smb2cli_ioctl(ct->session->transport->conn, + timeout_msec, + ct->session->smbXcli, + ct->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + UINT32_MAX, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + ct, + &out_input_buffer, + &out_output_buffer); + torture_assert(tctx, + smbXcli_conn_is_connected(ct->session->transport->conn), + "smbXcli_conn_is_connected"); + + for (pcli = 0; pcli < torture_qdepth; pcli++) { + struct test_smb2_bench_echo_loop *loop = &state->loops[li]; + + loop->idx = li++; + if (looplimit != -1) { + loop->max_finished = looplimit; + } else { + loop->max_finished = UINT64_MAX; + } + loop->state = state; + loop->conn = &state->conns[i]; + loop->im = tevent_create_immediate(state->loops); + torture_assert(tctx, loop->im != NULL, __location__); + + tevent_schedule_immediate(loop->im, + tctx->ev, + test_smb2_bench_echo_loop_start, + loop); + } + } + + torture_comment(tctx, "Opened %zu connections with qdepth=%d => %zu loops\n", + state->num_conns, torture_qdepth, state->num_loops); + + torture_comment(tctx, "Running for %d seconds\n", state->timelimit); + + state->starttime = timeval_current(); + state->pending_loops = state->num_loops; + + te = tevent_add_timer(tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_echo_progress, + state); + torture_assert(tctx, te != NULL, __location__); + + while (!state->stop) { + int rc = tevent_loop_once(tctx->ev); + torture_assert_int_equal(tctx, rc, 0, "tevent_loop_once"); + } + + torture_comment(tctx, "%.2f seconds\n", timeval_elapsed(&state->starttime)); + TALLOC_FREE(state); + return ret; +} + +/* + stress testing path base operations + e.g. contention on lockting.tdb records + */ + +struct test_smb2_bench_path_contention_shared_conn; +struct test_smb2_bench_path_contention_shared_loop; + +struct test_smb2_bench_path_contention_shared_state { + struct torture_context *tctx; + size_t num_conns; + struct test_smb2_bench_path_contention_shared_conn *conns; + size_t num_loops; + struct test_smb2_bench_path_contention_shared_loop *loops; + struct timeval starttime; + int timecount; + int timelimit; + struct { + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + } opens; + struct { + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + } closes; + bool ok; + bool stop; +}; + +struct test_smb2_bench_path_contention_shared_conn { + struct test_smb2_bench_path_contention_shared_state *state; + int idx; + struct smb2_tree *tree; +}; + +struct test_smb2_bench_path_contention_shared_loop { + struct test_smb2_bench_path_contention_shared_state *state; + struct test_smb2_bench_path_contention_shared_conn *conn; + int idx; + struct tevent_immediate *im; + struct { + struct smb2_create io; + struct smb2_request *req; + struct timeval starttime; + uint64_t num_started; + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + } opens; + struct { + struct smb2_close io; + struct smb2_request *req; + struct timeval starttime; + uint64_t num_started; + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + } closes; + NTSTATUS error; +}; + +static void test_smb2_bench_path_contention_loop_open( + struct test_smb2_bench_path_contention_shared_loop *loop); + +static void test_smb2_bench_path_contention_loop_start(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_smb2_bench_path_contention_shared_loop *loop = + (struct test_smb2_bench_path_contention_shared_loop *) + private_data; + + test_smb2_bench_path_contention_loop_open(loop); +} + +static void test_smb2_bench_path_contention_loop_opened(struct smb2_request *req); + +static void test_smb2_bench_path_contention_loop_open( + struct test_smb2_bench_path_contention_shared_loop *loop) +{ + struct test_smb2_bench_path_contention_shared_state *state = loop->state; + + loop->opens.num_started += 1; + loop->opens.starttime = timeval_current(); + loop->opens.req = smb2_create_send(loop->conn->tree, &loop->opens.io); + torture_assert_goto(state->tctx, loop->opens.req != NULL, + state->ok, asserted, "smb2_create_send"); + + loop->opens.req->async.fn = test_smb2_bench_path_contention_loop_opened; + loop->opens.req->async.private_data = loop; + return; +asserted: + state->stop = true; +} + +static void test_smb2_bench_path_contention_loop_close( + struct test_smb2_bench_path_contention_shared_loop *loop); + +static void test_smb2_bench_path_contention_loop_opened(struct smb2_request *req) +{ + struct test_smb2_bench_path_contention_shared_loop *loop = + (struct test_smb2_bench_path_contention_shared_loop *) + req->async.private_data; + struct test_smb2_bench_path_contention_shared_state *state = loop->state; + double latency = timeval_elapsed(&loop->opens.starttime); + TALLOC_CTX *frame = talloc_stackframe(); + + torture_assert_goto(state->tctx, loop->opens.req == req, + state->ok, asserted, __location__); + loop->error = smb2_create_recv(req, frame, &loop->opens.io); + torture_assert_ntstatus_ok_goto(state->tctx, loop->error, + state->ok, asserted, __location__); + ZERO_STRUCT(loop->opens.io.out.blobs); + SMB_ASSERT(latency >= 0.000001); + + if (loop->opens.num_finished == 0) { + /* first round */ + loop->opens.min_latency = latency; + loop->opens.max_latency = latency; + } + + loop->opens.num_finished += 1; + loop->opens.total_latency += latency; + + if (latency < loop->opens.min_latency) { + loop->opens.min_latency = latency; + } + + if (latency > loop->opens.max_latency) { + loop->opens.max_latency = latency; + } + + TALLOC_FREE(frame); + test_smb2_bench_path_contention_loop_close(loop); + return; +asserted: + state->stop = true; + TALLOC_FREE(frame); +} + +static void test_smb2_bench_path_contention_loop_closed(struct smb2_request *req); + +static void test_smb2_bench_path_contention_loop_close( + struct test_smb2_bench_path_contention_shared_loop *loop) +{ + struct test_smb2_bench_path_contention_shared_state *state = loop->state; + + loop->closes.num_started += 1; + loop->closes.starttime = timeval_current(); + loop->closes.io.in.file = loop->opens.io.out.file; + loop->closes.req = smb2_close_send(loop->conn->tree, &loop->closes.io); + torture_assert_goto(state->tctx, loop->closes.req != NULL, + state->ok, asserted, "smb2_close_send"); + + loop->closes.req->async.fn = test_smb2_bench_path_contention_loop_closed; + loop->closes.req->async.private_data = loop; + return; +asserted: + state->stop = true; +} + +static void test_smb2_bench_path_contention_loop_closed(struct smb2_request *req) +{ + struct test_smb2_bench_path_contention_shared_loop *loop = + (struct test_smb2_bench_path_contention_shared_loop *) + req->async.private_data; + struct test_smb2_bench_path_contention_shared_state *state = loop->state; + double latency = timeval_elapsed(&loop->closes.starttime); + + torture_assert_goto(state->tctx, loop->closes.req == req, + state->ok, asserted, __location__); + loop->error = smb2_close_recv(req, &loop->closes.io); + torture_assert_ntstatus_ok_goto(state->tctx, loop->error, + state->ok, asserted, __location__); + SMB_ASSERT(latency >= 0.000001); + if (loop->closes.num_finished == 0) { + /* first round */ + loop->closes.min_latency = latency; + loop->closes.max_latency = latency; + } + loop->closes.num_finished += 1; + + loop->closes.total_latency += latency; + + if (latency < loop->closes.min_latency) { + loop->closes.min_latency = latency; + } + + if (latency > loop->closes.max_latency) { + loop->closes.max_latency = latency; + } + + test_smb2_bench_path_contention_loop_open(loop); + return; +asserted: + state->stop = true; +} + +static void test_smb2_bench_path_contention_progress(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct test_smb2_bench_path_contention_shared_state *state = + (struct test_smb2_bench_path_contention_shared_state *)private_data; + uint64_t num_opens = 0; + double total_open_latency = 0; + double min_open_latency = 0; + double max_open_latency = 0; + double avs_open_latency = 0; + uint64_t num_closes = 0; + double total_close_latency = 0; + double min_close_latency = 0; + double max_close_latency = 0; + double avs_close_latency = 0; + size_t i; + + state->timecount += 1; + + for (i=0;inum_loops;i++) { + struct test_smb2_bench_path_contention_shared_loop *loop = + &state->loops[i]; + + num_opens += loop->opens.num_finished; + total_open_latency += loop->opens.total_latency; + if (min_open_latency == 0.0 && loop->opens.min_latency != 0.0) { + min_open_latency = loop->opens.min_latency; + } + if (loop->opens.min_latency < min_open_latency) { + min_open_latency = loop->opens.min_latency; + } + if (max_open_latency == 0.0) { + max_open_latency = loop->opens.max_latency; + } + if (loop->opens.max_latency > max_open_latency) { + max_open_latency = loop->opens.max_latency; + } + loop->opens.num_finished = 0; + loop->opens.total_latency = 0.0; + + num_closes += loop->closes.num_finished; + total_close_latency += loop->closes.total_latency; + if (min_close_latency == 0.0 && loop->closes.min_latency != 0.0) { + min_close_latency = loop->closes.min_latency; + } + if (loop->closes.min_latency < min_close_latency) { + min_close_latency = loop->closes.min_latency; + } + if (max_close_latency == 0.0) { + max_close_latency = loop->closes.max_latency; + } + if (loop->closes.max_latency > max_close_latency) { + max_close_latency = loop->closes.max_latency; + } + loop->closes.num_finished = 0; + loop->closes.total_latency = 0.0; + } + + state->opens.num_finished += num_opens; + state->opens.total_latency += total_open_latency; + if (state->opens.min_latency == 0.0 && min_open_latency != 0.0) { + state->opens.min_latency = min_open_latency; + } + if (min_open_latency < state->opens.min_latency) { + state->opens.min_latency = min_open_latency; + } + if (state->opens.max_latency == 0.0) { + state->opens.max_latency = max_open_latency; + } + if (max_open_latency > state->opens.max_latency) { + state->opens.max_latency = max_open_latency; + } + + state->closes.num_finished += num_closes; + state->closes.total_latency += total_close_latency; + if (state->closes.min_latency == 0.0 && min_close_latency != 0.0) { + state->closes.min_latency = min_close_latency; + } + if (min_close_latency < state->closes.min_latency) { + state->closes.min_latency = min_close_latency; + } + if (state->closes.max_latency == 0.0) { + state->closes.max_latency = max_close_latency; + } + if (max_close_latency > state->closes.max_latency) { + state->closes.max_latency = max_close_latency; + } + + if (state->timecount < state->timelimit) { + te = tevent_add_timer(state->tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_path_contention_progress, + state); + torture_assert_goto(state->tctx, te != NULL, + state->ok, asserted, "tevent_add_timer"); + + if (!torture_setting_bool(state->tctx, "progress", true)) { + return; + } + + avs_open_latency = total_open_latency / num_opens; + avs_close_latency = total_close_latency / num_closes; + + torture_comment(state->tctx, + "%.2f second: " + "open[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f] " + "close[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f] \r", + timeval_elapsed(&state->starttime), + (unsigned long long)num_opens, + avs_open_latency, + min_open_latency, + max_open_latency, + (unsigned long long)num_closes, + avs_close_latency, + min_close_latency, + max_close_latency); + return; + } + + avs_open_latency = state->opens.total_latency / state->opens.num_finished; + avs_close_latency = state->closes.total_latency / state->closes.num_finished; + num_opens = state->opens.num_finished / state->timelimit; + num_closes = state->closes.num_finished / state->timelimit; + + torture_comment(state->tctx, + "%.2f second: " + "open[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f] " + "close[num/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f]\n", + timeval_elapsed(&state->starttime), + (unsigned long long)num_opens, + avs_open_latency, + state->opens.min_latency, + state->opens.max_latency, + (unsigned long long)num_closes, + avs_close_latency, + state->closes.min_latency, + state->closes.max_latency); + +asserted: + state->stop = true; +} + +bool test_smb2_bench_path_contention_shared(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct test_smb2_bench_path_contention_shared_state *state = NULL; + bool ret = true; + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int torture_qdepth = torture_setting_int(tctx, "qdepth", 1); + size_t i; + size_t li = 0; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + const char *path = torture_setting_string(tctx, "bench_path", ""); + struct smb2_create open_io = { .level = RAW_OPEN_SMB2, }; + struct smb2_close close_io = { .level = RAW_CLOSE_SMB2, }; + struct tevent_timer *te = NULL; + uint32_t timeout_msec; + + state = talloc_zero(tctx, struct test_smb2_bench_path_contention_shared_state); + torture_assert(tctx, state != NULL, __location__); + state->tctx = tctx; + state->num_conns = torture_nprocs; + state->conns = talloc_zero_array(state, + struct test_smb2_bench_path_contention_shared_conn, + state->num_conns); + torture_assert(tctx, state->conns != NULL, __location__); + state->num_loops = torture_nprocs * torture_qdepth; + state->loops = talloc_zero_array(state, + struct test_smb2_bench_path_contention_shared_loop, + state->num_loops); + torture_assert(tctx, state->loops != NULL, __location__); + state->ok = true; + state->timelimit = MAX(timelimit, 1); + + open_io.in.desired_access = SEC_DIR_READ_ATTRIBUTE; + open_io.in.alloc_size = 0; + open_io.in.file_attributes = 0; + open_io.in.share_access = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; + open_io.in.create_disposition = FILE_OPEN; + open_io.in.create_options = FILE_OPEN_REPARSE_POINT; + open_io.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + open_io.in.security_flags = 0; + open_io.in.fname = path; + open_io.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + open_io.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + torture_comment(tctx, "Opening %zd connections\n", state->num_conns); + + for (i=0;inum_conns;i++) { + struct smb2_tree *ct = NULL; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + size_t pcli; + + state->conns[i].state = state; + state->conns[i].idx = i; + + if (!torture_smb2_connection(tctx, &ct)) { + torture_comment(tctx, "Failed opening %zd/%zd connections\n", i, state->num_conns); + return false; + } + state->conns[i].tree = talloc_steal(state->conns, ct); + + smb2cli_conn_set_max_credits(ct->session->transport->conn, 8192); + smb2cli_ioctl(ct->session->transport->conn, + timeout_msec, + ct->session->smbXcli, + ct->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + UINT32_MAX, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + ct, + &out_input_buffer, + &out_output_buffer); + torture_assert(tctx, + smbXcli_conn_is_connected(ct->session->transport->conn), + "smbXcli_conn_is_connected"); + for (pcli = 0; pcli < torture_qdepth; pcli++) { + struct test_smb2_bench_path_contention_shared_loop *loop = &state->loops[li]; + + loop->idx = li++; + loop->state = state; + loop->conn = &state->conns[i]; + loop->im = tevent_create_immediate(state->loops); + torture_assert(tctx, loop->im != NULL, __location__); + loop->opens.io = open_io; + loop->closes.io = close_io; + + tevent_schedule_immediate(loop->im, + tctx->ev, + test_smb2_bench_path_contention_loop_start, + loop); + } + } + + torture_comment(tctx, "Opened %zu connections with qdepth=%d => %zu loops\n", + state->num_conns, torture_qdepth, state->num_loops); + + torture_comment(tctx, "Running for %d seconds\n", state->timelimit); + + state->starttime = timeval_current(); + + te = tevent_add_timer(tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_path_contention_progress, + state); + torture_assert(tctx, te != NULL, __location__); + + while (!state->stop) { + int rc = tevent_loop_once(tctx->ev); + torture_assert_int_equal(tctx, rc, 0, "tevent_loop_once"); + } + + torture_comment(tctx, "%.2f seconds\n", timeval_elapsed(&state->starttime)); + TALLOC_FREE(state); + return ret; +} + +/* + stress testing read iops + */ + +struct test_smb2_bench_read_conn; +struct test_smb2_bench_read_loop; + +struct test_smb2_bench_read_state { + struct torture_context *tctx; + size_t num_conns; + struct test_smb2_bench_read_conn *conns; + size_t num_loops; + struct test_smb2_bench_read_loop *loops; + size_t pending_loops; + uint32_t io_size; + struct timeval starttime; + int timecount; + int timelimit; + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + bool ok; + bool stop; +}; + +struct test_smb2_bench_read_conn { + struct test_smb2_bench_read_state *state; + int idx; + struct smb2_tree *tree; +}; + +struct test_smb2_bench_read_loop { + struct test_smb2_bench_read_state *state; + struct test_smb2_bench_read_conn *conn; + int idx; + struct tevent_immediate *im; + char *fname; + struct smb2_handle handle; + struct tevent_req *req; + struct timeval starttime; + uint64_t num_started; + uint64_t num_finished; + uint64_t total_finished; + uint64_t max_finished; + double total_latency; + double min_latency; + double max_latency; + NTSTATUS error; +}; + +static void test_smb2_bench_read_loop_do( + struct test_smb2_bench_read_loop *loop); + +static void test_smb2_bench_read_loop_start(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_smb2_bench_read_loop *loop = + (struct test_smb2_bench_read_loop *) + private_data; + + test_smb2_bench_read_loop_do(loop); +} + +static void test_smb2_bench_read_loop_done(struct tevent_req *req); + +static void test_smb2_bench_read_loop_do( + struct test_smb2_bench_read_loop *loop) +{ + struct test_smb2_bench_read_state *state = loop->state; + uint32_t timeout_msec; + + timeout_msec = loop->conn->tree->session->transport->options.request_timeout * 1000; + + loop->num_started += 1; + loop->starttime = timeval_current(); + loop->req = smb2cli_read_send(state->loops, + state->tctx->ev, + loop->conn->tree->session->transport->conn, + timeout_msec, + loop->conn->tree->session->smbXcli, + loop->conn->tree->smbXcli, + state->io_size, /* length */ + 0, /* offset */ + loop->handle.data[0],/* fid_persistent */ + loop->handle.data[1],/* fid_volatile */ + state->io_size, /* minimum_count */ + 0); /* remaining_bytes */ + torture_assert_goto(state->tctx, loop->req != NULL, + state->ok, asserted, "smb2cli_read_send"); + + tevent_req_set_callback(loop->req, + test_smb2_bench_read_loop_done, + loop); + return; +asserted: + state->stop = true; +} + +static void test_smb2_bench_read_loop_done(struct tevent_req *req) +{ + struct test_smb2_bench_read_loop *loop = + (struct test_smb2_bench_read_loop *) + _tevent_req_callback_data(req); + struct test_smb2_bench_read_state *state = loop->state; + double latency = timeval_elapsed(&loop->starttime); + TALLOC_CTX *frame = talloc_stackframe(); + uint8_t *data = NULL; + uint32_t data_length = 0; + + torture_assert_goto(state->tctx, loop->req == req, + state->ok, asserted, __location__); + loop->error = smb2cli_read_recv(req, frame, &data, &data_length); + torture_assert_ntstatus_ok_goto(state->tctx, loop->error, + state->ok, asserted, __location__); + torture_assert_u32_equal_goto(state->tctx, data_length, state->io_size, + state->ok, asserted, __location__); + SMB_ASSERT(latency >= 0.000001); + + if (loop->num_finished == 0) { + /* first round */ + loop->min_latency = latency; + loop->max_latency = latency; + } + + loop->num_finished += 1; + loop->total_finished += 1; + loop->total_latency += latency; + + if (latency < loop->min_latency) { + loop->min_latency = latency; + } + + if (latency > loop->max_latency) { + loop->max_latency = latency; + } + + if (loop->total_finished >= loop->max_finished) { + if (state->pending_loops > 0) { + state->pending_loops -= 1; + } + if (state->pending_loops == 0) { + goto asserted; + } + } + + TALLOC_FREE(frame); + test_smb2_bench_read_loop_do(loop); + return; +asserted: + state->stop = true; + TALLOC_FREE(frame); +} + +static void test_smb2_bench_read_progress(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct test_smb2_bench_read_state *state = + (struct test_smb2_bench_read_state *)private_data; + uint64_t num_reads = 0; + double total_read_latency = 0; + double min_read_latency = 0; + double max_read_latency = 0; + double avs_read_latency = 0; + size_t i; + + state->timecount += 1; + + for (i=0;inum_loops;i++) { + struct test_smb2_bench_read_loop *loop = + &state->loops[i]; + + num_reads += loop->num_finished; + total_read_latency += loop->total_latency; + if (min_read_latency == 0.0 && loop->min_latency != 0.0) { + min_read_latency = loop->min_latency; + } + if (loop->min_latency < min_read_latency) { + min_read_latency = loop->min_latency; + } + if (max_read_latency == 0.0) { + max_read_latency = loop->max_latency; + } + if (loop->max_latency > max_read_latency) { + max_read_latency = loop->max_latency; + } + loop->num_finished = 0; + loop->total_latency = 0.0; + } + + state->num_finished += num_reads; + state->total_latency += total_read_latency; + if (state->min_latency == 0.0 && min_read_latency != 0.0) { + state->min_latency = min_read_latency; + } + if (min_read_latency < state->min_latency) { + state->min_latency = min_read_latency; + } + if (state->max_latency == 0.0) { + state->max_latency = max_read_latency; + } + if (max_read_latency > state->max_latency) { + state->max_latency = max_read_latency; + } + + if (state->timecount < state->timelimit) { + te = tevent_add_timer(state->tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_read_progress, + state); + torture_assert_goto(state->tctx, te != NULL, + state->ok, asserted, "tevent_add_timer"); + + if (!torture_setting_bool(state->tctx, "progress", true)) { + return; + } + + avs_read_latency = total_read_latency / num_reads; + + torture_comment(state->tctx, + "%.2f second: " + "read[num/s=%llu,bytes/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f] \r", + timeval_elapsed(&state->starttime), + (unsigned long long)num_reads, + (unsigned long long)num_reads*state->io_size, + avs_read_latency, + min_read_latency, + max_read_latency); + return; + } + + avs_read_latency = state->total_latency / state->num_finished; + num_reads = state->num_finished / state->timelimit; + + torture_comment(state->tctx, + "%.2f second: " + "read[num/s=%llu,bytes/s=%llu,avslat=%.6f,minlat=%.6f,maxlat=%.6f]\n", + timeval_elapsed(&state->starttime), + (unsigned long long)num_reads, + (unsigned long long)num_reads*state->io_size, + avs_read_latency, + state->min_latency, + state->max_latency); + +asserted: + state->stop = true; +} + +static bool test_smb2_bench_read(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct test_smb2_bench_read_state *state = NULL; + bool ret = true; + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int torture_qdepth = torture_setting_int(tctx, "qdepth", 1); + int torture_io_size = torture_setting_int(tctx, "io_size", 4096); + size_t i; + size_t li = 0; + int looplimit = torture_setting_int(tctx, "looplimit", -1); + int timelimit = torture_setting_int(tctx, "timelimit", 10); + struct tevent_timer *te = NULL; + uint32_t timeout_msec; + const char *dname = "bench_read_dir"; + const char *unique = generate_random_str(tctx, 8); + struct smb2_handle dh; + NTSTATUS status; + + smb2_deltree(tree, dname); + + status = torture_smb2_testdir(tree, dname, &dh); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, dh); + CHECK_STATUS(status, NT_STATUS_OK); + + state = talloc_zero(tctx, struct test_smb2_bench_read_state); + torture_assert(tctx, state != NULL, __location__); + state->tctx = tctx; + state->num_conns = torture_nprocs; + state->conns = talloc_zero_array(state, + struct test_smb2_bench_read_conn, + state->num_conns); + torture_assert(tctx, state->conns != NULL, __location__); + state->num_loops = torture_nprocs * torture_qdepth; + state->loops = talloc_zero_array(state, + struct test_smb2_bench_read_loop, + state->num_loops); + torture_assert(tctx, state->loops != NULL, __location__); + state->ok = true; + state->timelimit = MAX(timelimit, 1); + state->io_size = MAX(torture_io_size, 1); + state->io_size = MIN(state->io_size, 16*1024*1024); + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + torture_comment(tctx, "Opening %zu connections\n", state->num_conns); + + for (i=0;inum_conns;i++) { + struct smb2_tree *ct = NULL; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + size_t pcli; + + state->conns[i].state = state; + state->conns[i].idx = i; + + if (!torture_smb2_connection(tctx, &ct)) { + torture_comment(tctx, "Failed opening %zu/%zu connections\n", i, state->num_conns); + return false; + } + state->conns[i].tree = talloc_steal(state->conns, ct); + + smb2cli_conn_set_max_credits(ct->session->transport->conn, 8192); + smb2cli_ioctl(ct->session->transport->conn, + timeout_msec, + ct->session->smbXcli, + ct->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + UINT32_MAX, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + ct, + &out_input_buffer, + &out_output_buffer); + torture_assert(tctx, + smbXcli_conn_is_connected(ct->session->transport->conn), + "smbXcli_conn_is_connected"); + + for (pcli = 0; pcli < torture_qdepth; pcli++) { + struct test_smb2_bench_read_loop *loop = &state->loops[li]; + struct smb2_create cr; + union smb_setfileinfo sfinfo; + + loop->idx = li++; + if (looplimit != -1) { + loop->max_finished = looplimit; + } else { + loop->max_finished = UINT64_MAX; + } + loop->state = state; + loop->conn = &state->conns[i]; + loop->im = tevent_create_immediate(state->loops); + torture_assert(tctx, loop->im != NULL, __location__); + + loop->fname = talloc_asprintf(state->loops, + "%s\\%s_loop_%zu_conn_%zu_loop_%zu.dat", + dname, unique, li, i, pcli); + torture_assert(tctx, loop->fname != NULL, __location__); + + /* reasonable default parameters */ + ZERO_STRUCT(cr); + cr.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + cr.in.alloc_size = state->io_size; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + cr.in.create_disposition = NTCREATEX_DISP_CREATE; + cr.in.create_options = + NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = loop->fname; + status = smb2_create(state->conns[i].tree, tctx, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + loop->handle = cr.out.file.handle; + + ZERO_STRUCT(sfinfo); + sfinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.end_of_file_info.in.file.handle = loop->handle; + sfinfo.end_of_file_info.in.size = state->io_size; + status = smb2_setinfo_file(state->conns[i].tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + tevent_schedule_immediate(loop->im, + tctx->ev, + test_smb2_bench_read_loop_start, + loop); + } + } + + torture_comment(tctx, "Opened %zu connections with qdepth=%d => %zu loops\n", + state->num_conns, torture_qdepth, state->num_loops); + + torture_comment(tctx, "Running for %d seconds\n", state->timelimit); + + state->starttime = timeval_current(); + state->pending_loops = state->num_loops; + + te = tevent_add_timer(tctx->ev, + state, + timeval_current_ofs(1, 0), + test_smb2_bench_read_progress, + state); + torture_assert(tctx, te != NULL, __location__); + + while (!state->stop) { + int rc = tevent_loop_once(tctx->ev); + torture_assert_int_equal(tctx, rc, 0, "tevent_loop_once"); + } + + torture_comment(tctx, "%.2f seconds\n", timeval_elapsed(&state->starttime)); + TALLOC_FREE(state); + smb2_deltree(tree, dname); + return ret; +} + +struct torture_suite *torture_smb2_bench_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "bench"); + + torture_suite_add_1smb2_test(suite, "oplock1", test_smb2_bench_oplock); + torture_suite_add_1smb2_test(suite, "echo", test_smb2_bench_echo); + torture_suite_add_1smb2_test(suite, "path-contention-shared", test_smb2_bench_path_contention_shared); + torture_suite_add_1smb2_test(suite, "read", test_smb2_bench_read); + + suite->description = talloc_strdup(suite, "SMB2-BENCH tests"); + + return suite; +} diff --git a/source4/torture/smb2/block.c b/source4/torture/smb2/block.c new file mode 100644 index 0000000..b9982b0 --- /dev/null +++ b/source4/torture/smb2/block.c @@ -0,0 +1,446 @@ +/* + * Unix SMB/CIFS implementation. + * + * block SMB2 transports using iptables + * + * Copyright (C) Guenther Deschner, 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "system/network.h" +#include "lib/util/util_net.h" +#include "torture/smb2/block.h" +#include "libcli/smb/smbXcli_base.h" +#include "lib/util/tevent_ntstatus.h" +#include "oplock_break_handler.h" +#include "lease_break_handler.h" + +/* + * OUTPUT + * | + * -----> SMBTORTURE_OUTPUT + * | + * -----> SMBTORTURE_transportname1 + * -----> SMBTORTURE_transportname2 + */ + + +static bool run_cmd(const char *cmd) +{ + int ret; + + DEBUG(10, ("%s will call '%s'\n", __location__, cmd)); + + ret = system(cmd); + if (ret) { + DEBUG(1, ("%s failed to execute system call: %s: %d\n", + __location__, cmd, ret)); + return false; + } + + return true; +} + +static const char *iptables_command(struct torture_context *tctx) +{ + return torture_setting_string(tctx, "iptables_command", + "/usr/sbin/iptables"); +} + +char *escape_shell_string(const char *src); + +/* + * iptables v1.6.1: chain name `SMBTORTURE_INPUT_tree1->session->transport' + * too long (must be under 29 chars) + * + * maybe truncate chainname ? + */ +static const char *samba_chain_name(struct torture_context *tctx, + const char *name, + const char *prefix) +{ + const char *s; + char *sm; + + s = talloc_asprintf(tctx, "%s_%s", prefix, name); + if (s == NULL) { + return NULL; + } + + sm = escape_shell_string(s); + if (sm == NULL) { + return NULL; + } + + s = talloc_strdup(tctx, sm); + free(sm); + + return s; +} + +static bool iptables_setup_chain(struct torture_context *tctx, + const char *parent_chain, + const char *chain, + bool unblock) +{ + const char *ipt = iptables_command(tctx); + const char *cmd; + + if (unblock) { + cmd = talloc_asprintf(tctx, + "%s -L %s > /dev/null 2>&1 && " + "(" + "%s -F %s;" + "%s -D %s -j %s > /dev/null 2>&1 || true;" + "%s -X %s;" + ");" + "%s -L %s > /dev/null 2>&1 || true;", + ipt, chain, + ipt, chain, + ipt, parent_chain, chain, + ipt, chain, + ipt, chain); + } else { + cmd = talloc_asprintf(tctx, + "%s -L %s > /dev/null 2>&1 || " + "(" + "%s -N %s && " + "%s -I %s -j %s;" + ");" + "%s -F %s;", + ipt, chain, + ipt, chain, + ipt, parent_chain, chain, + ipt, chain); + } + + if (cmd == NULL) { + return false; + } + + if (!run_cmd(cmd)) { + return false; + } + + return true; +} + +uint16_t torture_get_local_port_from_transport(struct smb2_transport *t) +{ + const struct sockaddr_storage *local_ss; + + local_ss = smbXcli_conn_local_sockaddr(t->conn); + + return get_sockaddr_port(local_ss); +} + +static bool torture_block_tcp_output_port_internal( + struct torture_context *tctx, + const char *name, + uint16_t port, + bool unblock) +{ + const char *ipt = iptables_command(tctx); + const char *chain_out = NULL; + char *cmd_out = NULL; + + chain_out = samba_chain_name(tctx, name, "SMBTORTURE"); + if (chain_out == NULL) { + return false; + } + + torture_comment(tctx, "%sblocking %s dport %d\n", + unblock ? "un" : "", name, port); + + if (!unblock) { + bool ok; + + iptables_setup_chain(tctx, + "SMBTORTURE_OUTPUT", + chain_out, + true); + ok = iptables_setup_chain(tctx, + "SMBTORTURE_OUTPUT", + chain_out, + false); + if (!ok) { + return false; + } + } + + cmd_out = talloc_asprintf(tctx, + "%s %s %s -p tcp --sport %d -j DROP", + ipt, unblock ? "-D" : "-I", chain_out, port); + if (cmd_out == NULL) { + return false; + } + + if (!run_cmd(cmd_out)) { + return false; + } + + if (unblock) { + bool ok; + + ok = iptables_setup_chain(tctx, + "SMBTORTURE_OUTPUT", + chain_out, + true); + if (!ok) { + return false; + } + } + + return true; +} + +bool torture_block_tcp_output_port(struct torture_context *tctx, + const char *name, + uint16_t port) +{ + return torture_block_tcp_output_port_internal(tctx, name, port, false); +} + +bool torture_unblock_tcp_output_port(struct torture_context *tctx, + const char *name, + uint16_t port) +{ + return torture_block_tcp_output_port_internal(tctx, name, port, true); +} + +bool torture_block_tcp_output_setup(struct torture_context *tctx) +{ + return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", false); +} + +bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx) +{ + return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", true); +} + +/* + * Use iptables to block channels + */ +static bool test_block_smb2_transport_iptables(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name) +{ + uint16_t local_port; + bool ret; + + local_port = torture_get_local_port_from_transport(transport); + torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port); + ret = torture_block_tcp_output_port(tctx, name, local_port); + torture_assert(tctx, ret, "we could not block tcp transport"); + + return ret; +} + +static bool test_unblock_smb2_transport_iptables(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name) +{ + uint16_t local_port; + bool ret; + + local_port = torture_get_local_port_from_transport(transport); + torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port); + ret = torture_unblock_tcp_output_port(tctx, name, local_port); + torture_assert(tctx, ret, "we could not block tcp transport"); + + return ret; +} + +static bool torture_blocked_lease_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data) +{ + struct smb2_transport *transport_copy = + talloc_get_type_abort(private_data, + struct smb2_transport); + bool lease_skip_ack = lease_break_info.lease_skip_ack; + bool ok; + + lease_break_info.lease_skip_ack = true; + ok = transport_copy->lease.handler(transport, + lb, + transport_copy->lease.private_data); + lease_break_info.lease_skip_ack = lease_skip_ack; + + if (!ok) { + return false; + } + + if (lease_break_info.lease_skip_ack) { + return true; + } + + if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { + lease_break_info.failures++; + } + + return true; +} + +static bool torture_blocked_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_transport *transport_copy = + talloc_get_type_abort(private_data, + struct smb2_transport); + bool oplock_skip_ack = break_info.oplock_skip_ack; + bool ok; + + break_info.oplock_skip_ack = true; + ok = transport_copy->oplock.handler(transport, + handle, + level, + transport_copy->oplock.private_data); + break_info.oplock_skip_ack = oplock_skip_ack; + + if (!ok) { + return false; + } + + if (break_info.oplock_skip_ack) { + return true; + } + + break_info.failures++; + break_info.failure_status = NT_STATUS_CONNECTION_DISCONNECTED; + + return true; +} + +static bool test_block_smb2_transport_fsctl_smbtorture(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name) +{ + struct smb2_transport *transport_copy = NULL; + DATA_BLOB in_input_buffer = data_blob_null; + DATA_BLOB in_output_buffer = data_blob_null; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + struct tevent_req *req = NULL; + uint16_t local_port; + NTSTATUS status; + bool ok; + + transport_copy = talloc_zero(transport, struct smb2_transport); + torture_assert(tctx, transport_copy, "talloc transport_copy"); + transport_copy->lease = transport->lease; + transport_copy->oplock = transport->oplock; + + local_port = torture_get_local_port_from_transport(transport); + torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port); + req = smb2cli_ioctl_send(tctx, + tctx->ev, + transport->conn, + 1000, /* timeout_msec */ + NULL, /* session */ + NULL, /* tcon */ + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT, + 0, /* in_max_input_length */ + &in_input_buffer, + 0, /* in_max_output_length */ + &in_output_buffer, + SMB2_IOCTL_FLAG_IS_FSCTL); + torture_assert(tctx, req != NULL, "smb2cli_ioctl_send() failed"); + ok = tevent_req_poll_ntstatus(req, tctx->ev, &status); + if (ok) { + status = NT_STATUS_OK; + } + torture_assert_ntstatus_ok(tctx, status, "tevent_req_poll_ntstatus() failed"); + status = smb2cli_ioctl_recv(req, tctx, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT failed\n\n" + "On a Samba server 'smbd:FSCTL_SMBTORTURE = yes' is needed!\n\n" + "Otherwise you may need to use iptables like this:\n" + "--option='torture:use_iptables=yes'\n" + "And maybe something like this in addition:\n" + "--option='torture:iptables_command=sudo /sbin/iptables'\n\n"); + TALLOC_FREE(req); + + if (transport->lease.handler != NULL) { + transport->lease.handler = torture_blocked_lease_handler; + transport->lease.private_data = transport_copy; + } + if (transport->oplock.handler != NULL) { + transport->oplock.handler = torture_blocked_oplock_handler; + transport->oplock.private_data = transport_copy; + } + + return true; +} + +bool _test_block_smb2_transport(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name) +{ + bool use_iptables = torture_setting_bool(tctx, + "use_iptables", false); + + if (use_iptables) { + return test_block_smb2_transport_iptables(tctx, transport, name); + } else { + return test_block_smb2_transport_fsctl_smbtorture(tctx, transport, name); + } +} + +bool _test_unblock_smb2_transport(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name) +{ + bool use_iptables = torture_setting_bool(tctx, + "use_iptables", false); + + if (use_iptables) { + return test_unblock_smb2_transport_iptables(tctx, transport, name); + } else { + return true; + } +} + +bool test_setup_blocked_transports(struct torture_context *tctx) +{ + bool use_iptables = torture_setting_bool(tctx, + "use_iptables", false); + + if (use_iptables) { + return torture_block_tcp_output_setup(tctx); + } + + return true; +} + +void test_cleanup_blocked_transports(struct torture_context *tctx) +{ + bool use_iptables = torture_setting_bool(tctx, + "use_iptables", false); + + if (use_iptables) { + torture_unblock_tcp_output_cleanup(tctx); + } +} diff --git a/source4/torture/smb2/block.h b/source4/torture/smb2/block.h new file mode 100644 index 0000000..6a6370a --- /dev/null +++ b/source4/torture/smb2/block.h @@ -0,0 +1,43 @@ +/* + * Unix SMB/CIFS implementation. + * + * block SMB2 transports using iptables + * + * Copyright (C) Guenther Deschner, 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +uint16_t torture_get_local_port_from_transport(struct smb2_transport *t); + +bool torture_block_tcp_output_port(struct torture_context *tctx, + const char *name, + uint16_t port); +bool torture_unblock_tcp_output_port(struct torture_context *tctx, + const char *name, + uint16_t port); +bool torture_block_tcp_output_setup(struct torture_context *tctx); +bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx); + +bool test_setup_blocked_transports(struct torture_context *tctx); +void test_cleanup_blocked_transports(struct torture_context *tctx); + +#define test_block_smb2_transport(_tctx, _t) _test_block_smb2_transport(_tctx, _t, #_t) +bool _test_block_smb2_transport(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name); +#define test_unblock_smb2_transport(_tctx, _t) _test_unblock_smb2_transport(_tctx, _t, #_t) +bool _test_unblock_smb2_transport(struct torture_context *tctx, + struct smb2_transport *transport, + const char *name); diff --git a/source4/torture/smb2/charset.c b/source4/torture/smb2/charset.c new file mode 100644 index 0000000..a385266 --- /dev/null +++ b/source4/torture/smb2/charset.c @@ -0,0 +1,235 @@ +/* + Unix SMB/CIFS implementation. + + SMB torture tester - charset test routines + + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "param/param.h" + +#define BASEDIR "chartest" + +/* + open a file using a set of unicode code points for the name + + the prefix BASEDIR is added before the name +*/ +static NTSTATUS unicode_open(struct torture_context *tctx, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + uint32_t create_disposition, + const uint32_t *u_name, + size_t u_name_len) +{ + struct smb2_create io = {0}; + char *fname = NULL; + char *fname2 = NULL; + char *ucs_name = NULL; + size_t i; + NTSTATUS status; + + ucs_name = talloc_size(mem_ctx, (1+u_name_len)*2); + if (!ucs_name) { + torture_comment(tctx, "Failed to create UCS2 Name - talloc() failure\n"); + return NT_STATUS_NO_MEMORY; + } + + for (i=0;ilp_ctx), CH_UTF16, CH_UNIX, ucs_name, (1+u_name_len)*2, (void **)&fname, &i)) { + torture_comment(tctx, "Failed to convert UCS2 Name into unix - convert_string_talloc() failure\n"); + talloc_free(ucs_name); + return NT_STATUS_NO_MEMORY; + } + + fname2 = talloc_asprintf(ucs_name, "%s\\%s", BASEDIR, fname); + if (!fname2) { + talloc_free(ucs_name); + torture_comment(tctx, "Failed to create fname - talloc() failure\n"); + return NT_STATUS_NO_MEMORY; + } + + io.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.in.create_options = 0; + io.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname2; + io.in.create_disposition = create_disposition; + + status = smb2_create(tree, tctx, &io); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(ucs_name); + return status; + } + + smb2_util_close(tree, io.out.file.handle); + talloc_free(ucs_name); + return NT_STATUS_OK; +} + + +/* + see if the server recognises composed characters +*/ +static bool test_composed(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const uint32_t name1[] = {0x61, 0x308}; + const uint32_t name2[] = {0xe4}; + NTSTATUS status; + bool ret = true; + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, "setting up basedir"); + + status = unicode_open(tctx, tree, tctx, + NTCREATEX_DISP_CREATE, name1, 2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create composed name"); + + status = unicode_open(tctx, tree, tctx, + NTCREATEX_DISP_CREATE, name2, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create accented character"); + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + see if the server recognises a naked diacritical +*/ +static bool test_diacritical(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const uint32_t name1[] = {0x308}; + const uint32_t name2[] = {0x308, 0x308}; + NTSTATUS status; + bool ret = true; + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, "setting up basedir"); + + status = unicode_open(tctx, tree, tctx, + NTCREATEX_DISP_CREATE, name1, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create naked diacritical"); + + /* try a double diacritical */ + status = unicode_open(tctx, tree, tctx, + NTCREATEX_DISP_CREATE, name2, 2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create double " + "naked diacritical"); + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + see if the server recognises a partial surrogate pair +*/ +static bool test_surrogate(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const uint32_t name1[] = {0xd800}; + const uint32_t name2[] = {0xdc00}; + const uint32_t name3[] = {0xd800, 0xdc00}; + NTSTATUS status; + bool ret = true; + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, "setting up basedir"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name1, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create partial surrogate 1"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name2, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create partial surrogate 2"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name3, 2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create full surrogate"); + +done: + smb2_deltree(tree, BASEDIR); + return true; +} + +/* + see if the server recognises wide-a characters +*/ +static bool test_widea(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const uint32_t name1[] = {'a'}; + const uint32_t name2[] = {0xff41}; + const uint32_t name3[] = {0xff21}; + NTSTATUS status; + bool ret = true; + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ret, ret, done, "setting up basedir"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name1, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create 'a'"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name2, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to create wide-a"); + + status = unicode_open(tctx, tree, tctx, NTCREATEX_DISP_CREATE, name3, 1); + torture_assert_ntstatus_equal_goto(tctx, + status, + NT_STATUS_OBJECT_NAME_COLLISION, + ret, done, + "Failed to create wide-A"); + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} + +struct torture_suite *torture_smb2_charset(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "charset"); + + torture_suite_add_1smb2_test(suite, "Testing composite character (a umlaut)", test_composed); + torture_suite_add_1smb2_test(suite, "Testing naked diacritical (umlaut)", test_diacritical); + torture_suite_add_1smb2_test(suite, "Testing partial surrogate", test_surrogate); + torture_suite_add_1smb2_test(suite, "Testing wide-a", test_widea); + + return suite; +} diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c new file mode 100644 index 0000000..175069d --- /dev/null +++ b/source4/torture/smb2/compound.c @@ -0,0 +1,2595 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 compounded requests + + Copyright (C) Stefan Metzmacher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "tevent.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "../libcli/smb/smbXcli_base.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%d - should be %d\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(tctx->ev) != 0) { \ + break; \ + } \ + } + +static struct { + struct smb2_handle handle; + uint8_t level; + struct smb2_break br; + int count; + int failures; + NTSTATUS failure_status; +} break_info; + +static void torture_oplock_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + ZERO_STRUCT(br); + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + break_info.failure_status = status; + } + + return; +} + +/* A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. */ +static bool torture_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + ZERO_STRUCT(break_info.br); + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + printf("Acking to %s [0x%02X] in oplock handler\n", name, level); + + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} + +static bool test_compound_break(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname1 = "some-file.pptx"; + NTSTATUS status; + bool ret = true; + union smb_open io1; + struct smb2_create io2; + struct smb2_getinfo gf; + struct smb2_request *req[2]; + struct smb2_handle h1; + struct smb2_handle h; + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + ZERO_STRUCT(break_info); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io1.smb2); + io1.generic.level = RAW_OPEN_SMB2; + io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE| + SEC_STD_READ_CONTROL| + SEC_FILE_READ_ATTRIBUTE| + SEC_FILE_READ_EA| + SEC_FILE_READ_DATA); + io1.smb2.in.alloc_size = 0; + io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io1.smb2.in.create_options = 0; + io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io1.smb2.in.security_flags = 0; + io1.smb2.in.fname = fname1; + + torture_comment(tctx, "TEST2: open a file with an batch " + "oplock (share mode: all)\n"); + io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &(io1.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + + h1 = io1.smb2.out.file.handle; + + torture_comment(tctx, "TEST2: Opening second time with compound\n"); + + ZERO_STRUCT(io2); + + io2.in.desired_access = (SEC_STD_SYNCHRONIZE| + SEC_FILE_READ_ATTRIBUTE| + SEC_FILE_READ_EA); + io2.in.alloc_size = 0; + io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io2.in.create_disposition = NTCREATEX_DISP_OPEN; + io2.in.create_options = 0; + io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io2.in.security_flags = 0; + io2.in.fname = fname1; + io2.in.oplock_level = 0; + + smb2_transport_compound_start(tree->session->transport, 2); + + req[0] = smb2_create_send(tree, &io2); + + smb2_transport_compound_set_related(tree->session->transport, true); + + h.data[0] = UINT64_MAX; + h.data[1] = UINT64_MAX; + + ZERO_STRUCT(gf); + gf.in.file.handle = h; + gf.in.info_type = SMB2_0_INFO_FILE; + gf.in.info_class = 0x16; + gf.in.output_buffer_length = 0x1000; + gf.in.input_buffer = data_blob_null; + + req[1] = smb2_getinfo_send(tree, &gf); + + status = smb2_create_recv(req[0], tree, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_getinfo_recv(req[1], tree, &gf); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname1); + return ret; +} + +static bool test_compound_related1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_related1.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[2]; + struct smbXcli_tcon *saved_tcon = tree->smbXcli; + struct smbXcli_session *saved_session = tree->session->smbXcli; + + smb2_transport_credits_ask_num(tree->session->transport, 2); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 2); + + req[0] = smb2_create_send(tree, &cr); + + smb2_transport_compound_set_related(tree->session->transport, true); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + tree->smbXcli = smbXcli_tcon_create(tree); + smb2cli_tcon_set_values(tree->smbXcli, + NULL, /* session */ + 0xFFFFFFFF, /* tcon_id */ + 0, /* type */ + 0, /* flags */ + 0, /* capabilities */ + 0 /* maximal_access */); + + tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session, + tree->session->smbXcli); + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); + + req[1] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_OK); + + TALLOC_FREE(tree->smbXcli); + tree->smbXcli = saved_tcon; + TALLOC_FREE(tree->session->smbXcli); + tree->session->smbXcli = saved_session; + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_related2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_related2.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[5]; + struct smbXcli_tcon *saved_tcon = tree->smbXcli; + struct smbXcli_session *saved_session = tree->session->smbXcli; + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 5); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + tree->smbXcli = smbXcli_tcon_create(tree); + smb2cli_tcon_set_values(tree->smbXcli, + NULL, /* session */ + 0xFFFFFFFF, /* tcon_id */ + 0, /* type */ + 0, /* flags */ + 0, /* capabilities */ + 0 /* maximal_access */); + + tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session, + tree->session->smbXcli); + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); + + req[1] = smb2_close_send(tree, &cl); + req[2] = smb2_close_send(tree, &cl); + req[3] = smb2_close_send(tree, &cl); + req[4] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[3], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[4], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + + TALLOC_FREE(tree->smbXcli); + tree->smbXcli = saved_tcon; + TALLOC_FREE(tree->session->smbXcli); + tree->session->smbXcli = saved_session; + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_related3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_ioctl io; + struct smb2_create cr; + struct smb2_close cl; + const char *fname = "compound_related3.dat"; + struct smb2_request *req[3]; + NTSTATUS status; + bool ret = false; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 3); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(io); + io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID; + io.in.file.handle = hd; + io.in.reserved2 = 0; + io.in.max_output_response = 64; + io.in.flags = 1; + + req[1] = smb2_ioctl_send(tree, &io); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[2] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_ioctl_recv(req[1], tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + ret = true; +done: + return ret; +} + +static bool test_compound_related4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "compound_related4.dat"; + struct security_descriptor *sd = NULL; + struct smb2_handle hd; + struct smb2_create cr; + union smb_setfileinfo set; + struct smb2_ioctl io; + struct smb2_close cl; + struct smb2_request *req[4]; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.level = RAW_OPEN_SMB2; + cr.in.create_flags = 0; + cr.in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + cr.in.create_options = 0; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.alloc_size = 0; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n"); + + hd = cr.out.file.handle; + torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "security_descriptor_dacl_create failed\n"); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = hd; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + torture_comment(tctx, "try open for write\n"); + cr.in.desired_access = SEC_FILE_WRITE_DATA; + smb2_transport_compound_start(tree->session->transport, 4); + + req[0] = smb2_create_send(tree, &cr); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_create_send failed\n"); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + ZERO_STRUCT(io); + io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID; + io.in.file.handle = hd; + io.in.flags = 1; + + req[1] = smb2_ioctl_send(tree, &io); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_ioctl_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[2] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_create_send failed\n"); + + set.set_secdesc.in.file.handle = hd; + + req[3] = smb2_setinfo_file_send(tree, &set); + torture_assert_not_null_goto(tctx, req[3], ret, done, + "smb2_create_send failed\n"); + + status = smb2_create_recv(req[0], tree, &cr); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_create_recv failed\n"); + + status = smb2_ioctl_recv(req[1], tree, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_ioctl_recv failed\n"); + + status = smb2_close_recv(req[2], &cl); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_close_recv failed\n"); + + status = smb2_setinfo_recv(req[3]); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_setinfo_recv failed\n"); + +done: + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_related5(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_ioctl io; + struct smb2_close cl; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = false; + + smb2_transport_compound_start(tree->session->transport, 2); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(io); + io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID; + io.in.file.handle = hd; + io.in.flags = 1; + + req[0] = smb2_ioctl_send(tree, &io); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_ioctl_send failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[1] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_create_send failed\n"); + + status = smb2_ioctl_recv(req[0], tree, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED, + ret, done, + "smb2_ioctl_recv failed\n"); + + status = smb2_close_recv(req[1], &cl); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED, + ret, done, + "smb2_close_recv failed\n"); + + ret = true; + +done: + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_related6(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + struct smb2_read rd; + struct smb2_write wr; + struct smb2_close cl; + NTSTATUS status; + const char *fname = "compound_related6.dat"; + struct smb2_request *req[5]; + uint8_t buf[64]; + bool ret = true; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.level = RAW_OPEN_SMB2; + cr.in.create_flags = 0; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.create_options = 0; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.alloc_size = 0; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + hd = cr.out.file.handle; + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, hd, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + torture_comment(tctx, "try open for read\n"); + cr.in.desired_access = SEC_FILE_READ_DATA; + smb2_transport_compound_start(tree->session->transport, 5); + + req[0] = smb2_create_send(tree, &cr); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_create_send failed\n"); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(rd); + rd.in.file.handle = hd; + rd.in.length = 1; + rd.in.offset = 0; + + req[1] = smb2_read_send(tree, &rd); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_read_send failed\n"); + + ZERO_STRUCT(wr); + wr.in.file.handle = hd; + wr.in.offset = 0; + wr.in.data = data_blob_talloc(tctx, NULL, 64); + + req[2] = smb2_write_send(tree, &wr); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_write_send failed\n"); + + ZERO_STRUCT(rd); + rd.in.file.handle = hd; + rd.in.length = 1; + rd.in.offset = 0; + + req[3] = smb2_read_send(tree, &rd); + torture_assert_not_null_goto(tctx, req[3], ret, done, + "smb2_read_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[4] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[4], ret, done, + "smb2_close_send failed\n"); + + status = smb2_create_recv(req[0], tree, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create_recv failed\n"); + + status = smb2_read_recv(req[1], tree, &rd); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_read_recv failed\n"); + + status = smb2_write_recv(req[2], &wr); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_write_recv failed\n"); + + status = smb2_read_recv(req[3], tree, &rd); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_read_recv failed\n"); + + status = smb2_close_recv(req[4], &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close_recv failed\n"); + + done: + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_related7(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "compound_related4.dat"; + struct security_descriptor *sd = NULL; + struct smb2_handle hd; + struct smb2_create cr; + union smb_setfileinfo set; + struct smb2_notify nt; + struct smb2_close cl; + NTSTATUS status; + struct smb2_request *req[4]; + bool ret = true; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.level = RAW_OPEN_SMB2; + cr.in.create_flags = 0; + cr.in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + cr.in.create_options = 0; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.alloc_size = 0; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + hd = cr.out.file.handle; + torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n"); + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "security_descriptor_dacl_create failed\n"); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = hd; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + torture_comment(tctx, "try open for write\n"); + cr.in.desired_access = SEC_FILE_WRITE_DATA; + smb2_transport_compound_start(tree->session->transport, 4); + + req[0] = smb2_create_send(tree, &cr); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_create_send failed\n"); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(nt); + nt.in.recursive = true; + nt.in.buffer_size = 0x1000; + nt.in.file.handle = hd; + nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + nt.in.unknown = 0x00000000; + + req[1] = smb2_notify_send(tree, &nt); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_notify_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[2] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_close_send failed\n"); + + set.set_secdesc.in.file.handle = hd; + + req[3] = smb2_setinfo_file_send(tree, &set); + torture_assert_not_null_goto(tctx, req[3], ret, done, + "smb2_setinfo_file_send failed\n"); + + status = smb2_create_recv(req[0], tree, &cr); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_create_recv failed\n"); + + status = smb2_notify_recv(req[1], tree, &nt); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_notify_recv failed\n"); + + status = smb2_close_recv(req[2], &cl); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_close_recv failed\n"); + + status = smb2_setinfo_recv(req[3]); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, + "smb2_setinfo_recv failed\n"); + +done: + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_related8(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "compound_related8.dat"; + const char *fname_nonexisting = "compound_related8.dat.void"; + struct security_descriptor *sd = NULL; + struct smb2_handle hd; + struct smb2_create cr; + union smb_setfileinfo set; + struct smb2_notify nt; + struct smb2_close cl; + NTSTATUS status; + struct smb2_request *req[4]; + bool ret = true; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.level = RAW_OPEN_SMB2; + cr.in.create_flags = 0; + cr.in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + cr.in.create_options = 0; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.alloc_size = 0; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + hd = cr.out.file.handle; + + smb2_transport_compound_start(tree->session->transport, 4); + + torture_comment(tctx, "try open for write\n"); + cr.in.fname = fname_nonexisting; + cr.in.create_disposition = NTCREATEX_DISP_OPEN; + + req[0] = smb2_create_send(tree, &cr); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_create_send failed\n"); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(nt); + nt.in.recursive = true; + nt.in.buffer_size = 0x1000; + nt.in.file.handle = hd; + nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + nt.in.unknown = 0x00000000; + + req[1] = smb2_notify_send(tree, &nt); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_notify_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[2] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_close_send failed\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "security_descriptor_dacl_create failed\n"); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = hd; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + req[3] = smb2_setinfo_file_send(tree, &set); + torture_assert_not_null_goto(tctx, req[3], ret, done, + "smb2_setinfo_file_send failed\n"); + + status = smb2_create_recv(req[0], tree, &cr); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_create_recv failed\n"); + + status = smb2_notify_recv(req[1], tree, &nt); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_notify_recv failed\n"); + + status = smb2_close_recv(req[2], &cl); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_close_recv failed\n"); + + status = smb2_setinfo_recv(req[3]); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, + "smb2_setinfo_recv failed\n"); + +done: + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_related9(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "compound_related9.dat"; + struct security_descriptor *sd = NULL; + struct smb2_handle hd; + struct smb2_create cr; + union smb_setfileinfo set; + struct smb2_notify nt; + struct smb2_close cl; + NTSTATUS status; + struct smb2_request *req[3]; + bool ret = true; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.level = RAW_OPEN_SMB2; + cr.in.create_flags = 0; + cr.in.desired_access = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + cr.in.create_options = 0; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.alloc_size = 0; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + cr.in.security_flags = 0; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + hd = cr.out.file.handle; + + smb2_transport_compound_start(tree->session->transport, 3); + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(nt); + nt.in.recursive = true; + nt.in.buffer_size = 0x1000; + nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + + req[0] = smb2_notify_send(tree, &nt); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_notify_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + req[1] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_close_send failed\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + SID_CREATOR_OWNER, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ | SEC_STD_ALL, + 0, + NULL); + torture_assert_not_null_goto(tctx, sd, ret, done, + "security_descriptor_dacl_create failed\n"); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = hd; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + + req[2] = smb2_setinfo_file_send(tree, &set); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_setinfo_file_send failed\n"); + + status = smb2_notify_recv(req[0], tree, &nt); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER, + ret, done, + "smb2_notify_recv failed\n"); + + status = smb2_close_recv(req[1], &cl); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER, + ret, done, + "smb2_close_recv failed\n"); + + status = smb2_setinfo_recv(req[2]); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER, + ret, done, + "smb2_setinfo_recv failed\n"); + +done: + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +static bool test_compound_padding(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle h; + struct smb2_create cr; + struct smb2_read r; + struct smb2_read r2; + const char *fname = "compound_read.dat"; + const char *sname = "compound_read.dat:foo"; + struct smb2_request *req[3]; + NTSTATUS status; + bool ret = false; + + smb2_util_unlink(tree, fname); + + /* Write file */ + ZERO_STRUCT(cr); + cr.in.desired_access = SEC_FILE_WRITE_DATA; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.create_disposition = NTCREATEX_DISP_CREATE; + cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + cr.in.fname = fname; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, tctx, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + h = cr.out.file.handle; + + status = smb2_util_write(tree, h, "123", 0, 3); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h); + + /* Write stream */ + ZERO_STRUCT(cr); + cr.in.desired_access = SEC_FILE_WRITE_DATA; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.create_disposition = NTCREATEX_DISP_CREATE; + cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + cr.in.fname = sname; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, tctx, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + h = cr.out.file.handle; + + status = smb2_util_write(tree, h, "456", 0, 3); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h); + + /* Check compound read from basefile */ + smb2_transport_compound_start(tree->session->transport, 3); + + ZERO_STRUCT(cr); + cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + cr.in.desired_access = SEC_FILE_READ_DATA; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.create_disposition = NTCREATEX_DISP_OPEN; + cr.in.fname = fname; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + req[0] = smb2_create_send(tree, &cr); + + smb2_transport_compound_set_related(tree->session->transport, true); + + /* + * We send 2 reads in the compound here as the protocol + * allows the last read to be split off and possibly + * go async. Check the padding on the first read returned, + * not the second as the second may not be part of the + * returned compound. + */ + + ZERO_STRUCT(r); + h.data[0] = UINT64_MAX; + h.data[1] = UINT64_MAX; + r.in.file.handle = h; + r.in.length = 3; + r.in.offset = 0; + r.in.min_count = 1; + req[1] = smb2_read_send(tree, &r); + + ZERO_STRUCT(r2); + h.data[0] = UINT64_MAX; + h.data[1] = UINT64_MAX; + r2.in.file.handle = h; + r2.in.length = 3; + r2.in.offset = 0; + r2.in.min_count = 1; + req[2] = smb2_read_send(tree, &r2); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * We must do a manual smb2_request_receive() in order to be + * able to check the transport layer info, as smb2_read_recv() + * will destroy the req. smb2_read_recv() will call + * smb2_request_receive() again, but that's ok. + */ + if (!smb2_request_receive(req[1]) || + !smb2_request_is_ok(req[1])) { + torture_fail(tctx, "failed to receive read request"); + } + + /* + * size must be 24: 16 byte read response header plus 3 + * requested bytes padded to an 8 byte boundary. + */ + CHECK_VALUE(req[1]->in.body_size, 24); + + status = smb2_read_recv(req[1], tree, &r); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Pick up the second, possibly async, read. */ + status = smb2_read_recv(req[2], tree, &r2); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, cr.out.file.handle); + + /* Check compound read from stream */ + smb2_transport_compound_start(tree->session->transport, 3); + + ZERO_STRUCT(cr); + cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + cr.in.desired_access = SEC_FILE_READ_DATA; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.create_disposition = NTCREATEX_DISP_OPEN; + cr.in.fname = sname; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + req[0] = smb2_create_send(tree, &cr); + + smb2_transport_compound_set_related(tree->session->transport, true); + + /* + * We send 2 reads in the compound here as the protocol + * allows the last read to be split off and possibly + * go async. Check the padding on the first read returned, + * not the second as the second may not be part of the + * returned compound. + */ + + ZERO_STRUCT(r); + h.data[0] = UINT64_MAX; + h.data[1] = UINT64_MAX; + r.in.file.handle = h; + r.in.length = 3; + r.in.offset = 0; + r.in.min_count = 1; + req[1] = smb2_read_send(tree, &r); + + ZERO_STRUCT(r2); + h.data[0] = UINT64_MAX; + h.data[1] = UINT64_MAX; + r2.in.file.handle = h; + r2.in.length = 3; + r2.in.offset = 0; + r2.in.min_count = 1; + req[2] = smb2_read_send(tree, &r2); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * We must do a manual smb2_request_receive() in order to be + * able to check the transport layer info, as smb2_read_recv() + * will destroy the req. smb2_read_recv() will call + * smb2_request_receive() again, but that's ok. + */ + if (!smb2_request_receive(req[1]) || + !smb2_request_is_ok(req[1])) { + torture_fail(tctx, "failed to receive read request"); + } + + /* + * size must be 24: 16 byte read response header plus 3 + * requested bytes padded to an 8 byte boundary. + */ + CHECK_VALUE(req[1]->in.body_size, 24); + + status = smb2_read_recv(req[1], tree, &r); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Pick up the second, possibly async, read. */ + status = smb2_read_recv(req[2], tree, &r2); + CHECK_STATUS(status, NT_STATUS_OK); + + h = cr.out.file.handle; + + /* Check 2 compound (unrelateated) reads from existing stream handle */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = 3; + r.in.offset = 0; + r.in.min_count = 1; + req[0] = smb2_read_send(tree, &r); + req[1] = smb2_read_send(tree, &r); + + /* + * We must do a manual smb2_request_receive() in order to be + * able to check the transport layer info, as smb2_read_recv() + * will destroy the req. smb2_read_recv() will call + * smb2_request_receive() again, but that's ok. + */ + if (!smb2_request_receive(req[0]) || + !smb2_request_is_ok(req[0])) { + torture_fail(tctx, "failed to receive read request"); + } + if (!smb2_request_receive(req[1]) || + !smb2_request_is_ok(req[1])) { + torture_fail(tctx, "failed to receive read request"); + } + + /* + * size must be 24: 16 byte read response header plus 3 + * requested bytes padded to an 8 byte boundary. + */ + CHECK_VALUE(req[0]->in.body_size, 24); + CHECK_VALUE(req[1]->in.body_size, 24); + + status = smb2_read_recv(req[0], tree, &r); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_read_recv(req[1], tree, &r); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * now try a single read from the stream and verify there's no padding + */ + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = 3; + r.in.offset = 0; + r.in.min_count = 1; + req[0] = smb2_read_send(tree, &r); + + /* + * We must do a manual smb2_request_receive() in order to be + * able to check the transport layer info, as smb2_read_recv() + * will destroy the req. smb2_read_recv() will call + * smb2_request_receive() again, but that's ok. + */ + if (!smb2_request_receive(req[0]) || + !smb2_request_is_ok(req[0])) { + torture_fail(tctx, "failed to receive read request"); + } + + /* + * size must be 19: 16 byte read response header plus 3 + * requested bytes without padding. + */ + CHECK_VALUE(req[0]->in.body_size, 19); + + status = smb2_read_recv(req[0], tree, &r); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h); + + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + ret = true; +done: + return ret; +} + +static bool test_compound_create_write_close(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_create create; + struct smb2_write write; + struct smb2_close close; + const char *fname = "compound_create_write_close.dat"; + struct smb2_request *req[3]; + NTSTATUS status; + bool ret = false; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(create); + create.in.security_flags = 0x00; + create.in.oplock_level = 0; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + create.in.create_flags = 0x00000000; + create.in.reserved = 0x00000000; + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + create.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 3); + + req[0] = smb2_create_send(tree, &create); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(write); + write.in.file.handle = handle; + write.in.offset = 0; + write.in.data = data_blob_talloc(tctx, NULL, 1024); + + req[1] = smb2_write_send(tree, &write); + + ZERO_STRUCT(close); + close.in.file.handle = handle; + + req[2] = smb2_close_send(tree, &close); + + status = smb2_create_recv(req[0], tree, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE failed."); + + status = smb2_write_recv(req[1], &write); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "WRITE failed."); + + status = smb2_close_recv(req[2], &close); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE failed."); + + status = smb2_util_unlink(tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "File deletion failed."); + + ret = true; +done: + return ret; +} + +static bool test_compound_unrelated1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_unrelated1.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[5]; + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 5); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + req[1] = smb2_close_send(tree, &cl); + req[2] = smb2_close_send(tree, &cl); + req[3] = smb2_close_send(tree, &cl); + req[4] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[3], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[4], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_invalid1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_invalid1.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[3]; + + smb2_transport_credits_ask_num(tree->session->transport, 3); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 3); + + /* passing the first request with the related flag is invalid */ + smb2_transport_compound_set_related(tree->session->transport, true); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + req[1] = smb2_close_send(tree, &cl); + + smb2_transport_compound_set_related(tree->session->transport, false); + req[2] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + /* TODO: check why this fails with --signing=required */ + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_invalid2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_invalid2.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[5]; + struct smbXcli_tcon *saved_tcon = tree->smbXcli; + struct smbXcli_session *saved_session = tree->session->smbXcli; + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 5); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + + tree->smbXcli = smbXcli_tcon_create(tree); + smb2cli_tcon_set_values(tree->smbXcli, + NULL, /* session */ + 0xFFFFFFFF, /* tcon_id */ + 0, /* type */ + 0, /* flags */ + 0, /* capabilities */ + 0 /* maximal_access */); + + tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session, + tree->session->smbXcli); + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); + + req[1] = smb2_close_send(tree, &cl); + /* strange that this is not generating invalid parameter */ + smb2_transport_compound_set_related(tree->session->transport, false); + req[2] = smb2_close_send(tree, &cl); + req[3] = smb2_close_send(tree, &cl); + smb2_transport_compound_set_related(tree->session->transport, true); + req[4] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + status = smb2_close_recv(req[3], &cl); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + status = smb2_close_recv(req[4], &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + TALLOC_FREE(tree->smbXcli); + tree->smbXcli = saved_tcon; + TALLOC_FREE(tree->session->smbXcli); + tree->session->smbXcli = saved_session; + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_invalid3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status; + const char *fname = "compound_invalid3.dat"; + struct smb2_close cl; + bool ret = true; + struct smb2_request *req[5]; + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_util_unlink(tree, fname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + smb2_transport_compound_start(tree->session->transport, 5); + + req[0] = smb2_create_send(tree, &cr); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(cl); + cl.in.file.handle = hd; + req[1] = smb2_close_send(tree, &cl); + req[2] = smb2_close_send(tree, &cl); + /* flipping the related flag is invalid */ + smb2_transport_compound_set_related(tree->session->transport, true); + req[3] = smb2_close_send(tree, &cl); + req[4] = smb2_close_send(tree, &cl); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_close_recv(req[1], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[2], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[3], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + status = smb2_close_recv(req[4], &cl); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +static bool test_compound_invalid4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create cr; + struct smb2_read rd; + NTSTATUS status; + const char *fname = "compound_invalid4.dat"; + struct smb2_close cl; + bool ret = true; + bool ok; + struct smb2_request *req[2]; + + smb2_transport_credits_ask_num(tree->session->transport, 2); + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(cr); + cr.in.security_flags = 0x00; + cr.in.oplock_level = 0; + cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + cr.in.create_flags = 0x00000000; + cr.in.reserved = 0x00000000; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + cr.in.fname = fname; + + status = smb2_create(tree, tctx, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(rd); + rd.in.file.handle = cr.out.file.handle; + rd.in.length = 1; + rd.in.offset = 0; + req[0] = smb2_read_send(tree, &rd); + + smb2_transport_compound_set_related(tree->session->transport, true); + + /* + * Send a completely bogus request as second compound + * element. This triggers smbd_smb2_request_error() in in + * smbd_smb2_request_dispatch() before calling + * smbd_smb2_request_dispatch_update_counts(). + */ + + req[1] = smb2_request_init_tree(tree, 0xff, 0x04, false, 0); + smb2_transport_send(req[1]); + + status = smb2_read_recv(req[0], tctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + ok = smb2_request_receive(req[1]); + torture_assert(tctx, ok, "Invalid request failed\n"); + CHECK_STATUS(req[1]->status, NT_STATUS_INVALID_PARAMETER); + + ZERO_STRUCT(cl); + cl.in.file.handle = cr.out.file.handle; + + status = smb2_close(tree, &cl); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_unlink(tree, fname); +done: + return ret; +} + +/* Send a compound request where we expect the last request (Create, Notify) + * to go asynchronous. This works against a Win7 server and the reply is + * sent in two different packets. */ +static bool test_compound_interim1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status = NT_STATUS_OK; + const char *dname = "compound_interim_dir"; + struct smb2_notify nt; + bool ret = true; + struct smb2_request *req[2]; + + /* Win7 compound request implementation deviates substantially from the + * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently + * verifies the Windows behavior, not the general spec behavior. */ + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_deltree(tree, dname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_CREATE; + cr.in.fname = dname; + + smb2_transport_compound_start(tree->session->transport, 2); + + req[0] = smb2_create_send(tree, &cr); + + smb2_transport_compound_set_related(tree->session->transport, true); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(nt); + nt.in.recursive = true; + nt.in.buffer_size = 0x1000; + nt.in.file.handle = hd; + nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + nt.in.unknown = 0x00000000; + + req[1] = smb2_notify_send(tree, &nt); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_cancel(req[1]); + status = smb2_notify_recv(req[1], tree, &nt); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + smb2_util_close(tree, cr.out.file.handle); + + smb2_deltree(tree, dname); +done: + return ret; +} + +/* Send a compound request where we expect the middle request (Create, Notify, + * GetInfo) to go asynchronous. Against Win7 the sync request succeed while + * the async fails. All are returned in the same compound response. */ +static bool test_compound_interim2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle hd; + struct smb2_create cr; + NTSTATUS status = NT_STATUS_OK; + const char *dname = "compound_interim_dir"; + struct smb2_getinfo gf; + struct smb2_notify nt; + bool ret = true; + struct smb2_request *req[3]; + + /* Win7 compound request implementation deviates substantially from the + * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently + * verifies the Windows behavior, not the general spec behavior. */ + + smb2_transport_credits_ask_num(tree->session->transport, 5); + + smb2_deltree(tree, dname); + + smb2_transport_credits_ask_num(tree->session->transport, 1); + + ZERO_STRUCT(cr); + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + cr.in.create_disposition = NTCREATEX_DISP_CREATE; + cr.in.fname = dname; + + smb2_transport_compound_start(tree->session->transport, 3); + + req[0] = smb2_create_send(tree, &cr); + + smb2_transport_compound_set_related(tree->session->transport, true); + + hd.data[0] = UINT64_MAX; + hd.data[1] = UINT64_MAX; + + ZERO_STRUCT(nt); + nt.in.recursive = true; + nt.in.buffer_size = 0x1000; + nt.in.file.handle = hd; + nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + nt.in.unknown = 0x00000000; + + req[1] = smb2_notify_send(tree, &nt); + + ZERO_STRUCT(gf); + gf.in.file.handle = hd; + gf.in.info_type = SMB2_0_INFO_FILE; + gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */ + gf.in.output_buffer_length = 0x1000; + gf.in.input_buffer = data_blob_null; + + req[2] = smb2_getinfo_send(tree, &gf); + + status = smb2_create_recv(req[0], tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req[1], tree, &nt); + CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR); + + status = smb2_getinfo_recv(req[2], tree, &gf); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, cr.out.file.handle); + + smb2_deltree(tree, dname); +done: + return ret; +} + +/* Test compound related finds */ +static bool test_compound_find_related(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *dname = "compound_find_dir"; + struct smb2_create create; + struct smb2_find f; + struct smb2_handle h; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, dname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = dname; + + status = smb2_create(tree, mem_ctx, &create); + h = create.out.file.handle; + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n"); + + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + req[0] = smb2_find_send(tree, &f); + + smb2_transport_compound_set_related(tree->session->transport, true); + + req[1] = smb2_find_send(tree, &f); + + status = smb2_find_recv(req[0], mem_ctx, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n"); + + status = smb2_find_recv(req[1], mem_ctx, &f); + torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n"); + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, dname); + TALLOC_FREE(mem_ctx); + return ret; +} + +/* Test compound related finds */ +static bool test_compound_find_close(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *dname = "compound_find_dir"; + struct smb2_create create; + struct smb2_find f; + struct smb2_handle h; + struct smb2_request *req = NULL; + const int num_files = 5000; + int i; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, dname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = dname; + + smb2cli_conn_set_max_credits(tree->session->transport->conn, 256); + + status = smb2_create(tree, mem_ctx, &create); + h = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + for (i = 0; i < num_files; i++) { + create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d", + dname, i); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + } + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n"); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.max_response_size = 8*1024*1024; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + req = smb2_find_send(tree, &f); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n"); + + status = smb2_find_recv(req, mem_ctx, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n"); + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, dname); + TALLOC_FREE(mem_ctx); + return ret; +} + +/* Test compound unrelated finds */ +static bool test_compound_find_unrelated(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *dname = "compound_find_dir"; + struct smb2_create create; + struct smb2_find f; + struct smb2_handle h; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, dname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = dname; + + status = smb2_create(tree, mem_ctx, &create); + h = create.out.file.handle; + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n"); + + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + req[0] = smb2_find_send(tree, &f); + req[1] = smb2_find_send(tree, &f); + + status = smb2_find_recv(req[0], mem_ctx, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n"); + + status = smb2_find_recv(req[1], mem_ctx, &f); + torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n"); + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, dname); + TALLOC_FREE(mem_ctx); + return ret; +} + +static bool test_compound_async_flush_close(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_close cl; + struct smb2_flush fl; + const char *fname = "compound_async_flush_close"; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = false; + + /* Start clean. */ + smb2_util_unlink(tree, fname); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now do a compound flush + close handle. */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(fl); + fl.in.file.handle = fhandle; + + req[0] = smb2_flush_send(tree, &fl); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_flush_send failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(cl); + cl.in.file.handle = relhandle; + req[1] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_close_send failed\n"); + + status = smb2_flush_recv(req[0], &fl); + /* + * On Windows, this flush will usually + * succeed as we have nothing to flush, + * so allow NT_STATUS_OK. Once bug #15172 + * is fixed Samba will do the flush synchronously + * so allow NT_STATUS_OK. + */ + if (!NT_STATUS_IS_OK(status)) { + /* + * If we didn't get NT_STATUS_OK, we *must* + * get NT_STATUS_INTERNAL_ERROR if the flush + * goes async. + * + * For pre-bugfix #15172 Samba, the flush goes async and + * we should get NT_STATUS_INTERNAL_ERROR. + */ + torture_assert_ntstatus_equal_goto(tctx, + status, + NT_STATUS_INTERNAL_ERROR, + ret, + done, + "smb2_flush_recv didn't return " + "NT_STATUS_INTERNAL_ERROR.\n"); + } + status = smb2_close_recv(req[1], &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close_recv failed."); + + ZERO_STRUCT(fhandle); + + /* + * Do several more operations on the tree, spaced + * out by 1 sec sleeps to make sure the server didn't + * crash on the close. The sleeps are required to + * make test test for a crash reliable, as we ensure + * the pthread fsync internally finishes and accesses + * freed memory. Without them the test occasionally + * passes as we disconnect before the pthread fsync + * finishes. + */ + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + sleep(1); + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + sleep(1); + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ret = true; + + done: + + if (fhandle.data[0] != 0) { + smb2_util_close(tree, fhandle); + } + + smb2_util_unlink(tree, fname); + return ret; +} + +static bool test_compound_async_flush_flush(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_flush fl1; + struct smb2_flush fl2; + const char *fname = "compound_async_flush_flush"; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = false; + + /* Start clean. */ + smb2_util_unlink(tree, fname); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now do a compound flush + flush handle. */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(fl1); + fl1.in.file.handle = fhandle; + + req[0] = smb2_flush_send(tree, &fl1); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_flush_send (1) failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(fl2); + fl2.in.file.handle = relhandle; + + req[1] = smb2_flush_send(tree, &fl2); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_flush_send (2) failed\n"); + + status = smb2_flush_recv(req[0], &fl1); + /* + * On Windows, this flush will usually + * succeed as we have nothing to flush, + * so allow NT_STATUS_OK. Once bug #15172 + * is fixed Samba will do the flush synchronously + * so allow NT_STATUS_OK. + */ + if (!NT_STATUS_IS_OK(status)) { + /* + * If we didn't get NT_STATUS_OK, we *must* + * get NT_STATUS_INTERNAL_ERROR if the flush + * goes async. + * + * For pre-bugfix #15172 Samba, the flush goes async and + * we should get NT_STATUS_INTERNAL_ERROR. + */ + torture_assert_ntstatus_equal_goto(tctx, + status, + NT_STATUS_INTERNAL_ERROR, + ret, + done, + "smb2_flush_recv (1) didn't return " + "NT_STATUS_INTERNAL_ERROR.\n"); + } + + /* + * If the flush is the last entry in a compound, + * it should always succeed even if it goes async. + */ + status = smb2_flush_recv(req[1], &fl2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_flush_recv (2) failed."); + + status = smb2_util_close(tree, fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed."); + ZERO_STRUCT(fhandle); + + /* + * Do several more operations on the tree, spaced + * out by 1 sec sleeps to make sure the server didn't + * crash on the close. The sleeps are required to + * make test test for a crash reliable, as we ensure + * the pthread fsync internally finishes and accesses + * freed memory. Without them the test occasionally + * passes as we disconnect before the pthread fsync + * finishes. + */ + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + sleep(1); + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + sleep(1); + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ret = true; + + done: + + if (fhandle.data[0] != 0) { + smb2_util_close(tree, fhandle); + } + + smb2_util_unlink(tree, fname); + return ret; +} + +/* + * For Samba/smbd this test must be run against the aio_delay_inject share + * as we need to ensure the last write in the compound takes longer than + * 500 us, which is the threshold for going async in smbd SMB2 writes. + */ + +static bool test_compound_async_write_write(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_write w1; + struct smb2_write w2; + const char *fname = "compound_async_write_write"; + struct smb2_request *req[2]; + NTSTATUS status; + bool is_smbd = torture_setting_bool(tctx, "smbd", true); + bool ret = false; + + /* Start clean. */ + smb2_util_unlink(tree, fname); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now do a compound write + write handle. */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(w1); + w1.in.file.handle = fhandle; + w1.in.offset = 0; + w1.in.data = data_blob_talloc_zero(tctx, 64); + req[0] = smb2_write_send(tree, &w1); + + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_write_send (1) failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(w2); + w2.in.file.handle = relhandle; + w2.in.offset = 64; + w2.in.data = data_blob_talloc_zero(tctx, 64); + req[1] = smb2_write_send(tree, &w2); + + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_write_send (2) failed\n"); + + status = smb2_write_recv(req[0], &w1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_write_recv (1) failed."); + + if (!is_smbd) { + /* + * Windows and other servers don't go async. + */ + status = smb2_write_recv(req[1], &w2); + } else { + /* + * For smbd, the second write should go async + * as it's the last element of a compound. + */ + WAIT_FOR_ASYNC_RESPONSE(req[1]); + CHECK_VALUE(req[1]->cancel.can_cancel, true); + /* + * Now pick up the real return. + */ + status = smb2_write_recv(req[1], &w2); + } + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_write_recv (2) failed."); + + status = smb2_util_close(tree, fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed."); + ZERO_STRUCT(fhandle); + + ret = true; + + done: + + if (fhandle.data[0] != 0) { + smb2_util_close(tree, fhandle); + } + + smb2_util_unlink(tree, fname); + return ret; +} + +/* + * For Samba/smbd this test must be run against the aio_delay_inject share + * as we need to ensure the last read in the compound takes longer than + * 500 us, which is the threshold for going async in smbd SMB2 reads. + */ + +static bool test_compound_async_read_read(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_write w; + struct smb2_read r1; + struct smb2_read r2; + const char *fname = "compound_async_read_read"; + struct smb2_request *req[2]; + NTSTATUS status; + bool is_smbd = torture_setting_bool(tctx, "smbd", true); + bool ret = false; + + /* Start clean. */ + smb2_util_unlink(tree, fname); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Write 128 bytes. */ + ZERO_STRUCT(w); + w.in.file.handle = fhandle; + w.in.offset = 0; + w.in.data = data_blob_talloc_zero(tctx, 128); + status = smb2_write(tree, &w); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_write_recv (1) failed."); + + /* Now do a compound read + read handle. */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(r1); + r1.in.file.handle = fhandle; + r1.in.length = 64; + r1.in.offset = 0; + req[0] = smb2_read_send(tree, &r1); + + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_read_send (1) failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(r2); + r2.in.file.handle = relhandle; + r2.in.length = 64; + r2.in.offset = 64; + req[1] = smb2_read_send(tree, &r2); + + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_read_send (2) failed\n"); + + status = smb2_read_recv(req[0], tree, &r1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_read_recv (1) failed."); + + if (!is_smbd) { + /* + * Windows and other servers don't go async. + */ + status = smb2_read_recv(req[1], tree, &r2); + } else { + /* + * For smbd, the second write should go async + * as it's the last element of a compound. + */ + WAIT_FOR_ASYNC_RESPONSE(req[1]); + CHECK_VALUE(req[1]->cancel.can_cancel, true); + /* + * Now pick up the real return. + */ + status = smb2_read_recv(req[1], tree, &r2); + } + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_read_recv (2) failed."); + + status = smb2_util_close(tree, fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed."); + ZERO_STRUCT(fhandle); + + ret = true; + + done: + + if (fhandle.data[0] != 0) { + smb2_util_close(tree, fhandle); + } + + smb2_util_unlink(tree, fname); + return ret; +} + + +struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "compound"); + + torture_suite_add_1smb2_test(suite, "related1", test_compound_related1); + torture_suite_add_1smb2_test(suite, "related2", test_compound_related2); + torture_suite_add_1smb2_test(suite, "related3", + test_compound_related3); + torture_suite_add_1smb2_test(suite, "related4", + test_compound_related4); + torture_suite_add_1smb2_test(suite, "related5", + test_compound_related5); + torture_suite_add_1smb2_test(suite, "related6", + test_compound_related6); + torture_suite_add_1smb2_test(suite, "related7", + test_compound_related7); + torture_suite_add_1smb2_test(suite, "related8", + test_compound_related8); + torture_suite_add_1smb2_test(suite, "related9", + test_compound_related9); + torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1); + torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1); + torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2); + torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3); + torture_suite_add_1smb2_test( + suite, "invalid4", test_compound_invalid4); + torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1); + torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2); + torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break); + torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding); + torture_suite_add_1smb2_test(suite, "create-write-close", + test_compound_create_write_close); + + suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests"); + + return suite; +} + +struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "compound_find"); + + torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related); + torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated); + torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close); + + suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests"); + + return suite; +} + +struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, + "compound_async"); + + torture_suite_add_1smb2_test(suite, "flush_close", + test_compound_async_flush_close); + torture_suite_add_1smb2_test(suite, "flush_flush", + test_compound_async_flush_flush); + torture_suite_add_1smb2_test(suite, "write_write", + test_compound_async_write_write); + torture_suite_add_1smb2_test(suite, "read_read", + test_compound_async_read_read); + + suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests"); + + return suite; +} diff --git a/source4/torture/smb2/connect.c b/source4/torture/smb2/connect.c new file mode 100644 index 0000000..5a2b48b --- /dev/null +++ b/source4/torture/smb2/connect.c @@ -0,0 +1,257 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 connection operations + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +/* + send a close +*/ +static NTSTATUS torture_smb2_close(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_handle handle) +{ + struct smb2_close io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + ZERO_STRUCT(io); + io.in.file.handle = handle; + io.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + status = smb2_close(tree, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "close failed - %s\n", nt_errstr(status)); + return status; + } + + if (DEBUGLVL(1)) { + torture_comment(tctx, "Close gave:\n"); + torture_comment(tctx, "create_time = %s\n", nt_time_string(tmp_ctx, io.out.create_time)); + torture_comment(tctx, "access_time = %s\n", nt_time_string(tmp_ctx, io.out.access_time)); + torture_comment(tctx, "write_time = %s\n", nt_time_string(tmp_ctx, io.out.write_time)); + torture_comment(tctx, "change_time = %s\n", nt_time_string(tmp_ctx, io.out.change_time)); + torture_comment(tctx, "alloc_size = %lld\n", (long long)io.out.alloc_size); + torture_comment(tctx, "size = %lld\n", (long long)io.out.size); + torture_comment(tctx, "file_attr = 0x%x\n", io.out.file_attr); + } + + talloc_free(tmp_ctx); + + return status; +} + + +/* + test writing +*/ +static NTSTATUS torture_smb2_write(struct torture_context *tctx, struct smb2_tree *tree, struct smb2_handle handle) +{ + struct smb2_write w; + struct smb2_read r; + struct smb2_flush f; + NTSTATUS status; + DATA_BLOB data; + int i; + uint32_t size = torture_setting_int(tctx, "smb2maxwrite", 64*1024); + + data = data_blob_talloc(tree, NULL, size); + if (size != data.length) { + torture_comment(tctx, "data_blob_talloc(%u) failed\n", (unsigned int)size); + return NT_STATUS_NO_MEMORY; + } + + for (i=0;isession); + torture_assert_ntstatus_ok(tctx, status, "logoff failed"); + + req = smb2_logoff_send(tree->session); + torture_assert_not_null(tctx, req, "smb2_logoff_send failed"); + + req->session = NULL; + + status = smb2_logoff_recv(req); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_USER_SESSION_DELETED, + "logoff should have disabled session"); + + status = smb2_keepalive(tree->session->transport); + torture_assert_ntstatus_ok(tctx, status, "keepalive failed"); + + talloc_free(mem_ctx); + + return true; +} diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c new file mode 100644 index 0000000..c1d132d --- /dev/null +++ b/source4/torture/smb2/create.c @@ -0,0 +1,3629 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 create test suite + + Copyright (C) Andrew Tridgell 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" + +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/security.h" +#include "lib/events/events.h" + +#define FNAME "test_create.dat" +#define DNAME "smb2_open" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + return false; \ + }} while (0) + +#define CHECK_EQUAL(v, correct) do { \ + if (v != correct) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value for %s 0x%08llx - " \ + "should be 0x%08llx\n", \ + __location__, #v, \ + (unsigned long long)v, \ + (unsigned long long)correct); \ + return false; \ + }} while (0) + +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \ + if (abs(t1-t2) > 2) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + timestring(tctx, t1), \ + timestring(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (llabs((int64_t)(t-t2)) > 20000) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + nt_time_string(tctx, t), \ + nt_time_string(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != (finfo.all_info.out.field)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for field %s 0x%x - 0x%x\n", \ + __location__, #field, (int)v,\ + (int)(finfo.all_info.out.field)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)correct); \ + ret = false; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.basic_info.in.file.handle = h1; \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, \ + "(%s) Failed to set attrib 0x%x on %s\n", \ + __location__, (unsigned int)(sattrib), fname); \ + }} while (0) + +/* + test some interesting combinations found by gentest + */ +static bool test_create_gentest(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t access_mask, file_attributes_set; + uint32_t ok_mask, not_supported_mask, invalid_parameter_mask; + uint32_t not_a_directory_mask, unexpected_mask; + union smb_fileinfo q; + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.create_options = 0xF0000000; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + io.in.create_options = 0; + + io.in.file_attributes = FILE_ATTRIBUTE_DEVICE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + io.in.file_attributes = FILE_ATTRIBUTE_VOLUME; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.file_attributes = FILE_ATTRIBUTE_VOLUME; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.desired_access = 0x08000000; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + io.in.desired_access = 0x04000000; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + ok_mask = 0; + not_supported_mask = 0; + invalid_parameter_mask = 0; + not_a_directory_mask = 0; + unexpected_mask = 0; + { + int i; + for (i=0;i<32;i++) { + io.in.create_options = (uint32_t)1<ev == NULL) || (trees == NULL) || (requests == NULL) || + (ios == NULL)) { + torture_comment(tctx, ("talloc failed\n")); + ret = false; + goto done; + } + + tree->session->transport->options.request_timeout = 60; + + for (i=0; isession->transport->options.request_timeout = 60; + } + + /* cleanup */ + smb2_util_unlink(tree, fname); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + io.smb2.in.create_flags = 0; + + for (i=0; istate < SMB2_REQUEST_DONE) { + unreplied = true; + break; + } + status = smb2_create_recv(requests[i], tctx, + &(ios[i].smb2)); + + torture_comment(tctx, + "File %d returned status %s\n", i, + nt_errstr(status)); + + if (NT_STATUS_IS_OK(status)) { + num_ok += 1; + } + + if (NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { + num_collision += 1; + } + + requests[i] = NULL; + } + if (!unreplied) { + break; + } + + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "tevent_loop_once failed\n"); + ret = false; + goto done; + } + } + + if ((num_ok != 1) || (num_ok + num_collision != num_files)) { + ret = false; + } +done: + smb2_deltree(tree, fname); + + return ret; +} + +/* + test opening for delete on a read-only attribute file. +*/ + +static bool test_smb2_open_for_delete(struct torture_context *tctx, + struct smb2_tree *tree) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = DNAME "\\torture_open_for_delete.txt"; + NTSTATUS status; + struct smb2_handle h, h1; + bool ret = true; + + torture_comment(tctx, + "Checking SMB2_OPEN for delete on a readonly file.\n"); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* reasonable default parameters */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.alloc_size = 0; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_READONLY; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Create the readonly file. */ + + status = smb2_create(tree, tctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + CHECK_VAL(io.smb2.out.oplock_level, 0); + io.smb2.in.create_options = 0; + CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); + smb2_util_close(tree, h1); + + /* Now try and open for delete only - should succeed. */ + io.smb2.in.desired_access = SEC_STD_DELETE; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, tctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, io.smb2.out.file.handle); + + /* Clear readonly flag to allow file deletion */ + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE; + status = smb2_create(tree, tctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + SET_ATTRIB(FILE_ATTRIBUTE_ARCHIVE); + smb2_util_close(tree, h1); + + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + return ret; +} + +/* + test SMB2 open with a leading slash on the path. + Trying to create a directory with a leading slash + should give NT_STATUS_INVALID_PARAMETER error +*/ +static bool test_smb2_leading_slash(struct torture_context *tctx, + struct smb2_tree *tree) +{ + union smb_open io; + const char *dnameslash = "\\"DNAME; + NTSTATUS status; + bool ret = true; + + torture_comment(tctx, + "Trying to create a directory with leading slash on path\n"); + smb2_deltree(tree, dnameslash); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.oplock_level = 0; + io.smb2.in.desired_access = SEC_RIGHTS_DIR_ALL; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.fname = dnameslash; + + status = smb2_create(tree, tree, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + smb2_deltree(tree, dnameslash); + return ret; +} + +/* + test SMB2 open with an invalid impersonation level. + Should give NT_STATUS_BAD_IMPERSONATION_LEVEL error +*/ +static bool test_smb2_impersonation_level(struct torture_context *tctx, + struct smb2_tree *tree) +{ + union smb_open io; + const char *fname = DNAME "\\torture_invalid_impersonation_level.txt"; + NTSTATUS status; + struct smb2_handle h; + bool ret = true; + + torture_comment(tctx, + "Testing SMB2 open with an invalid impersonation level.\n"); + + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = 0x12345678; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + io.smb2.in.create_flags = 0; + + status = smb2_create(tree, tree, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_BAD_IMPERSONATION_LEVEL); + + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + return ret; +} + +static bool test_create_acl_file(struct torture_context *tctx, + struct smb2_tree *tree) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc on files\n"); + + return test_create_acl_ext(tctx, tree, false); +} + +static bool test_create_acl_dir(struct torture_context *tctx, + struct smb2_tree *tree) +{ + torture_comment(tctx, "Testing nttrans create with sec_desc on directories\n"); + + return test_create_acl_ext(tctx, tree, true); +} + +#define CHECK_ACCESS_FLAGS(_fh, flags) do { \ + union smb_fileinfo _q; \ + _q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; \ + _q.access_information.in.file.handle = (_fh); \ + status = smb2_getinfo_file(tree, tctx, &_q); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if (_q.access_information.out.access_flags != (flags)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect access_flags 0x%08x - should be 0x%08x\n", \ + __location__, _q.access_information.out.access_flags, (flags)); \ + ret = false; \ + goto done; \ + } \ +} while (0) + +/* + * Test creating a file with a NULL DACL. + */ +static bool test_create_null_dacl(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + const char *fname = "nulldacl.txt"; + bool ret = true; + struct smb2_handle handle; + union smb_fileinfo q; + union smb_setfileinfo s; + struct security_descriptor *sd = security_descriptor_initialise(tctx); + struct security_acl dacl; + + torture_comment(tctx, "TESTING SEC_DESC WITH A NULL DACL\n"); + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC + | SEC_STD_WRITE_OWNER; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + io.in.sec_desc = sd; + /* XXX create_options ? */ + io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + + torture_comment(tctx, "creating a file with a empty sd\n"); + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Testing the created DACL, + * the server should add the inherited DACL + * when SEC_DESC_DACL_PRESENT isn't specified + */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); + } + if (q.query_secdesc.out.sd->dacl == NULL) { + ret = false; + torture_fail_goto(tctx, done, "no DACL has been created on the server!\n"); + } + + torture_comment(tctx, "set NULL DACL\n"); + sd->type |= SEC_DESC_DACL_PRESENT; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.handle = handle; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); + } + if (q.query_secdesc.out.sd->dacl != NULL) { + ret = false; + torture_fail_goto(tctx, done, "DACL has been created on the server!\n"); + } + + io.in.create_disposition = NTCREATEX_DISP_OPEN; + + torture_comment(tctx, "try open for read control\n"); + io.in.desired_access = SEC_STD_READ_CONTROL; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_STD_READ_CONTROL); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for write\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_FILE_WRITE_DATA); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for read\n"); + io.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_FILE_READ_DATA); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for generic write\n"); + io.in.desired_access = SEC_GENERIC_WRITE; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_RIGHTS_FILE_WRITE); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for generic read\n"); + io.in.desired_access = SEC_GENERIC_READ; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_RIGHTS_FILE_READ); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "set DACL with 0 aces\n"); + ZERO_STRUCT(dacl); + dacl.revision = SECURITY_ACL_REVISION_NT4; + dacl.num_aces = 0; + sd->dacl = &dacl; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.handle = handle; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); + } + if (q.query_secdesc.out.sd->dacl == NULL) { + ret = false; + torture_fail_goto(tctx, done, "no DACL has been created on the server!\n"); + } + if (q.query_secdesc.out.sd->dacl->num_aces != 0) { + torture_result(tctx, TORTURE_FAIL, "DACL has %u aces!\n", + q.query_secdesc.out.sd->dacl->num_aces); + ret = false; + goto done; + } + + torture_comment(tctx, "try open for read control\n"); + io.in.desired_access = SEC_STD_READ_CONTROL; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ACCESS_FLAGS(io.out.file.handle, + SEC_STD_READ_CONTROL); + smb2_util_close(tree, io.out.file.handle); + + torture_comment(tctx, "try open for write => access_denied\n"); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "try open for read => access_denied\n"); + io.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "try open for generic write => access_denied\n"); + io.in.desired_access = SEC_GENERIC_WRITE; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "try open for generic read => access_denied\n"); + io.in.desired_access = SEC_GENERIC_READ; + status = smb2_create(tree, tctx, &io); + if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } else { + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + } + + torture_comment(tctx, "set empty sd\n"); + sd->type &= ~SEC_DESC_DACL_PRESENT; + sd->dacl = NULL; + + s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + s.set_secdesc.in.file.handle = handle; + s.set_secdesc.in.secinfo_flags = SECINFO_DACL; + s.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &s); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "get the sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Testing the modified DACL */ + if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { + ret = false; + torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); + } + if (q.query_secdesc.out.sd->dacl != NULL) { + ret = false; + torture_fail_goto(tctx, done, "DACL has been created on the server!\n"); + } +done: + smb2_util_close(tree, handle); + smb2_util_unlink(tree, fname); + smb2_tdis(tree); + smb2_logoff(tree->session); + return ret; +} + +/* + test SMB2 mkdir with OPEN_IF on the same name twice. + Must use 2 connections to hit the race. +*/ + +static bool test_mkdir_dup(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "mkdir_dup"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_tree **trees; + struct smb2_request **requests; + union smb_open *ios; + int i, num_files = 2; + int num_ok = 0; + int num_created = 0; + int num_existed = 0; + + torture_comment(tctx, + "Testing SMB2 Create Directory with multiple connections\n"); + trees = talloc_array(tctx, struct smb2_tree *, num_files); + requests = talloc_array(tctx, struct smb2_request *, num_files); + ios = talloc_array(tctx, union smb_open, num_files); + if ((tctx->ev == NULL) || (trees == NULL) || (requests == NULL) || + (ios == NULL)) { + torture_fail(tctx, ("talloc failed\n")); + ret = false; + goto done; + } + + tree->session->transport->options.request_timeout = 60; + + for (i=0; isession->transport->options.request_timeout = 60; + } + + /* cleanup */ + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, fname); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + io.smb2.in.create_flags = 0; + + for (i=0; istate < SMB2_REQUEST_DONE) { + unreplied = true; + break; + } + status = smb2_create_recv(requests[i], tctx, + &(ios[i].smb2)); + + if (NT_STATUS_IS_OK(status)) { + num_ok += 1; + + if (ios[i].smb2.out.create_action == + NTCREATEX_ACTION_CREATED) { + num_created++; + } + if (ios[i].smb2.out.create_action == + NTCREATEX_ACTION_EXISTED) { + num_existed++; + } + } else { + torture_fail(tctx, + talloc_asprintf(tctx, + "File %d returned status %s\n", i, + nt_errstr(status))); + } + + + requests[i] = NULL; + } + if (!unreplied) { + break; + } + + if (tevent_loop_once(tctx->ev) != 0) { + torture_fail(tctx, "tevent_loop_once failed\n"); + ret = false; + goto done; + } + } + + if (num_ok != 2) { + torture_fail(tctx, + talloc_asprintf(tctx, + "num_ok == %d\n", num_ok)); + ret = false; + } + if (num_created != 1) { + torture_fail(tctx, + talloc_asprintf(tctx, + "num_created == %d\n", num_created)); + ret = false; + } + if (num_existed != 1) { + torture_fail(tctx, + talloc_asprintf(tctx, + "num_existed == %d\n", num_existed)); + ret = false; + } +done: + smb2_deltree(tree, fname); + + return ret; +} + +/* + test directory creation with an initial allocation size > 0 +*/ +static bool test_dir_alloc_size(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + const char *dname = DNAME "\\torture_alloc_size.dir"; + NTSTATUS status; + struct smb2_create c; + struct smb2_handle h1 = {{0}}, h2; + + torture_comment(tctx, "Checking initial allocation size on directories\n"); + + smb2_deltree(tree, dname); + + status = torture_smb2_testdir(tree, DNAME, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed"); + + ZERO_STRUCT(c); + c.in.create_disposition = NTCREATEX_DISP_CREATE; + c.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + c.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + c.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + c.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + c.in.fname = dname; + /* + * An insanely large value so we can check the value is + * ignored: Samba either returns 0 (current behaviour), or, + * once vfswrap_get_alloc_size() is fixed to allow retrieving + * the allocated size for directories, returns + * smb_roundup(..., stat.st_size) which would be 1 MB by + * default. + * + * Windows returns 0 for empty directories, once directories + * have a few entries it starts replying with values > 0. + */ + c.in.alloc_size = 1024*1024*1024; + + status = smb2_create(tree, tctx, &c); + h2 = c.out.file.handle; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "dir create with initial alloc size failed"); + + smb2_util_close(tree, h2); + + torture_comment(tctx, "Got directory alloc size: %ju\n", (uintmax_t)c.out.alloc_size); + + /* + * See above for the rational for this test + */ + if (c.out.alloc_size > 1024*1024) { + torture_fail_goto(tctx, done, talloc_asprintf(tctx, "bad alloc size: %ju", + (uintmax_t)c.out.alloc_size)); + } + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, DNAME); + return ret; +} + +static bool test_twrp_write(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + struct smb2_handle h1 = {{0}}; + NTSTATUS status; + bool ret = true; + char *p = NULL; + struct tm tm; + time_t t; + uint64_t nttime; + const char *file = NULL; + const char *snapshot = NULL; + uint32_t expected_access; + union smb_fileinfo getinfo; + union smb_setfileinfo setinfo; + struct security_descriptor *sd = NULL, *sd_orig = NULL; + const char *owner_sid = NULL; + struct create_disps_tests { + const char *file; + uint32_t create_disposition; + uint32_t create_options; + NTSTATUS expected_status; + }; + struct create_disps_tests *cd_test = NULL; + + file = torture_setting_string(tctx, "twrp_file", NULL); + if (file == NULL) { + torture_skip(tctx, "missing 'twrp_file' option\n"); + } + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_skip(tctx, "missing 'twrp_snapshot' option\n"); + } + + torture_comment(tctx, "Testing timewarp (%s) (%s)\n", file, snapshot); + + setenv("TZ", "GMT", 1); + + /* strptime does not set tm.tm_isdst but mktime assumes DST is in + * effect if it is greater than 1. */ + ZERO_STRUCT(tm); + + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + t = mktime(&tm); + unix_to_nt_time(&nttime, t); + + io = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = file, + .in.query_maximal_access = true, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + smb2_util_close(tree, io.out.file.handle); + + expected_access = SEC_RIGHTS_FILE_ALL & + ~(SEC_FILE_EXECUTE | SEC_DIR_DELETE_CHILD); + + torture_assert_int_equal_goto(tctx, + io.out.maximal_access & expected_access, + expected_access, + ret, done, "Bad access\n"); + + { + /* + * Test create dispositions + */ + struct create_disps_tests cd_tests[] = { + { + .file = file, + .create_disposition = NTCREATEX_DISP_OPEN, + .expected_status = NT_STATUS_OK, + }, + { + .file = file, + .create_disposition = NTCREATEX_DISP_OPEN_IF, + .expected_status = NT_STATUS_OK, + }, + { + .file = file, + .create_disposition = NTCREATEX_DISP_OVERWRITE, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = file, + .create_disposition = NTCREATEX_DISP_OVERWRITE_IF, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = file, + .create_disposition = NTCREATEX_DISP_SUPERSEDE, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newfile", + .create_disposition = NTCREATEX_DISP_OPEN_IF, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newfile", + .create_disposition = NTCREATEX_DISP_OVERWRITE_IF, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newfile", + .create_disposition = NTCREATEX_DISP_CREATE, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newfile", + .create_disposition = NTCREATEX_DISP_SUPERSEDE, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newdir", + .create_disposition = NTCREATEX_DISP_OPEN_IF, + .create_options = NTCREATEX_OPTIONS_DIRECTORY, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = "newdir", + .create_disposition = NTCREATEX_DISP_CREATE, + .create_options = NTCREATEX_OPTIONS_DIRECTORY, + .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED, + }, + { + .file = NULL, + }, + }; + + for (cd_test = &cd_tests[0]; cd_test->file != NULL; cd_test++) { + io = (struct smb2_create) { + .in.fname = cd_test->file, + .in.create_disposition = cd_test->create_disposition, + .in.create_options = cd_test->create_options, + + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_equal_goto( + tctx, status, cd_test->expected_status, ret, done, + "Bad status\n"); + } + } + + io = (struct smb2_create) { + .in.desired_access = expected_access, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = file, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h1 = io.out.file.handle; + + status = smb2_util_write(tree, h1, "123", 0, 3); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, "smb2_create\n"); + + /* + * Verify access mask + */ + + ZERO_STRUCT(getinfo); + getinfo.generic.level = RAW_FILEINFO_ACCESS_INFORMATION; + getinfo.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree, tree, &getinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file\n"); + + torture_assert_int_equal_goto( + tctx, + getinfo.access_information.out.access_flags, + expected_access, + ret, done, + "Bad access mask\n"); + + /* + * Check we can't set various things + */ + + ZERO_STRUCT(getinfo); + getinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + getinfo.query_secdesc.in.file.handle = h1; + getinfo.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + + status = smb2_getinfo_file(tree, tctx, &getinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file\n"); + + sd_orig = getinfo.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA, + 0, + NULL); + + /* Try to set ACL */ + + ZERO_STRUCT(setinfo); + setinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + setinfo.set_secdesc.in.file.handle = h1; + setinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; + setinfo.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, + "smb2_setinfo_file\n"); + + /* Try to delete */ + + ZERO_STRUCT(setinfo); + setinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + setinfo.disposition_info.in.delete_on_close = 1; + setinfo.generic.in.file.handle = h1; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, + "smb2_setinfo_file\n"); + + ZERO_STRUCT(setinfo); + setinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN; + setinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + setinfo.generic.in.file.handle = h1; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, + "smb2_setinfo_file\n"); + + /* Try to truncate */ + + ZERO_STRUCT(setinfo); + setinfo.generic.level = SMB_SFILEINFO_END_OF_FILE_INFORMATION; + setinfo.generic.in.file.handle = h1; + setinfo.end_of_file_info.in.size = 0x100000; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, + "smb2_setinfo_file\n"); + + /* Try to set a hardlink */ + + ZERO_STRUCT(setinfo); + setinfo.generic.level = RAW_SFILEINFO_LINK_INFORMATION; + setinfo.generic.in.file.handle = h1; + setinfo.link_information.in.new_name = "hardlink"; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_NOT_SAME_DEVICE, + ret, done, + "smb2_setinfo_file\n"); + + /* Try to rename */ + + ZERO_STRUCT(setinfo); + setinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + setinfo.rename_information.in.file.handle = h1; + setinfo.rename_information.in.new_name = "renamed"; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_equal_goto( + tctx, + status, + NT_STATUS_NOT_SAME_DEVICE, + ret, done, + "smb2_setinfo_file\n"); + + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + return ret; +} + +static bool test_twrp_stream(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + bool ret = true; + char *p = NULL; + struct tm tm; + time_t t; + uint64_t nttime; + const char *file = NULL; + const char *stream = NULL; + const char *snapshot = NULL; + int stream_size; + char *path = NULL; + uint8_t *buf = NULL; + struct smb2_handle h1 = {{0}}; + struct smb2_read r; + + file = torture_setting_string(tctx, "twrp_file", NULL); + if (file == NULL) { + torture_skip(tctx, "missing 'twrp_file' option\n"); + } + + stream = torture_setting_string(tctx, "twrp_stream", NULL); + if (stream == NULL) { + torture_skip(tctx, "missing 'twrp_stream' option\n"); + } + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_skip(tctx, "missing 'twrp_snapshot' option\n"); + } + + stream_size = torture_setting_int(tctx, "twrp_stream_size", 0); + if (stream_size == 0) { + torture_skip(tctx, "missing 'twrp_stream_size' option\n"); + } + + torture_comment(tctx, "Testing timewarp on stream (%s) (%s)\n", + file, snapshot); + + path = talloc_asprintf(tree, "%s:%s", file, stream); + torture_assert_not_null_goto(tctx, path, ret, done, "path\n"); + + buf = talloc_zero_array(tree, uint8_t, stream_size); + torture_assert_not_null_goto(tctx, buf, ret, done, "buf\n"); + + setenv("TZ", "GMT", 1); + + /* strptime does not set tm.tm_isdst but mktime assumes DST is in + * effect if it is greater than 1. */ + ZERO_STRUCT(tm); + + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + t = mktime(&tm); + unix_to_nt_time(&nttime, t); + + io = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = path, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h1 = io.out.file.handle; + + r = (struct smb2_read) { + .in.file.handle = h1, + .in.length = stream_size, + .in.offset = 0, + }; + + status = smb2_read(tree, tree, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + + smb2_util_close(tree, h1); + +done: + return ret; +} + +static bool test_twrp_openroot(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + bool ret = true; + char *p = NULL; + struct tm tm; + time_t t; + uint64_t nttime; + const char *snapshot = NULL; + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_skip(tctx, "missing 'twrp_snapshot' option\n"); + } + + torture_comment(tctx, "Testing open of root of " + "share with timewarp (%s)\n", + snapshot); + + setenv("TZ", "GMT", 1); + + /* strptime does not set tm.tm_isdst but mktime assumes DST is in + * effect if it is greater than 1. */ + ZERO_STRUCT(tm); + + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + t = mktime(&tm); + unix_to_nt_time(&nttime, t); + + io = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = "", + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + smb2_util_close(tree, io.out.file.handle); + +done: + return ret; +} + +static bool test_twrp_listdir(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create create; + struct smb2_handle h = {{0}}; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + char *p = NULL; + struct tm tm; + time_t t; + uint64_t nttime; + const char *snapshot = NULL; + uint64_t normal_fileid; + uint64_t snapshot_fileid; + NTSTATUS status; + bool ret = true; + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_fail(tctx, "missing 'twrp_snapshot' option\n"); + } + + torture_comment(tctx, "Testing File-Ids of directory listing " + "with timewarp (%s)\n", + snapshot); + + setenv("TZ", "GMT", 1); + + /* strptime does not set tm.tm_isdst but mktime assumes DST is in + * effect if it is greater than 1. */ + ZERO_STRUCT(tm); + + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + t = mktime(&tm); + unix_to_nt_time(&nttime, t); + + /* + * 1: Query the file's File-Id + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = "subdir/hardlink", + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + smb2_util_close(tree, create.out.file.handle); + normal_fileid = BVAL(&create.out.on_disk_id, 0); + + /* + * 2: check directory listing of the file returns same File-Id + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_DIR_LIST, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = "subdir", + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h = create.out.file.handle; + + find = (struct smb2_find) { + .in.file.handle = h, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + smb2_util_close(tree, h); + + torture_assert_int_equal_goto(tctx, count, 3, ret, done, "Bad count\n"); + torture_assert_str_equal_goto(tctx, + d[2].id_both_directory_info.name.s, + "hardlink", + ret, done, "bad name"); + torture_assert_u64_equal_goto(tctx, + d[2].id_both_directory_info.file_id, + normal_fileid, + ret, done, "bad fileid\n"); + + /* + * 3: Query File-Id of snapshot of the file and check the File-Id is + * different compared to the basefile + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = "subdir/hardlink", + .in.query_on_disk_id = true, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + smb2_util_close(tree, create.out.file.handle); + + snapshot_fileid = BVAL(&create.out.on_disk_id, 0); + + /* + * 4: List directory of the snapshot and check the File-Id returned here + * is the same as in 3. + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_DIR_LIST, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = "subdir", + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h = create.out.file.handle; + + find = (struct smb2_find) { + .in.file.handle = h, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + smb2_util_close(tree, h); + + torture_assert_int_equal_goto(tctx, count, 3, ret, done, "Bad count\n"); + torture_assert_str_equal_goto(tctx, + d[2].id_both_directory_info.name.s, + "hardlink", + ret, done, "bad name"); + torture_assert_u64_equal_goto(tctx, + snapshot_fileid, + d[2].id_both_directory_info.file_id, + ret, done, "bad fileid\n"); + +done: + return ret; +} + +static bool test_fileid(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = DNAME "\\foo"; + const char *sname = DNAME "\\foo:bar"; + struct smb2_handle testdirh; + struct smb2_handle h1; + struct smb2_create create; + union smb_fileinfo finfo; + union smb_setfileinfo sinfo; + struct smb2_find f; + unsigned int count; + union smb_search_data *d; + uint64_t expected_fileid; + uint64_t returned_fileid; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + /* + * Initial create with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.fname = fname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + expected_fileid = BVAL(&create.out.on_disk_id, 0); + + /* + * Getinfo the File-ID on the just opened handle + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Open existing with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = fname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the just opened handle + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Overwrite with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OVERWRITE, + .in.fname = fname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the open with overwrite handle + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Do some modifications on the basefile (IO, setinfo), verifying + * File-ID after each step. + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = fname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + + status = smb2_util_write(tree, h1, "foo", 0, strlen("foo")); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + sinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + }; + unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL)); + + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Create stream, check the stream's File-ID, should be the same as the + * base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the created stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Open stream, check the stream's File-ID, should be the same as the + * base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the opened stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Overwrite stream, check the stream's File-ID, should be the same as + * the base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OVERWRITE, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the overwritten stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Do some modifications on the stream (IO, setinfo), verifying File-ID + * after each step. + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + + status = smb2_util_write(tree, h1, "foo", 0, strlen("foo")); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + sinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + }; + unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL)); + + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final open of the basefile with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = fname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final Getinfo checking File-ID + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final list directory, verifying the operations on basefile and stream + * didn't modify the base file metadata. + */ + f = (struct smb2_find) { + .in.file.handle = testdirh, + .in.pattern = "foo", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART, + }; + + status = smb2_find_level(tree, tree, &f, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + torture_assert_u64_equal_goto(tctx, + d->id_both_directory_info.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + +done: + smb2_util_close(tree, testdirh); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + return ret; +} + +static bool test_fileid_dir(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *dname = DNAME "\\foo"; + const char *sname = DNAME "\\foo:bar"; + struct smb2_handle testdirh; + struct smb2_handle h1; + struct smb2_create create; + union smb_fileinfo finfo; + union smb_setfileinfo sinfo; + struct smb2_find f; + unsigned int count; + union smb_search_data *d; + uint64_t expected_fileid; + uint64_t returned_fileid; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + /* + * Initial directory create with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.fname = dname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + expected_fileid = BVAL(&create.out.on_disk_id, 0); + + /* + * Getinfo the File-ID on the just opened handle + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Open existing directory with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.fname = dname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the just opened handle + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Create stream, check the stream's File-ID, should be the same as the + * base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the created stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Open stream, check the stream's File-ID, should be the same as the + * base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the opened stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Overwrite stream, check the stream's File-ID, should be the same as + * the base file (sic!, tested against Windows). + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OVERWRITE, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Getinfo the File-ID on the overwritten stream + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Do some modifications on the stream (IO, setinfo), verifying File-ID + * after each step. + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = sname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + + status = smb2_util_write(tree, h1, "foo", 0, strlen("foo")); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + sinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + }; + unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL)); + + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final open of the directory with QFID + */ + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = dname, + .in.query_on_disk_id = true, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test file could not be created\n"); + h1 = create.out.file.handle; + returned_fileid = BVAL(&create.out.on_disk_id, 0); + torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final Getinfo checking File-ID + */ + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + + /* + * Final list directory, verifying the operations on basefile and stream + * didn't modify the base file metadata. + */ + f = (struct smb2_find) { + .in.file.handle = testdirh, + .in.pattern = "foo", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART, + }; + + status = smb2_find_level(tree, tree, &f, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + torture_assert_u64_equal_goto(tctx, + d->id_both_directory_info.file_id, + expected_fileid, + ret, done, "bad fileid\n"); + +done: + smb2_util_close(tree, testdirh); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + return ret; +} + +static bool test_fileid_unique_object( + struct torture_context *tctx, + struct smb2_tree *tree, + unsigned int num_objs, + bool create_dirs) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char *fname = NULL; + struct smb2_handle testdirh; + struct smb2_handle h1; + struct smb2_create create; + unsigned int i; + uint64_t fileid_array[num_objs]; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "test_fileid_unique failed\n"); + smb2_util_close(tree, testdirh); + + /* Create num_obj files as rapidly as we can. */ + for (i = 0; i < num_objs; i++) { + fname = talloc_asprintf(mem_ctx, + "%s\\testfile.%u", + DNAME, + i); + torture_assert_goto(tctx, + fname != NULL, + ret, + done, + "talloc failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_ATTRIBUTE, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.fname = fname, + }; + + if (create_dirs) { + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.create_options = FILE_DIRECTORY_FILE; + } + + status = smb2_create(tree, tctx, &create); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(tctx, + talloc_asprintf(tctx, + "test file %s could not be created\n", + fname)); + TALLOC_FREE(fname); + ret = false; + goto done; + } + + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + TALLOC_FREE(fname); + } + + /* + * Get the file ids. + */ + for (i = 0; i < num_objs; i++) { + union smb_fileinfo finfo; + + fname = talloc_asprintf(mem_ctx, + "%s\\testfile.%u", + DNAME, + i); + torture_assert_goto(tctx, + fname != NULL, + ret, + done, + "talloc failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_ATTRIBUTE, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = fname, + }; + + if (create_dirs) { + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.create_options = FILE_DIRECTORY_FILE; + } + + status = smb2_create(tree, tctx, &create); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(tctx, + talloc_asprintf(tctx, + "test file %s could not " + "be opened: %s\n", + fname, + nt_errstr(status))); + TALLOC_FREE(fname); + ret = false; + goto done; + } + + h1 = create.out.file.handle; + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_fail(tctx, + talloc_asprintf(tctx, + "failed to get fileid for " + "test file %s: %s\n", + fname, + nt_errstr(status))); + TALLOC_FREE(fname); + ret = false; + goto done; + } + smb2_util_close(tree, h1); + + fileid_array[i] = finfo.all_info2.out.file_id; + TALLOC_FREE(fname); + } + + /* All returned fileids must be unique. 100 is small so brute force. */ + for (i = 0; i < num_objs - 1; i++) { + unsigned int j; + for (j = i + 1; j < num_objs; j++) { + if (fileid_array[i] == fileid_array[j]) { + torture_fail(tctx, + talloc_asprintf(tctx, + "fileid %u == fileid %u (0x%"PRIu64")\n", + i, + j, + fileid_array[i])); + ret = false; + goto done; + } + } + } + +done: + + smb2_util_close(tree, testdirh); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + return ret; +} + +static bool test_fileid_unique( + struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_fileid_unique_object(tctx, tree, 100, false); +} + +static bool test_fileid_unique_dir( + struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_fileid_unique_object(tctx, tree, 100, true); +} + +static bool test_dosattr_tmp_dir(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create c; + struct smb2_handle h1 = {{0}}; + const char *fname = DNAME; + + smb2_deltree(tree, fname); + smb2_util_rmdir(tree, fname); + + c = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_ALL, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.fname = DNAME, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h1 = c.out.file.handle; + + /* Try to set temporary attribute on directory */ + SET_ATTRIB(FILE_ATTRIBUTE_TEMPORARY); + + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_INVALID_PARAMETER, + ret, done, + "Unexpected setinfo result\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + + return ret; +} + +/* + test opening quota fakefile handle and returned attributes +*/ +static bool test_smb2_open_quota_fake_file(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "$Extend\\$Quota:$Q:$INDEX_ALLOCATION"; + struct smb2_create create; + struct smb2_handle h = {{0}}; + NTSTATUS status; + bool ret = true; + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_READ, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tree, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h = create.out.file.handle; + + torture_assert_u64_equal_goto(tctx, + create.out.file_attr, + FILE_ATTRIBUTE_HIDDEN + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_ARCHIVE, + ret, + done, + "Wrong attributes\n"); + + torture_assert_u64_equal_goto(tctx, + create.out.create_time, 0, + ret, + done, + "create_time is not 0\n"); + torture_assert_u64_equal_goto(tctx, + create.out.access_time, 0, + ret, + done, + "access_time is not 0\n"); + torture_assert_u64_equal_goto(tctx, + create.out.write_time, 0, + ret, + done, + "write_time is not 0\n"); + torture_assert_u64_equal_goto(tctx, + create.out.change_time, 0, + ret, + done, + "change_time is not 0\n"); + +done: + smb2_util_close(tree, h); + return ret; +} + +/** + Find Maximum Path Length + */ +static bool generate_path(const size_t len, + char *buffer, + const size_t buf_len) +{ + size_t i; + + if (len >= buf_len) { + return false; + } + + for (i = 0; i < len ; i++) { + buffer[i] = (char)(i % 10) + 48; + } + buffer[i] = '\0'; + return true; +} + +static bool test_path_length_test(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const size_t max_name = 2048; + char *name = talloc_array(tctx, char, max_name); + struct smb2_handle fh = {{0}}; + size_t length = 128; + size_t max_file_name = 0; + size_t max_path_length = 0; + char *path_ok = NULL; + char *path_next = NULL; + char *topdir = NULL; + bool is_interactive = torture_setting_bool(tctx, "interactive", false); + NTSTATUS status; + bool ret = true; + + if (!is_interactive) { + torture_result(tctx, TORTURE_SKIP, + "Interactive Test: Skipping... " + "(enable with --interactive)\n"); + return ret; + } + + torture_comment(tctx, "Testing filename and path lengths\n"); + + /* Find Longest File Name */ + for (length = 128; length < max_name; length++) { + if (!generate_path(length, name, max_name)) { + torture_result(tctx, TORTURE_FAIL, + "Failed to generate path."); + return false; + } + + status = torture_smb2_testfile(tree, name, &fh); + if (!NT_STATUS_IS_OK(status)) { + break; + } + + smb2_util_close(tree, fh); + smb2_util_unlink(tree, name); + + max_file_name = length; + } + + torture_assert_int_not_equal_goto(tctx, length, max_name, ret, done, + "Name too big\n"); + + torture_comment(tctx, "Max file name length: %zu\n", max_file_name); + + /* Remove one char that caused the failure above */ + name[max_file_name] = '\0'; + + path_ok = talloc_strdup(tree, name); + torture_assert_not_null_goto(tctx, path_ok, ret, done, + "talloc_strdup failed\n"); + + topdir = talloc_strdup(tree, name); + torture_assert_not_null_goto(tctx, topdir, ret, done, + "talloc_strdup failed\n"); + + status = smb2_util_mkdir(tree, path_ok); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "mkdir [%s] failed: %s\n", + path_ok, nt_errstr(status)); + torture_result(tctx, TORTURE_FAIL, "Initial mkdir failed"); + return false; + } + + while (true) { + path_next = talloc_asprintf(tctx, "%s\\%s", path_ok, name); + torture_assert_not_null_goto(tctx, path_next, ret, done, + "talloc_asprintf failed\n"); + + status = smb2_util_mkdir(tree, path_next); + if (!NT_STATUS_IS_OK(status)) { + break; + } + + path_ok = path_next; + } + + for (length = 1; length < max_name; length++) { + if (!generate_path(length, name, max_name)) { + torture_result(tctx, TORTURE_FAIL, + "Failed to generate path."); + return false; + } + + path_next = talloc_asprintf(tctx, "%s\\%s", path_ok, name); + torture_assert_not_null_goto(tctx, path_next, ret, done, + "talloc_asprintf failed\n"); + + status = torture_smb2_testfile(tree, path_next, &fh); + if (!NT_STATUS_IS_OK(status)) { + break; + } + smb2_util_close(tree, fh); + path_ok = path_next; + } + + max_path_length = talloc_array_length(path_ok); + + torture_comment(tctx, "Max path name length: %zu\n", max_path_length); + +done: + return ret; +} + +/* + basic testing of SMB2 read +*/ +struct torture_suite *torture_smb2_create_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "create"); + + torture_suite_add_1smb2_test(suite, "gentest", test_create_gentest); + torture_suite_add_1smb2_test(suite, "blob", test_create_blob); + torture_suite_add_1smb2_test(suite, "open", test_smb2_open); + torture_suite_add_1smb2_test(suite, "brlocked", test_smb2_open_brlocked); + torture_suite_add_1smb2_test(suite, "multi", test_smb2_open_multi); + torture_suite_add_1smb2_test(suite, "delete", test_smb2_open_for_delete); + torture_suite_add_1smb2_test(suite, "leading-slash", test_smb2_leading_slash); + torture_suite_add_1smb2_test(suite, "impersonation", test_smb2_impersonation_level); + torture_suite_add_1smb2_test(suite, "aclfile", test_create_acl_file); + torture_suite_add_1smb2_test(suite, "acldir", test_create_acl_dir); + torture_suite_add_1smb2_test(suite, "nulldacl", test_create_null_dacl); + torture_suite_add_1smb2_test(suite, "mkdir-dup", test_mkdir_dup); + torture_suite_add_1smb2_test(suite, "dir-alloc-size", test_dir_alloc_size); + torture_suite_add_1smb2_test(suite, "dosattr_tmp_dir", test_dosattr_tmp_dir); + torture_suite_add_1smb2_test(suite, "quota-fake-file", test_smb2_open_quota_fake_file); + torture_suite_add_1smb2_test(suite, "path-length", test_path_length_test); + torture_suite_add_1smb2_test(suite, "bench-path-contention-shared", test_smb2_bench_path_contention_shared); + + suite->description = talloc_strdup(suite, "SMB2-CREATE tests"); + + return suite; +} + +struct torture_suite *torture_smb2_twrp_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "twrp"); + + torture_suite_add_1smb2_test(suite, "write", test_twrp_write); + torture_suite_add_1smb2_test(suite, "stream", test_twrp_stream); + torture_suite_add_1smb2_test(suite, "openroot", test_twrp_openroot); + torture_suite_add_1smb2_test(suite, "listdir", test_twrp_listdir); + + suite->description = talloc_strdup(suite, "SMB2-TWRP tests"); + + return suite; +} + +/* + basic testing of SMB2 File-IDs +*/ +struct torture_suite *torture_smb2_fileid_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "fileid"); + + torture_suite_add_1smb2_test(suite, "fileid", test_fileid); + torture_suite_add_1smb2_test(suite, "fileid-dir", test_fileid_dir); + torture_suite_add_1smb2_test(suite, "unique", test_fileid_unique); + torture_suite_add_1smb2_test(suite, "unique-dir", test_fileid_unique_dir); + + suite->description = talloc_strdup(suite, "SMB2-FILEID tests"); + + return suite; +} + +static bool test_no_stream(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create c; + NTSTATUS status; + bool ret = true; + const char *names[] = { + "test_no_stream::$DATA", + "test_no_stream::foooooooooooo", + "test_no_stream:stream", + "test_no_stream:stream:$DATA", + NULL + }; + int i; + + for (i = 0; names[i] != NULL; i++) { + c = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = names[i], + }; + + status = smb2_create(tree, tctx, &c); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) { + torture_comment( + tctx, "Expected NT_STATUS_OBJECT_NAME_INVALID, " + "got %s, name: '%s'\n", + nt_errstr(status), names[i]); + torture_fail_goto(tctx, done, "Bad create result\n"); + } + } +done: + return ret; +} + +struct torture_suite *torture_smb2_create_no_streams_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "create_no_streams"); + + torture_suite_add_1smb2_test(suite, "no_stream", test_no_stream); + + suite->description = talloc_strdup(suite, "SMB2-CREATE stream test on share without streams support"); + + return suite; +} diff --git a/source4/torture/smb2/credits.c b/source4/torture/smb2/credits.c new file mode 100644 index 0000000..b06bae7 --- /dev/null +++ b/source4/torture/smb2/credits.c @@ -0,0 +1,268 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 credits + + Copyright (C) Ralph Boehme 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/param/param.h" + +/** + * Request 64k credits in negprot/sessionsetup and require at least 8k + * + * This passes against Windows 2016 + **/ +static bool test_session_setup_credits_granted(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_tree *tree = NULL; + uint16_t cur_credits; + NTSTATUS status; + bool ret = true; + + transport = _tree->session->transport; + options = transport->options; + + status = smb2_logoff(_tree->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_logoff failed\n"); + TALLOC_FREE(_tree); + + options.max_credits = 65535; + + ret = torture_smb2_connection_ext(tctx, 0, &options, &tree); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_smb2_connection_ext failed\n"); + + transport = tree->session->transport; + + cur_credits = smb2cli_conn_get_cur_credits(transport->conn); + if (cur_credits < 8192) { + torture_result(tctx, TORTURE_FAIL, + "Server only granted %" PRIu16" credits\n", + cur_credits); + ret = false; + goto done; + } + +done: + TALLOC_FREE(tree); + return ret; +} + +/** + * Request 64K credits in a single SMB2 request and requite at least 8192 + * + * This passes against Windows 2016 + **/ +static bool test_single_req_credits_granted(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_tree *tree = NULL; + struct smb2_handle h = {{0}}; + struct smb2_create create; + const char *fname = "single_req_credits_granted.dat"; + uint16_t cur_credits; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(_tree, fname); + + transport = _tree->session->transport; + options = transport->options; + + status = smb2_logoff(_tree->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_logoff failed\n"); + TALLOC_FREE(_tree); + + options.max_credits = 1; + + ret = torture_smb2_connection_ext(tctx, 0, &options, &tree); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_smb2_connection_ext failed\n"); + + transport = tree->session->transport; + + cur_credits = smb2cli_conn_get_cur_credits(transport->conn); + if (cur_credits != 1) { + torture_result(tctx, TORTURE_FAIL, + "Only wanted 1 credit but server granted %" PRIu16"\n", + cur_credits); + ret = false; + goto done; + } + + smb2cli_conn_set_max_credits(transport->conn, 65535); + + ZERO_STRUCT(create); + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.fname = fname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h = create.out.file.handle; + + cur_credits = smb2cli_conn_get_cur_credits(transport->conn); + if (cur_credits < 8192) { + torture_result(tctx, TORTURE_FAIL, + "Server only granted %" PRIu16" credits\n", + cur_credits); + ret = false; + goto done; + } + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_util_unlink(tree, fname); + TALLOC_FREE(tree); + return ret; +} + +static bool test_crediting_skipped_mid(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_tree *tree = NULL; + struct smb2_handle h = {{0}}; + struct smb2_create create; + const char *fname = "skipped_mid.dat"; + uint64_t mid; + uint16_t cur_credits; + NTSTATUS status; + bool ret = true; + int i; + + smb2_util_unlink(_tree, fname); + + transport = _tree->session->transport; + options = transport->options; + + status = smb2_logoff(_tree->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_logoff failed\n"); + TALLOC_FREE(_tree); + + options.max_credits = 8192; + + ret = torture_smb2_connection_ext(tctx, 0, &options, &tree); + torture_assert_goto(tctx, ret == true, ret, done, "torture_smb2_connection_ext failed\n"); + + transport = tree->session->transport; + + cur_credits = smb2cli_conn_get_cur_credits(transport->conn); + if (cur_credits != 8192) { + torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits); + ret = false; + goto done; + } + + ZERO_STRUCT(create); + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.fname = fname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n"); + h = create.out.file.handle; + + /* + * See what happens if we skip a mid. As we want to avoid triggering our + * client side mid window check we keep conn->smb2.cur_credits + * unchanged so the server keeps granting credits until it's max mid + * windows size is reached at which point it will disconnect us: + * + * o Windows 2016 currently has a maximum mid window size of 8192 by + * default + * + * o Samba's limit is 512 + * + * o Windows 2008r2 uses some special algorithm (MS-SMB2 3.3.1.1 + * footnote <167>) that kicks in once a mid is skipped, resulting in a + * maximum window size of 100-300 depending on the number of granted + * credits at the moment of skipping a mid. + */ + + mid = smb2cli_conn_get_mid(tree->session->transport->conn); + smb2cli_conn_set_mid(tree->session->transport->conn, mid + 1); + + for (i = 0; i < 8191; i++) { + status = smb2_util_write(tree, h, "\0", 0, 1); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "Server only allowed %d writes\n", i); + ret = false; + goto done; + } + } + + /* + * Now use the skipped mid (the smb2_util_close...), we should + * immediately get a full mid window of size 8192. + */ + smb2cli_conn_set_mid(tree->session->transport->conn, mid); + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_close failed\n"); + ZERO_STRUCT(h); + + cur_credits = smb2cli_conn_get_cur_credits(transport->conn); + if (cur_credits != 8192) { + torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits); + ret = false; + goto done; + } + + smb2cli_conn_set_mid(tree->session->transport->conn, mid + 8192); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_util_unlink(tree, fname); + TALLOC_FREE(tree); + return ret; +} + +struct torture_suite *torture_smb2_crediting_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "credits"); + + torture_suite_add_1smb2_test(suite, "session_setup_credits_granted", test_session_setup_credits_granted); + torture_suite_add_1smb2_test(suite, "single_req_credits_granted", test_single_req_credits_granted); + torture_suite_add_1smb2_test(suite, "skipped_mid", test_crediting_skipped_mid); + + suite->description = talloc_strdup(suite, "SMB2-CREDITS tests"); + + return suite; +} diff --git a/source4/torture/smb2/delete-on-close.c b/source4/torture/smb2/delete-on-close.c new file mode 100644 index 0000000..0524287 --- /dev/null +++ b/source4/torture/smb2/delete-on-close.c @@ -0,0 +1,762 @@ +/* + Unix SMB/CIFS implementation. + + test delete-on-close in more detail + + Copyright (C) Richard Sharpe, 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" + +#define DNAME "test_dir" +#define FNAME DNAME "\\test_create.dat" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + return false; \ + }} while (0) + +static bool create_dir(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + struct smb2_handle handle; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig; + const char *owner_sid; + uint32_t perms = 0; + + torture_comment(tctx, "Creating Directory for testing: %s\n", DNAME); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = DNAME; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * We create an SD that allows us to do most things but we do not + * get DELETE and DELETE CHILD access! + */ + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_OWNER | + SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_TRAVERSE | SEC_DIR_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA | SEC_FILE_READ_DATA; + + torture_comment(tctx, "Setting permissions on dir to 0x1e01bf\n"); + sd = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + perms, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, handle); + + return true; +} + +static bool set_dir_delete_perms(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create io; + struct smb2_handle handle; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd, *sd_orig; + const char *owner_sid; + uint32_t perms = 0; + + torture_comment(tctx, "Opening Directory for setting new SD: %s\n", DNAME); + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_STD_WRITE_OWNER; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = DNAME; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + handle = io.out.file.handle; + + torture_comment(tctx, "get the original sd\n"); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + /* + * We create an SD that allows us to do most things including + * get DELETE and DELETE CHILD access! + */ + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_OWNER | + SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_TRAVERSE | SEC_DIR_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | + SEC_DIR_DELETE_CHILD | SEC_STD_DELETE | + SEC_FILE_WRITE_DATA | SEC_FILE_READ_DATA; + + torture_comment(tctx, "Setting permissions on dir to 0x%0x\n", perms); + sd = security_descriptor_dacl_create(tctx, + 0, owner_sid, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + perms, + 0, + NULL); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + set.set_secdesc.in.sd = sd; + + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, handle); + + return true; +} + +static bool test_doc_overwrite_if(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (OVERWRITE_IF)\n"); + torture_comment(tctx, "We expect NT_STATUS_OK\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Check it was deleted */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0; + io.in.fname = FNAME; + + torture_comment(tctx, "Testing if the file was deleted when closed\n"); + torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n"); + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return true; +} + +static bool test_doc_overwrite_if_exist(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + /* And set the SEC Descriptor appropriately */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on existing file (OVERWRITE_IF)\n"); + torture_comment(tctx, "We expect NT_STATUS_ACCESS_DENIED\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + /* First, create this file ... */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0x0; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Next, try to open it for Delete On Close */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + status = smb2_util_close(tree, io.out.file.handle); + + return true; +} + +static bool test_doc_create(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (CREATE) \n"); + torture_comment(tctx, "We expect NT_STATUS_OK\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Check it was deleted */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0; + io.in.fname = FNAME; + + torture_comment(tctx, "Testing if the file was deleted when closed\n"); + torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n"); + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return true; +} + +static bool test_doc_create_exist(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (CREATE) \n"); + torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_COLLISION\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + /* First, create the file */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0x0; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Next, try to open it for Delete on Close */ + status = smb2_util_close(tree, io.out.file.handle); + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + status = smb2_util_close(tree, io.out.file.handle); + + return true; +} + +static bool test_doc_create_if(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (OPEN_IF)\n"); + torture_comment(tctx, "We expect NT_STATUS_OK\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Check it was deleted */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0; + io.in.fname = FNAME; + + torture_comment(tctx, "Testing if the file was deleted when closed\n"); + torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n"); + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + return true; +} + +static bool test_doc_create_if_exist(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + uint32_t perms = 0; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "Create file with DeleteOnClose on existing file (OPEN_IF)\n"); + torture_comment(tctx, "We expect NT_STATUS_ACCESS_DENIED\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + /* Create the file first */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0x0; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + + /* Now try to create it for delete on close */ + ZERO_STRUCT(io); + io.in.desired_access = 0x130196; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.in.fname = FNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + status = smb2_util_close(tree, io.out.file.handle); + + return true; +} + +static bool test_doc_find_and_set_doc(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + struct smb2_find find; + NTSTATUS status; + union smb_search_data *d; + union smb_setfileinfo sfinfo; + unsigned int count; + uint32_t perms = 0; + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA | SEC_DIR_LIST; + + /* File should not exist for this first test, so make sure */ + set_dir_delete_perms(tctx, tree); + + smb2_deltree(tree, DNAME); + + create_dir(tctx, tree); + + torture_comment(tctx, "FIND and delete directory\n"); + torture_comment(tctx, "We expect NT_STATUS_OK\n"); + + /* open the directory first */ + ZERO_STRUCT(io); + io.in.desired_access = perms; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.fname = DNAME; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* list directory */ + ZERO_STRUCT(find); + find.in.file.handle = io.out.file.handle; + find.in.pattern = "*"; + find.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + find.in.max_response_size = 0x100; + find.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + /* start enumeration on directory */ + status = smb2_find_level(tree, tree, &find, &count, &d); + CHECK_STATUS(status, NT_STATUS_OK); + + /* set delete-on-close */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.in.file.handle = io.out.file.handle; + status = smb2_setinfo_file(tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* close directory */ + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + return true; +} + +static bool test_doc_read_only(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle dir_handle; + union smb_setfileinfo sfinfo = {{0}}; + struct smb2_create create = {0}; + struct smb2_close close = {0}; + NTSTATUS status, expected_status; + bool ret = true, delete_readonly; + + /* + * Allow testing of the Samba 'delete readonly' option. + */ + delete_readonly = torture_setting_bool(tctx, "delete_readonly", false); + expected_status = delete_readonly ? + NT_STATUS_OK : NT_STATUS_CANNOT_DELETE; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &dir_handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE directory failed\n"); + + create = (struct smb2_create) {0}; + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create.in.file_attributes = FILE_ATTRIBUTE_READONLY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = FNAME; + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, expected_status, ret, + done, "Unexpected status for CREATE " + "of new file.\n"); + + if (delete_readonly) { + close.in.file.handle = create.out.file.handle; + status = smb2_close(tree, &close); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE of READONLY file " + "failed.\n"); + } + + torture_comment(tctx, "Creating file with READ_ONLY attribute.\n"); + + create = (struct smb2_create) {0}; + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + create.in.file_attributes = FILE_ATTRIBUTE_READONLY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = FNAME; + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE of READONLY file failed.\n"); + + close.in.file.handle = create.out.file.handle; + status = smb2_close(tree, &close); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE of READONLY file failed.\n"); + + torture_comment(tctx, "Testing CREATE with DELETE_ON_CLOSE on " + "READ_ONLY attribute file.\n"); + + create = (struct smb2_create) {0}; + create.in.desired_access = SEC_RIGHTS_FILE_READ | SEC_STD_DELETE; + create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create.in.file_attributes = 0; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = FNAME; + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, + expected_status, ret, done, + "CREATE returned unexpected " + "status.\n"); + + torture_comment(tctx, "Testing setting DELETE_ON_CLOSE disposition on " + " file with READONLY attribute.\n"); + + create = (struct smb2_create) {0}; + create.in.desired_access = SEC_RIGHTS_FILE_READ | SEC_STD_DELETE;; + create.in.create_options = 0; + create.in.file_attributes = 0; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = FNAME; + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Opening file failed.\n"); + + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = create.out.file.handle; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal(tctx, status, expected_status, + "Set DELETE_ON_CLOSE disposition " + "returned un expected status.\n"); + + status = smb2_util_close(tree, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE failed\n"); + +done: + smb2_deltree(tree, DNAME); + return ret; +} + +/* + * This is a regression test for + * https://bugzilla.samba.org/show_bug.cgi?id=14427 + * + * It's not really a delete-on-close specific test. + */ +static bool test_doc_bug14427(struct torture_context *tctx, struct smb2_tree *tree1) +{ + struct smb2_tree *tree2 = NULL; + NTSTATUS status; + char fname[256]; + bool ret = false; + bool ok; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "doc_bug14427_%s.dat", + generate_random_str(tctx, 8)); + + ok = torture_smb2_tree_connect(tctx, tree1->session, tctx, &tree2); + torture_assert_goto(tctx, ok, ret, done, + "torture_smb2_tree_connect() failed.\n"); + + status = torture_setup_simple_file(tctx, tree1, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_setup_simple_file() failed on tree1.\n"); + + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink() failed on tree2.\n"); + TALLOC_FREE(tree2); + ret = true; +done: + if (tree2 != NULL) { + TALLOC_FREE(tree2); + smb2_util_unlink(tree1, fname); + } + + TALLOC_FREE(tree1); + return ret; +} + +/* + * Extreme testing of Delete On Close and permissions + */ +struct torture_suite *torture_smb2_doc_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "delete-on-close-perms"); + + torture_suite_add_1smb2_test(suite, "OVERWRITE_IF", test_doc_overwrite_if); + torture_suite_add_1smb2_test(suite, "OVERWRITE_IF Existing", test_doc_overwrite_if_exist); + torture_suite_add_1smb2_test(suite, "CREATE", test_doc_create); + torture_suite_add_1smb2_test(suite, "CREATE Existing", test_doc_create_exist); + torture_suite_add_1smb2_test(suite, "CREATE_IF", test_doc_create_if); + torture_suite_add_1smb2_test(suite, "CREATE_IF Existing", test_doc_create_if_exist); + torture_suite_add_1smb2_test(suite, "FIND_and_set_DOC", test_doc_find_and_set_doc); + torture_suite_add_1smb2_test(suite, "READONLY", test_doc_read_only); + torture_suite_add_1smb2_test(suite, "BUG14427", test_doc_bug14427); + + suite->description = talloc_strdup(suite, "SMB2-Delete-on-Close-Perms tests"); + + return suite; +} diff --git a/source4/torture/smb2/deny.c b/source4/torture/smb2/deny.c new file mode 100644 index 0000000..e42fd56 --- /dev/null +++ b/source4/torture/smb2/deny.c @@ -0,0 +1,526 @@ +/* + Unix SMB/CIFS implementation. + SMB2 torture tester - deny mode scanning functions + Copyright (C) Andrew Tridgell 2001 + Copyright (C) David Mulder 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5}; + +static const char *denystr(int denymode) +{ + const struct { + int v; + const char *name; + } deny_modes[] = { + {NTCREATEX_SHARE_ACCESS_NONE, "NTCREATEX_SHARE_ACCESS_NONE"}, + {NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE, "NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE"}, + {NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, "NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE"}, + {NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, "NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE"}, + {-1, NULL}}; + int i; + for (i=0;deny_modes[i].name;i++) { + if (deny_modes[i].v == denymode) return deny_modes[i].name; + } + return "DENY_XXX"; +} + +static const char *openstr(int mode) +{ + const struct { + int v; + const char *name; + } open_modes[] = { + {SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, "SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA"}, + {SEC_FILE_READ_DATA, "SEC_FILE_READ_DATA"}, + {SEC_FILE_WRITE_DATA, "SEC_FILE_WRITE_DATA"}, + {-1, NULL}}; + int i; + for (i=0;open_modes[i].name;i++) { + if (open_modes[i].v == mode) return open_modes[i].name; + } + return "O_XXX"; +} + +static const char *resultstr(enum deny_result res) +{ + const struct { + enum deny_result res; + const char *name; + } results[] = { + {A_X, "X"}, + {A_0, "-"}, + {A_R, "R"}, + {A_W, "W"}, + {A_RW,"RW"}}; + int i; + for (i=0;idescription = talloc_strdup(suite, "SMB2 deny tests"); + + return suite; +} diff --git a/source4/torture/smb2/dir.c b/source4/torture/smb2/dir.c new file mode 100644 index 0000000..4020e6b --- /dev/null +++ b/source4/torture/smb2/dir.c @@ -0,0 +1,1606 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 dir list test suite + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Zachary Loafman 2009 + Copyright (C) Aravind Srinivasan 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" + +#include "system/filesys.h" +#include "lib/util/tsort.h" + +#define DNAME "smb2_dir" +#define NFILES 100 + +struct file_elem { + char *name; + NTTIME create_time; + bool found; +}; + +static NTSTATUS populate_tree(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct file_elem *files, + int nfiles, + struct smb2_handle *h_out) +{ + struct smb2_create create; + char **strs = NULL; + NTSTATUS status; + bool ret = true; + int i; + + smb2_deltree(tree, DNAME); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = DNAME; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + *h_out = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + strs = generate_unique_strs(mem_ctx, 8, nfiles); + if (strs == NULL) { + status = NT_STATUS_OBJECT_NAME_COLLISION; + goto done; + } + for (i = 0; i < nfiles; i++) { + files[i].name = strs[i]; + create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", + DNAME, files[i].name); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + files[i].create_time = create.out.create_time; + smb2_util_close(tree, create.out.file.handle); + } + done: + if (!ret) { + return status; + } + + return status; +} + +/* + test find continue +*/ + +static bool test_find(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h; + struct smb2_find f; + union smb_search_data *d; + struct file_elem files[NFILES] = {}; + NTSTATUS status; + bool ret = true; + unsigned int count; + int i, j = 0, file_count = 0; + + status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + for (i = 0; i < count; i++) { + bool expected; + const char *found = d[i].both_directory_info.name.s; + NTTIME ct = d[i].both_directory_info.create_time; + + if (!strcmp(found, ".") || !strcmp(found, "..")) + continue; + + expected = false; + for (j = 0; j < NFILES; j++) { + if (strcmp(files[j].name, found) != 0) { + continue; + } + + torture_assert_u64_equal_goto(tctx, + files[j].create_time, + ct, ret, done, + talloc_asprintf(tctx, + "file[%d]\n", j)); + + files[j].found = true; + expected = true; + break; + } + + if (expected) + continue; + + torture_result(tctx, TORTURE_FAIL, + "(%s): didn't expect %s\n", + __location__, found); + ret = false; + goto done; + } + + file_count = file_count + i; + f.in.continue_flags = 0; + f.in.max_response_size = 4096; + } while (count != 0); + + torture_assert_int_equal_goto(tctx, file_count, NFILES + 2, ret, done, + ""); + + for (i = 0; i < NFILES; i++) { + if (files[j].found) + continue; + + torture_result(tctx, TORTURE_FAIL, + "(%s): expected to find %s, but didn't\n", + __location__, files[j].name); + ret = false; + goto done; + } + + done: + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +/* + test fixed enumeration +*/ + +static bool test_fixed(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create create; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_find f; + union smb_search_data *d; + struct file_elem files[NFILES] = {}; + NTSTATUS status; + bool ret = true; + unsigned int count; + int i; + + status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = DNAME; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + h2 = create.out.file.handle; + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + /* Start enumeration on h, then delete all from h2 */ + status = smb2_find_level(tree, tree, &f, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + f.in.file.handle = h2; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + for (i = 0; i < count; i++) { + const char *found = d[i].both_directory_info.name.s; + char *path = talloc_asprintf(mem_ctx, "%s\\%s", + DNAME, found); + + if (!strcmp(found, ".") || !strcmp(found, "..")) + continue; + + status = smb2_util_unlink(tree, path); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + ""); + + talloc_free(path); + } + + f.in.continue_flags = 0; + f.in.max_response_size = 4096; + } while (count != 0); + + /* Now finish h enumeration. */ + f.in.file.handle = h; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + for (i = 0; i < count; i++) { + const char *found = d[i].both_directory_info.name.s; + + if (!strcmp(found, ".") || !strcmp(found, "..")) + continue; + + torture_result(tctx, TORTURE_FAIL, + "(%s): didn't expect %s (count=%u)\n", + __location__, found, count); + ret = false; + goto done; + } + + f.in.continue_flags = 0; + f.in.max_response_size = 4096; + } while (count != 0); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static struct { + const char *name; + uint8_t level; + enum smb_search_data_level data_level; + int name_offset; + int resume_key_offset; + uint32_t capability_mask; + NTSTATUS status; + union smb_search_data data; +} levels[] = { + { + .name = "SMB2_FIND_DIRECTORY_INFO", + .level = SMB2_FIND_DIRECTORY_INFO, + .data_level = RAW_SEARCH_DATA_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + directory_info.file_index), + }, + { + .name = "SMB2_FIND_FULL_DIRECTORY_INFO", + .level = SMB2_FIND_FULL_DIRECTORY_INFO, + .data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + full_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + full_directory_info.file_index), + }, + { + .name = "SMB2_FIND_NAME_INFO", + .level = SMB2_FIND_NAME_INFO, + .data_level = RAW_SEARCH_DATA_NAME_INFO, + .name_offset = offsetof(union smb_search_data, + name_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + name_info.file_index), + }, + { + .name = "SMB2_FIND_BOTH_DIRECTORY_INFO", + .level = SMB2_FIND_BOTH_DIRECTORY_INFO, + .data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + both_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + both_directory_info.file_index), + }, + { + .name = "SMB2_FIND_ID_FULL_DIRECTORY_INFO", + .level = SMB2_FIND_ID_FULL_DIRECTORY_INFO, + .data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + id_full_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + id_full_directory_info.file_index), + }, + { + .name = "SMB2_FIND_ID_BOTH_DIRECTORY_INFO", + .level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + .data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, + .name_offset = offsetof(union smb_search_data, + id_both_directory_info.name.s), + .resume_key_offset = offsetof(union smb_search_data, + id_both_directory_info.file_index), + } +}; + +/* + extract the name from a smb_data structure and level +*/ +static const char *extract_name(union smb_search_data *data, + uint8_t level, + enum smb_search_data_level data_level) +{ + int i; + for (i=0;isname1.field1) != (v.sname2.out.field2)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \ + __location__, \ + #sname1, #field1, (int)s->sname1.field1, \ + #sname2, #field2, (int)v.sname2.out.field2); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != \ + (~1 & nt_time_to_unix(v.sname2.out.field2))) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, \ + timestring(tctx, s->sname1.field1), \ + #sname2, #field2, \ + nt_time_string(tctx, v.sname2.out.field2)); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != v.sname2.out.field2) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, \ + nt_time_string(tctx, s->sname1.field1), \ + #sname2, #field2, \ + nt_time_string(tctx, v.sname2.out.field2)); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || \ + strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s/%s [%s]\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1.s, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, fname)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1.s, fname); \ + ret = false; \ + } \ + }} while (0) + +#define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || \ + strcmp(s->sname1.field1, fname)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s/%s [%s] != %s\n", \ + __location__, \ + #sname1, #field1, s->sname1.field1, fname); \ + ret = false; \ + } \ + }} while (0) + + /* check that all the results are as expected */ + CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, attrib, all_info2, all_info2, attrib); + CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info2, all_info2, attrib); + CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info2, all_info2, attrib); + CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info2, all_info2, attrib); + CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info2, all_info2, attrib); + + CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, write_time, all_info2, all_info2, write_time); + CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info2, all_info2, write_time); + CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info2, all_info2, write_time); + CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info2, all_info2, write_time); + CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info2, all_info2, write_time); + + CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, create_time, all_info2, all_info2, create_time); + CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info2, all_info2, create_time); + CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info2, all_info2, create_time); + CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info2, all_info2, create_time); + CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info2, all_info2, create_time); + + CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, access_time, all_info2, all_info2, access_time); + CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info2, all_info2, access_time); + CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info2, all_info2, access_time); + CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info2, all_info2, access_time); + CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info2, all_info2, access_time); + + CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, change_time, all_info2, all_info2, change_time); + CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info2, all_info2, change_time); + CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info2, all_info2, change_time); + CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info2, all_info2, change_time); + CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info2, all_info2, change_time); + + CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, size, all_info2, all_info2, size); + CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, size, all_info2, all_info2, size); + CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, size, all_info2, all_info2, size); + CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info2, all_info2, size); + CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info2, all_info2, size); + + CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, alloc_size, all_info2, all_info2, alloc_size); + CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info2, all_info2, alloc_size); + CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info2, all_info2, alloc_size); + CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info2, all_info2, alloc_size); + CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info2, all_info2, alloc_size); + + CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info2, all_info2, ea_size); + CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info2, all_info2, ea_size); + CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info2, all_info2, ea_size); + CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info2, all_info2, ea_size); + + CHECK_NAME("SMB2_FIND_DIRECTORY_INFO", directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("SMB2_FIND_NAME_INFO", name_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname, STR_TERMINATE_ASCII); + CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname, STR_TERMINATE_ASCII); + + CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE); + + CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id); + CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id); + +done: + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + + return ret; +} + + +struct multiple_result { + TALLOC_CTX *tctx; + int count; + union smb_search_data *list; +}; + +bool fill_result(void *private_data, + union smb_search_data *file, + int count, + uint8_t level, + enum smb_search_data_level data_level) +{ + int i; + const char *sname; + struct multiple_result *data = (struct multiple_result *)private_data; + + for (i=0; icount++; + data->list = talloc_realloc(data->tctx, + data->list, + union smb_search_data, + data->count); + data->list[data->count-1] = file[i]; + } + return true; +} + +enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART, CONT_REOPEN}; + +static NTSTATUS multiple_smb2_search(struct smb2_tree *tree, + TALLOC_CTX *tctx, + const char *pattern, + uint8_t level, + enum smb_search_data_level data_level, + enum continue_type cont_type, + void *data, + struct smb2_handle *h) +{ + struct smb2_find f; + bool ret = true; + unsigned int count = 0; + union smb_search_data *d; + NTSTATUS status; + struct multiple_result *result = (struct multiple_result *)data; + + ZERO_STRUCT(f); + f.in.file.handle = *h; + f.in.pattern = pattern; + f.in.max_response_size = 1024*1024; + f.in.level = level; + + /* The search should start from the beginning every time */ + f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; + if (cont_type == CONT_REOPEN) { + f.in.continue_flags = SMB2_CONTINUE_FLAG_REOPEN; + } + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + if (!fill_result(result, d, count, level, data_level)) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (count == 0 || result == NULL || result->count == 0) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* + * After the first iteration is complete set the CONTINUE + * FLAGS appropriately + */ + switch (cont_type) { + case CONT_INDEX: + f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX; + switch (data_level) { + case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: + f.in.file_index = + result->list[result->count-1].both_directory_info.file_index; + break; + case RAW_SEARCH_DATA_DIRECTORY_INFO: + f.in.file_index = + result->list[result->count-1].directory_info.file_index; + break; + case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: + f.in.file_index = + result->list[result->count-1].full_directory_info.file_index; + break; + case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: + f.in.file_index = + result->list[result->count-1].id_full_directory_info.file_index; + break; + case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: + f.in.file_index = + result->list[result->count-1].id_both_directory_info.file_index; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + break; + case CONT_SINGLE: + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + break; + case CONT_RESTART: + default: + /* we should prevent staying in the loop + * forever */ + f.in.continue_flags = 0; + break; + } + } while (count != 0); +done: + if (!ret) { + return status; + } + return status; +} + + +static enum smb_search_data_level compare_data_level; +uint8_t level_sort; + +static int search_compare(union smb_search_data *d1, + union smb_search_data *d2) +{ + const char *s1, *s2; + + s1 = extract_name(d1, level_sort, compare_data_level); + s2 = extract_name(d2, level_sort, compare_data_level); + return strcmp_safe(s1, s2); +} + +/* + basic testing of search calls using many files +*/ +static bool test_many_files(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const int num_files = 700; + int i, t; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + struct smb2_create create; + struct smb2_handle h; + struct { + const char *name; + const char *cont_name; + uint8_t level; + enum smb_search_data_level data_level; + enum continue_type cont_type; + } search_types[] = { + {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE}, + {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX}, + {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_REOPEN}, + {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE}, + {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX}, + {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_DIRECTORY_INFO", "REOPEN", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_REOPEN}, + {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE}, + {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX}, + {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_REOPEN}, + {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE}, + {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX}, + {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_REOPEN}, + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE}, + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX}, + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_REOPEN}, + }; + + smb2_deltree(tree, DNAME); + status = torture_smb2_testdir(tree, DNAME, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + torture_comment(tctx, "Testing with %d files\n", num_files); + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + for (i=num_files-1;i>=0;i--) { + fname = talloc_asprintf(mem_ctx, DNAME "\\t%03d-%d.txt", i, i); + create.in.fname = talloc_asprintf(mem_ctx, "%s", fname); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + talloc_free(fname); + } + + for (t=0;tcount;i++) { + if (strcmp(name, + result->list[i].both_directory_info.name.s) == 0) { + break; + } + } + if (i == result->count) { + if (exist) { + torture_result(tctx, TORTURE_FAIL, + "failed: '%s' should exist with attribute %s\n", + name, attrib_string(result->list, attrib)); + return false; + } + return true; + } + + if (!exist) { + torture_result(tctx, TORTURE_FAIL, + "failed: '%s' should NOT exist (has attribute %s)\n", + name, attrib_string(result->list, + result->list[i].both_directory_info.attrib)); + return false; + } + + if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) { + torture_result(tctx, TORTURE_FAIL, + "failed: '%s' should have attribute 0x%x (has 0x%x)\n", + name, attrib, result->list[i].both_directory_info.attrib); + return false; + } + return true; +} + +/* + test what happens when the directory is modified during a search +*/ +static bool test_modify_search(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct multiple_result result; + union smb_setfileinfo sfinfo; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create create; + struct smb2_handle h; + struct smb2_find f; + union smb_search_data *d; + struct file_elem files[703] = {}; + int num_files = ARRAY_SIZE(files)-3; + NTSTATUS status; + bool ret = true; + int i; + unsigned int count; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + torture_comment(tctx, "Creating %d files\n", num_files); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + for (i = num_files-1; i >= 0; i--) { + files[i].name = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i); + create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", + DNAME, files[i].name); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + } + + torture_comment(tctx, "pulling the first two files\n"); + ZERO_STRUCT(result); + result.tctx = talloc_new(tctx); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + if (!fill_result(&result, d, count, f.in.level, + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) { + ret = false; + goto done; + } + } while (result.count < 2); + + torture_comment(tctx, "Changing attributes and deleting\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + files[num_files].name = talloc_asprintf(mem_ctx, "T003-03.txt.2"); + create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME, + files[num_files].name); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + files[num_files + 1].name = talloc_asprintf(mem_ctx, "T013-13.txt.2"); + create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME, + files[num_files + 1].name); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + + files[num_files + 2].name = talloc_asprintf(mem_ctx, "T013-13.txt.3"); + status = smb2_create_complex_file(tctx, tree, DNAME "\\T013-13.txt.3", &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + smb2_util_unlink(tree, DNAME "\\T014-14.txt"); + smb2_util_setatr(tree, DNAME "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN); + smb2_util_setatr(tree, DNAME "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL); + smb2_util_setatr(tree, DNAME "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM); + smb2_util_setatr(tree, DNAME "\\T018-18.txt", 0); + smb2_util_setatr(tree, DNAME "\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN); + smb2_util_setatr(tree, DNAME "\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN); + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.path = DNAME "\\T013-13.txt.3"; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb2_composite_setpathinfo(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + /* Reset the numfiles to include the new files and start the + * search from the beginning */ + num_files = num_files + 2; + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; + result.count = 0; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + if (!fill_result(&result, d, count, f.in.level, + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) { + ret = false; + goto done; + } + f.in.continue_flags = 0; + f.in.max_response_size = 4096; + } while (count != 0); + + + ret &= check_result(tctx, &result, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN); + ret &= check_result(tctx, &result, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN); + ret &= check_result(tctx, &result, "t014-14.txt", false, 0); + ret &= check_result(tctx, &result, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN); + ret &= check_result(tctx, &result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL); + ret &= check_result(tctx, &result, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM); + ret &= check_result(tctx, &result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(tctx, &result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(tctx, &result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE); + ret &= check_result(tctx, &result, "T003-3.txt.2", false, 0); + ret &= check_result(tctx, &result, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL); + + if (!ret) { + for (i=0;i 0) { + torture_comment(tctx, "non-alphabetical order at entry " + "%d '%s' '%s'\n", i, name1, name2); + torture_comment(tctx, + "Server does not produce sorted directory listings" + "(not an error)\n"); + goto done; + } + } + talloc_free(result.list); +done: + smb2_util_close(tree, h); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +/* test the behavior of file_index field in the SMB2_FIND struct */ +static bool test_file_index(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const int num_files = 100; + int resume_index = 4; + int i; + char *fname; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + struct smb2_create create; + struct smb2_find f; + struct smb2_handle h; + union smb_search_data *d; + unsigned count; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + torture_comment(tctx, "Testing the behavior of file_index flag\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + for (i = num_files-1; i >= 0; i--) { + fname = talloc_asprintf(mem_ctx, DNAME "\\file%u.txt", i); + create.in.fname = fname; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + talloc_free(fname); + smb2_util_close(tree, create.out.file.handle); + } + + ZERO_STRUCT(result); + result.tctx = tctx; + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + f.in.max_response_size = 0x1000; + f.in.level = SMB2_FIND_FULL_DIRECTORY_INFO; + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + if (!fill_result(&result, d, count, f.in.level, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) { + ret = false; + goto done; + } + } while(result.count < 10); + + if (result.list[0].full_directory_info.file_index == 0) { + torture_skip_goto(tctx, done, + "Talking to a server that doesn't provide a " + "file index.\nWindows servers using NTFS do " + "not provide a file_index. Skipping test\n"); + } else { + /* We are not talking to a Windows based server. Windows + * servers using NTFS do not provide a file_index. Windows + * servers using FAT do provide a file index, however in both + * cases they do not honor a file index on a resume request. + * See MS-FSCC <62> and MS-SMB2 <54> for more information. */ + + /* Set the file_index flag to point to the fifth file from the + * previous enumeration and try to start the subsequent + * searches from that point */ + f.in.file_index = + result.list[resume_index].full_directory_info.file_index; + f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX; + + /* get the name of the next expected file */ + fname = talloc_asprintf(mem_ctx, DNAME "\\%s", + result.list[resume_index].full_directory_info.name.s); + + ZERO_STRUCT(result); + result.tctx = tctx; + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + goto done; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + if (!fill_result(&result, d, count, f.in.level, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) { + ret = false; + goto done; + } + if (strcmp(fname, + result.list[0].full_directory_info.name.s)) { + torture_comment(tctx, "Next expected file: %s but the " + "server returned %s\n", fname, + result.list[0].full_directory_info.name.s); + torture_comment(tctx, + "Not an error. Resuming using a file " + "index is an optional feature of the " + "protocol.\n"); + goto done; + } + } +done: + smb2_util_close(tree, h); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Tests directory enumeration in a directory containing >1000 files with + * names of varying lengths. + */ +static bool test_large_files(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const int num_files = 2000; + int max_len = 200; + /* These should be evenly divisible */ + int num_at_len = num_files / max_len; + struct file_elem files[2000] = {}; + size_t len = 1; + bool ret = true; + NTSTATUS status; + struct smb2_create create; + struct smb2_find f; + struct smb2_handle h = {{0}}; + union smb_search_data *d; + int i, j = 0, file_count = 0; + char **strs = NULL; + unsigned count; + struct timespec ts1, ts2; + + torture_comment(tctx, + "Testing directory enumeration in a directory with >1000 files\n"); + + smb2_deltree(tree, DNAME); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = DNAME; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + h = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + + for (i = 0; i < num_files; i++) { + if (i % num_at_len == 0) { + strs = generate_unique_strs(mem_ctx, len, num_at_len); + len++; + } + files[i].name = strs[i % num_at_len]; + create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", + DNAME, files[i].name); + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + smb2_util_close(tree, create.out.file.handle); + } + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "*"; + f.in.max_response_size = 0x100; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + clock_gettime_mono(&ts1); + + do { + status = smb2_find_level(tree, tree, &f, &count, &d); + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) + break; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + for (i = 0; i < count; i++) { + bool expected; + const char *found = d[i].both_directory_info.name.s; + + if (!strcmp(found, ".") || !strcmp(found, "..")) + continue; + + expected = false; + for (j = 0; j < 2000; j++) { + if (!strcmp(files[j].name, found)) { + files[j].found = true; + expected = true; + break; + } + } + + if (expected) + continue; + + torture_result(tctx, TORTURE_FAIL, + "(%s): didn't expect %s\n", + __location__, found); + ret = false; + goto done; + } + file_count = file_count + i; + f.in.continue_flags = 0; + f.in.max_response_size = 4096; + } while (count != 0); + + torture_assert_int_equal_goto(tctx, file_count, num_files + 2, ret, + done, ""); + + clock_gettime_mono(&ts2); + + for (i = 0; i < num_files; i++) { + if (files[j].found) + continue; + + torture_result(tctx, TORTURE_FAIL, + "(%s): expected to find %s, but didn't\n", + __location__, files[j].name); + ret = false; + goto done; + } + + torture_comment(tctx, "Directory enumeration completed in %.3f s.\n", + timespec_elapsed2(&ts1, &ts2)); + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +/* + * basic testing of search calls using many files, + * including renaming files + */ +#define NUM_FILES 1000 +static bool test_1k_files_rename(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const int num_files = NUM_FILES; + int rename_index = 0; + int i, j; + char *fname_list[NUM_FILES] = {0}; + bool ret = true; + NTSTATUS status; + struct multiple_result result; + struct smb2_create create; + struct smb2_create dir; + struct smb2_handle dir_handle; + struct smb2_create open; + union smb_setfileinfo sinfo; + struct timespec ts1, ts2; + bool reopen; + + reopen = torture_setting_bool(tctx, "1k_files_rename_reopendir", false); + + torture_comment(tctx, "Testing with %d files\n", num_files); + + smb2_deltree(tree, DNAME); + + dir = (struct smb2_create) { + .in.desired_access = SEC_DIR_LIST, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.fname = DNAME, + }; + + status = smb2_create(tree, tree, &dir); + torture_assert_ntstatus_ok(tctx, status, + "Could not create test directory"); + + dir_handle = dir.out.file.handle; + + /* Create 1k files, store in array for later rename */ + + torture_comment(tctx, "Create files.\n"); + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_ALL, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + }; + + for (i = num_files - 1; i >= 0; i--) { + fname_list[i] = talloc_asprintf(mem_ctx, DNAME "\\t%03d.txt", i); + torture_assert_not_null_goto(tctx, fname_list[i], ret, done, + "talloc_asprintf failed"); + + create.in.fname = fname_list[i]; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + status = smb2_util_close(tree, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + open = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_ALL | SEC_STD_DELETE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + }; + + clock_gettime_mono(&ts1); + + for (j = 0; j < 100; j++) { + ZERO_STRUCT(result); + result.tctx = talloc_new(tctx); + + torture_comment(tctx, "Iteration: %02d of 100.\n", j); + + if (reopen) { + torture_comment( + tctx, "Close and reopen test directory \n"); + smb2_util_close(tree, dir_handle); + + status = smb2_create(tree, tree, &dir); + torture_assert_ntstatus_ok_goto( + tctx, status, ret, done, + "Could not reopen test directory"); + + dir_handle = dir.out.file.handle; + } + + status = multiple_smb2_search(tree, tctx, "*", + SMB2_FIND_FULL_DIRECTORY_INFO, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, + 100, + &result, &dir_handle); + torture_assert_int_equal_goto(tctx, result.count, num_files, + ret, done, "Wrong number of files"); + + /* Check name validity of all files*/ + compare_data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO; + level_sort = SMB2_FIND_FULL_DIRECTORY_INFO; + + TYPESAFE_QSORT(result.list, result.count, search_compare); + + for (i = 0; i < result.count; i++) { + const char *s = NULL; + char *dname_s = NULL; + + s = extract_name(&result.list[i], + SMB2_FIND_FULL_DIRECTORY_INFO, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO); + dname_s = talloc_asprintf(mem_ctx, DNAME "\\%s", s); + torture_assert_not_null_goto(tctx, dname_s, ret, done, + "talloc_asprintf failed"); + + torture_assert_str_equal_goto(tctx, dname_s, fname_list[i], ret, + done, "Incorrect name\n"); + TALLOC_FREE(dname_s); + } + + TALLOC_FREE(result.tctx); + + /* Rename one file */ + open.in.fname = fname_list[rename_index]; + + status = smb2_create(tree, tctx, &open); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed open\n"); + + sinfo = (union smb_setfileinfo) { + .rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION, + .rename_information.in.file.handle = open.out.file.handle, + }; + + TALLOC_FREE(fname_list[rename_index]); + fname_list[rename_index] = talloc_asprintf( + mem_ctx, DNAME "\\t%03d-rename.txt", rename_index); + sinfo.rename_information.in.new_name = fname_list[rename_index]; + + torture_comment(tctx, "Renaming test file to: %s\n", + sinfo.rename_information.in.new_name); + + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed setinfo/rename\n"); + + rename_index++; + smb2_util_close(tree, open.out.file.handle); + } + + clock_gettime_mono(&ts2); + + torture_comment(tctx, "\nDirectory enumeration completed in %.3f s.\n", + timespec_elapsed2(&ts1, &ts2)); + +done: + TALLOC_FREE(mem_ctx); + smb2_util_close(tree, dir_handle); + smb2_deltree(tree, DNAME); + return ret; +} +#undef NUM_FILES + +struct torture_suite *torture_smb2_dir_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "dir"); + + torture_suite_add_1smb2_test(suite, "find", test_find); + torture_suite_add_1smb2_test(suite, "fixed", test_fixed); + torture_suite_add_1smb2_test(suite, "one", test_one_file); + torture_suite_add_1smb2_test(suite, "many", test_many_files); + torture_suite_add_1smb2_test(suite, "modify", test_modify_search); + torture_suite_add_1smb2_test(suite, "sorted", test_sorted); + torture_suite_add_1smb2_test(suite, "file-index", test_file_index); + torture_suite_add_1smb2_test(suite, "large-files", test_large_files); + torture_suite_add_1smb2_test(suite, "1kfiles_rename", test_1k_files_rename); + + suite->description = talloc_strdup(suite, "SMB2-DIR tests"); + + return suite; +} diff --git a/source4/torture/smb2/dosmode.c b/source4/torture/smb2/dosmode.c new file mode 100644 index 0000000..7610a20 --- /dev/null +++ b/source4/torture/smb2/dosmode.c @@ -0,0 +1,254 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 setinfo individual test suite + + Copyright (C) Ralph Boehme 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +/* + test dosmode and hidden files +*/ +bool torture_smb2_dosmode(struct torture_context *tctx) +{ + bool ret = true; + NTSTATUS status; + struct smb2_tree *tree = NULL; + const char *dname = "torture_dosmode"; + const char *fname = "torture_dosmode\\file"; + const char *hidefile = "torture_dosmode\\hidefile"; + const char *dotfile = "torture_dosmode\\.dotfile"; + struct smb2_handle h1 = {{0}}; + struct smb2_create io; + union smb_setfileinfo sfinfo; + union smb_fileinfo finfo2; + + torture_comment(tctx, "Checking dosmode with \"hide files\" " + "and \"hide dot files\"\n"); + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + smb2_deltree(tree, dname); + + status = torture_smb2_testdir(tree, dname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed"); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = 0; + io.in.fname = fname; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN; + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = io.out.file.handle; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_filefailed"); + + ZERO_STRUCT(finfo2); + finfo2.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo2.generic.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tctx, &finfo2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + torture_assert_int_equal_goto(tctx, finfo2.all_info2.out.attrib & FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_HIDDEN, ret, done, + "FILE_ATTRIBUTE_HIDDEN is not set"); + + smb2_util_close(tree, io.out.file.handle); + + /* This must fail with attribute mismatch */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.create_options = 0; + io.in.fname = fname; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done,"smb2_create failed"); + + /* Create a file in "hide files" */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = 0; + io.in.fname = hidefile; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + ZERO_STRUCT(finfo2); + finfo2.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo2.generic.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tctx, &finfo2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + torture_assert_int_equal_goto(tctx, finfo2.all_info2.out.attrib & FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_HIDDEN, ret, done, + "FILE_ATTRIBUTE_HIDDEN is not set"); + + smb2_util_close(tree, io.out.file.handle); + + /* Overwrite a file in "hide files", should pass */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.create_options = 0; + io.in.fname = hidefile; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + smb2_util_close(tree, io.out.file.handle); + + /* Create a "hide dot files" */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = 0; + io.in.fname = dotfile; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + ZERO_STRUCT(finfo2); + finfo2.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo2.generic.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tctx, &finfo2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + torture_assert_int_equal_goto(tctx, finfo2.all_info2.out.attrib & FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_HIDDEN, ret, done, + "FILE_ATTRIBUTE_HIDDEN is not set"); + + smb2_util_close(tree, io.out.file.handle); + + /* Overwrite a "hide dot files", should pass */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.create_options = 0; + io.in.fname = dotfile; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + smb2_util_close(tree, io.out.file.handle); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, dname); + return ret; +} + +bool torture_smb2_async_dosmode(struct torture_context *tctx) +{ + bool ret = true; + NTSTATUS status; + struct smb2_tree *tree = NULL; + const char *dname = "torture_dosmode"; + const char *fname = "torture_dosmode\\file"; + struct smb2_handle h = {{0}}; + struct smb2_create io; + union smb_setfileinfo sfinfo; + struct smb2_find f; + union smb_search_data *d; + unsigned int count; + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + smb2_deltree(tree, dname); + + status = torture_smb2_testdir(tree, dname, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed"); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = 0; + io.in.fname = fname; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN; + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = io.out.file.handle; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_filefailed"); + + smb2_util_close(tree, io.out.file.handle); + + ZERO_STRUCT(f); + f.in.file.handle = h; + f.in.pattern = "file"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; + f.in.max_response_size = 0x1000; + f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + + status = smb2_find_level(tree, tree, &f, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); + + smb2_util_close(tree, h); + ZERO_STRUCT(h); + + torture_assert_goto(tctx, + d->both_directory_info.attrib & FILE_ATTRIBUTE_HIDDEN, + ret, done, + "FILE_ATTRIBUTE_HIDDEN is not set\n"); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_deltree(tree, dname); + return ret; +} diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c new file mode 100644 index 0000000..f56c558 --- /dev/null +++ b/source4/torture/smb2/durable_open.c @@ -0,0 +1,2872 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 durable opens + + Copyright (C) Stefan Metzmacher 2008 + Copyright (C) Michael Adam 2011-2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \ + __location__, #v, (unsigned long long)v, (unsigned long long)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_NOT_VAL(v, incorrect) do { \ + if ((v) == (incorrect)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \ + __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \ + ret = false; \ + }} while (0) + +#define CHECK_NOT_NULL(p) do { \ + if ((p) == NULL) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \ + __location__, #p); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +#define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + if (__alloc_size != 0) { \ + CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \ + } \ + CHECK_VAL((__io)->out.size, (__size)); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + + + +/** + * basic durable_open test. + * durable state should only be granted when requested + * along with a batch oplock or a handle lease. + * + * This test tests durable open with all possible oplock types. + */ + +struct durable_open_vs_oplock { + const char *level; + const char *share_mode; + bool expected; +}; + +#define NUM_OPLOCK_TYPES 4 +#define NUM_SHARE_MODES 8 +#define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES ) +static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] = +{ + { "", "", false }, + { "", "R", false }, + { "", "W", false }, + { "", "D", false }, + { "", "RD", false }, + { "", "RW", false }, + { "", "WD", false }, + { "", "RWD", false }, + + { "s", "", false }, + { "s", "R", false }, + { "s", "W", false }, + { "s", "D", false }, + { "s", "RD", false }, + { "s", "RW", false }, + { "s", "WD", false }, + { "s", "RWD", false }, + + { "x", "", false }, + { "x", "R", false }, + { "x", "W", false }, + { "x", "D", false }, + { "x", "RD", false }, + { "x", "RW", false }, + { "x", "WD", false }, + { "x", "RWD", false }, + + { "b", "", true }, + { "b", "R", true }, + { "b", "W", true }, + { "b", "D", true }, + { "b", "RD", true }, + { "b", "RW", true }, + { "b", "WD", true }, + { "b", "RWD", true }, +}; + +static bool test_one_durable_open_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + struct durable_open_vs_oplock test) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + bool ret = true; + struct smb2_create io; + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(test.share_mode), + smb2_util_oplock_level(test.level)); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, test.expected); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level)); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_durable_open_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + bool ret = true; + int i; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + /* test various oplock levels with durable open */ + + for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) { + ret = test_one_durable_open_open_oplock(tctx, + tree, + fname, + durable_open_vs_oplock_table[i]); + if (ret == false) { + goto done; + } + } + +done: + smb2_util_unlink(tree, fname); + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic durable_open test. + * durable state should only be granted when requested + * along with a batch oplock or a handle lease. + * + * This test tests durable open with all valid lease types. + */ + +struct durable_open_vs_lease { + const char *type; + const char *share_mode; + bool expected; +}; + +#define NUM_LEASE_TYPES 5 +#define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES ) +static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] = +{ + { "", "", false }, + { "", "R", false }, + { "", "W", false }, + { "", "D", false }, + { "", "RW", false }, + { "", "RD", false }, + { "", "WD", false }, + { "", "RWD", false }, + + { "R", "", false }, + { "R", "R", false }, + { "R", "W", false }, + { "R", "D", false }, + { "R", "RW", false }, + { "R", "RD", false }, + { "R", "DW", false }, + { "R", "RWD", false }, + + { "RW", "", false }, + { "RW", "R", false }, + { "RW", "W", false }, + { "RW", "D", false }, + { "RW", "RW", false }, + { "RW", "RD", false }, + { "RW", "WD", false }, + { "RW", "RWD", false }, + + { "RH", "", true }, + { "RH", "R", true }, + { "RH", "W", true }, + { "RH", "D", true }, + { "RH", "RW", true }, + { "RH", "RD", true }, + { "RH", "WD", true }, + { "RH", "RWD", true }, + + { "RHW", "", true }, + { "RHW", "R", true }, + { "RHW", "W", true }, + { "RHW", "D", true }, + { "RHW", "RW", true }, + { "RHW", "RD", true }, + { "RHW", "WD", true }, + { "RHW", "RWD", true }, +}; + +static bool test_one_durable_open_open_lease(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + struct durable_open_vs_lease test) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + bool ret = true; + struct smb2_create io; + struct smb2_lease ls; + uint64_t lease; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + lease = random(); + + smb2_lease_create_share(&io, &ls, false /* dir */, fname, + smb2_util_share_access(test.share_mode), + lease, + smb2_util_lease_state(test.type)); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, test.expected); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state(test.type)); +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_durable_open_open_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + + /* test various oplock levels with durable open */ + + for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) { + ret = test_one_durable_open_open_lease(tctx, + tree, + fname, + durable_open_vs_lease_table[i]); + if (ret == false) { + goto done; + } + } + +done: + smb2_util_unlink(tree, fname); + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a durable open + * and do a durable reopen on the same connection + * while the first open is still active (fails) + */ +static bool test_durable_open_reopen1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io1, io2; + bool ret = true; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io1.out.file.handle; + h = &_h; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + + /* try a durable reconnect while the file is still open */ + ZERO_STRUCT(io2); + io2.in.fname = fname; + io2.in.durable_handle = h; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * Basic test for doing a durable open + * and do a session reconnect while the first + * session is still active and the handle is + * still open in the client. + * This closes the original session and a + * durable reconnect on the new session succeeds. + */ +static bool test_durable_open_reopen1a(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + bool ret = true; + struct smb2_tree *tree2 = NULL; + struct smb2_tree *tree3 = NULL; + uint64_t previous_session_id; + struct smbcli_options options; + struct GUID orig_client_guid; + + options = tree->session->transport->options; + orig_client_guid = options.client_guid; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen1a_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + /* + * a session reconnect on a second tcp connection + */ + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* for oplocks, the client guid can be different: */ + options.client_guid = GUID_random(); + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree2); + torture_assert_goto(tctx, ret, ret, done, "could not reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + + TALLOC_FREE(tree); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + /* + * a session reconnect on a second tcp connection + */ + + previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli); + + /* the original client_guid works just the same */ + options.client_guid = orig_client_guid; + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree3); + torture_assert_goto(tctx, ret, ret, done, "could not reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + + TALLOC_FREE(tree2); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + + status = smb2_create(tree3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (tree == NULL) { + tree = tree2; + } + + if (tree == NULL) { + tree = tree3; + } + + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + h = NULL; + } + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease variant of reopen1a + * + * Basic test for doing a durable open and doing a session + * reconnect while the first session is still active and the + * handle is still open in the client. + * This closes the original session and a durable reconnect on + * the new session succeeds depending on the client guid: + * + * Durable reconnect on a session with a different client guid fails. + * Durable reconnect on a session with the original client guid succeeds. + */ +bool test_durable_open_reopen1a_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smb2_tree *tree2 = NULL; + struct smb2_tree *tree3 = NULL; + uint64_t previous_session_id; + struct smbcli_options options; + struct GUID orig_client_guid; + + options = tree->session->transport->options; + orig_client_guid = options.client_guid; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_create(&io, &ls, false /* dir */, fname, + lease_key, smb2_util_lease_state("RWH")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* + * a session reconnect on a second tcp connection + * with a different client_guid does not allow + * the durable reconnect. + */ + + options.client_guid = GUID_random(); + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree2); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + io.in.lease_request = &ls; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + TALLOC_FREE(tree); + + + /* + * but a durable reconnect on the new session with the wrong + * client guid fails + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + io.in.lease_request = &ls; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * now a session reconnect on a second tcp connection + * with original client_guid allows the durable reconnect. + */ + + options.client_guid = orig_client_guid; + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree3); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + * In this case, a durable reconnect attempt with the + * correct client_guid yields a different error code. + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + io.in.lease_request = &ls; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + TALLOC_FREE(tree2); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + io.in.lease_request = &ls; + status = smb2_create(tree3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); /* no dh response context... */ + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + +done: + if (tree == NULL) { + tree = tree2; + } + + if (tree == NULL) { + tree = tree3; + } + + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + + +/** + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +static bool test_durable_open_reopen2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + bool ret = true; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + /* disconnect, leaving the durable in place */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + /* the path name is ignored by the server */ + io.in.fname = fname; + io.in.durable_handle = h; /* durable v1 reconnect request */ + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + /* disconnect again, leaving the durable in place */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * show that the filename and many other fields + * are ignored. only the reconnect request blob + * is important. + */ + ZERO_STRUCT(io); + /* the path name is ignored by the server */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle = h; /* durable v1 reconnect request */ + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + /* disconnect, leaving the durable in place */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * show that an additionally specified durable v1 request + * is ignored by the server. + * See MS-SMB2, 3.3.5.9.7 + * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context + */ + ZERO_STRUCT(io); + /* the path name is ignored by the server */ + io.in.fname = fname; + io.in.durable_handle = h; /* durable v1 reconnect request */ + io.in.durable_open = true; /* durable v1 handle request */ + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease variant of reopen2 + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +static bool test_durable_open_reopen2_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smbcli_options options; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key, + smb2_util_lease_state("RWH")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + /* disconnect, reconnect and then do durable reopen */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + + /* a few failure tests: */ + + /* + * several attempts without lease attached: + * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND + * irrespective of file name provided + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * attempt with lease provided, but + * with a changed lease key. => fails + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + /* a wrong lease key lets the request fail */ + ls.lease_key.data[0]++; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* restore the correct lease key */ + ls.lease_key.data[0]--; + + /* + * this last failing attempt is almost correct: + * only problem is: we use the wrong filename... + * Note that this gives INVALID_PARAMETER. + * This is different from oplocks! + */ + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Now for a succeeding reconnect: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + + /* disconnect one more time */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * demonstrate that various parameters are ignored + * in the reconnect + */ + + ZERO_STRUCT(io); + /* + * These are completely ignored by the server + */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + + /* + * only these are checked: + * - io.in.fname + * - io.in.durable_handle, + * - io.in.lease_request->lease_key + */ + + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.lease_request = &ls; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + _h = io.out.file.handle; + h = &_h; + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease v2 variant of reopen2 + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smbcli_options options; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_v2_create(&io, &ls, false /* dir */, fname, + lease_key, 0, /* parent lease key */ + smb2_util_lease_state("RWH"), 0 /* lease epoch */); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + + /* disconnect, reconnect and then do durable reopen */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* a few failure tests: */ + + /* + * several attempts without lease attached: + * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND + * irrespective of file name provided + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * attempt with lease provided, but + * with a changed lease key. => fails + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + /* a wrong lease key lets the request fail */ + ls.lease_key.data[0]++; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* restore the correct lease key */ + ls.lease_key.data[0]--; + + /* + * this last failing attempt is almost correct: + * only problem is: we use the wrong filename... + * Note that this gives INVALID_PARAMETER. + * This is different from oplocks! + */ + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Now for a succeeding reconnect: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open = false; + io.in.durable_handle = h; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + + /* disconnect one more time */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * demonstrate that various parameters are ignored + * in the reconnect + */ + + ZERO_STRUCT(io); + /* + * These are completely ignored by the server + */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + + /* + * only these are checked: + * - io.in.fname + * - io.in.durable_handle, + * - io.in.lease_request->lease_key + */ + + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.lease_request_v2 = &ls; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + + _h = io.out.file.handle; + h = &_h; + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a durable open + * tcp disconnect, reconnect with a session reconnect and + * do a durable reopen (succeeds) + */ +static bool test_durable_open_reopen2a(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io1, io2; + uint64_t previous_session_id; + bool ret = true; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io1.out.file.handle; + h = &_h; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + + /* disconnect, reconnect and then do durable reopen */ + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io2); + io2.in.fname = fname; + io2.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + _h = io2.out.file.handle; + h = &_h; + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + + +/** + * basic test for doing a durable open: + * tdis, new tcon, try durable reopen (fails) + */ +static bool test_durable_open_reopen3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io1, io2; + bool ret = true; + struct smb2_tree *tree2; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen3_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io1.out.file.handle; + h = &_h; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + + /* disconnect, reconnect and then do durable reopen */ + status = smb2_tdis(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) { + torture_warning(tctx, "couldn't reconnect to share, bailing\n"); + ret = false; + goto done; + } + + + ZERO_STRUCT(io2); + io2.in.fname = fname; + io2.in.durable_handle = h; + + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree2, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a durable open: + * logoff, create a new session, do a durable reopen (succeeds) + */ +static bool test_durable_open_reopen4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io1, io2; + bool ret = true; + struct smb2_transport *transport; + struct smb2_session *session2; + struct smb2_tree *tree2; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_reopen4_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io1.out.file.handle; + h = &_h; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + + /* + * do a session logoff, establish a new session and tree + * connect on the same transport, and try a durable reopen + */ + transport = tree->session->transport; + status = smb2_logoff(tree->session); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!torture_smb2_session_setup(tctx, transport, + 0, /* previous_session_id */ + mem_ctx, &session2)) + { + torture_warning(tctx, "session setup failed.\n"); + ret = false; + goto done; + } + + /* + * the session setup has talloc-stolen the transport, + * so we can safely free the old tree+session for clarity + */ + TALLOC_FREE(tree); + + if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) { + torture_warning(tctx, "tree connect failed.\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io2); + io2.in.fname = fname; + io2.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + + _h = io2.out.file.handle; + h = &_h; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree2, *h); + } + + smb2_util_unlink(tree2, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_durable_open_delete_on_close1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io1, io2; + bool ret = true; + uint8_t b = 0; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = true; + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io1.out.file.handle; + h = &_h; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + + status = smb2_util_write(tree, *h, &b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, leaving the durable handle in place */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "could not reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * Open the file on the new connection again + * and check that it has been newly created, + * i.e. delete on close was effective on the disconnected handle. + * Also check that the file is really empty, + * the previously written byte gone. + */ + smb2_oplock_create_share(&io2, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io2.out.file.handle; + h = &_h; + CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + + +static bool test_durable_open_delete_on_close2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + bool ret = true; + uint8_t b = 0; + uint64_t previous_session_id; + uint64_t alloc_size_step; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + status = smb2_util_write(tree, *h, &b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* disconnect, leaving the durable handle in place */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "could not reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + alloc_size_step = io.out.alloc_size; + CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + /* close the file, thereby deleting it */ + smb2_util_close(tree, *h); + status = smb2_logoff(tree->session); + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "could not reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * Open the file on the new connection again + * and check that it has been newly created, + * i.e. delete on close was effective on the reconnected handle. + * Also check that the file is really empty, + * the previously written byte gone. + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/* + basic testing of SMB2 durable opens + regarding the position information on the handle +*/ +static bool test_durable_open_file_position(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h; + struct smb2_create io; + NTSTATUS status; + const char *fname = "durable_open_position.dat"; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + bool ret = true; + uint64_t pos; + uint64_t previous_session_id; + struct smbcli_options options; + + options = tree->session->transport->options; + + smb2_util_unlink(tree, fname); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* TODO: check extra blob content */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0); + pos = qfinfo.position_information.out.position; + torture_comment(tctx, "position: %llu\n", + (unsigned long long)pos); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.generic.in.file.handle = h; + sfinfo.position_information.in.position = 0x1000; + status = smb2_setinfo_file(tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0x1000); + pos = qfinfo.position_information.out.position; + torture_comment(tctx, "position: %llu\n", + (unsigned long long)pos); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* tcp disconnect */ + talloc_free(tree); + tree = NULL; + + /* do a session reconnect */ + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = &h; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + CHECK_VAL(io.out.reserved, 0x00); + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 0); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.reserved2, 0); + + h = io.out.file.handle; + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0x1000); + pos = qfinfo.position_information.out.position; + torture_comment(tctx, "position: %llu\n", + (unsigned long long)pos); + + smb2_util_close(tree, h); + + talloc_free(mem_ctx); + + smb2_util_unlink(tree, fname); + +done: + talloc_free(tree); + + return ret; +} + +/* + Open, disconnect, oplock break, reconnect. +*/ +static bool test_durable_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1, io2; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + char fname[256]; + bool ret = true; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree1, fname); + + /* Create with batch oplock */ + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + io1.in.durable_open = true; + + io2 = io1; + io2.in.create_disposition = NTCREATEX_DISP_OPEN; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* Disconnect after getting the batch */ + talloc_free(tree1); + tree1 = NULL; + + /* + * Windows7 (build 7000) will break a batch oplock immediately if the + * original client is gone. (ZML: This seems like a bug. It should give + * some time for the client to reconnect!) + */ + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.durable_open, true); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* What if tree1 tries to come back and reclaim? */ + if (!torture_smb2_connection(tctx, &tree1)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io1); + io1.in.fname = fname; + io1.in.durable_handle = &h1; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + done: + smb2_util_close(tree2, h2); + smb2_util_unlink(tree2, fname); + + talloc_free(tree1); + talloc_free(tree2); + + return ret; +} + +/* + Open, disconnect, lease break, reconnect. +*/ +static bool test_durable_open_lease(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1, io2; + struct smb2_lease ls1, ls2; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + char fname[256]; + bool ret = true; + uint64_t lease1, lease2; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* + * Choose a random name and random lease in case the state is left a + * little funky. + */ + lease1 = random(); + lease2 = random(); + snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree1, fname); + + /* Create with lease */ + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease1, smb2_util_lease_state("RHW")); + io1.in.durable_open = true; + + smb2_lease_create(&io2, &ls2, false /* dir */, fname, + lease2, smb2_util_lease_state("RHW")); + io2.in.durable_open = true; + io2.in.create_disposition = NTCREATEX_DISP_OPEN; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_VAL(io1.out.durable_open, true); + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1); + CHECK_VAL(io1.out.lease_response.lease_state, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + + /* Disconnect after getting the lease */ + talloc_free(tree1); + tree1 = NULL; + + /* + * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?) + * even if the original client is gone. (ZML: This seems like a bug. It + * should give some time for the client to reconnect! And why RH?) + * + * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH. + * Test is adapted accordingly. + */ + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_VAL(io2.out.durable_open, true); + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2); + CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2); + CHECK_VAL(io2.out.lease_response.lease_state, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + + /* What if tree1 tries to come back and reclaim? */ + if (!torture_smb2_connection(tctx, &tree1)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io1); + io1.in.fname = fname; + io1.in.durable_handle = &h1; + io1.in.lease_request = &ls1; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + done: + smb2_util_close(tree2, h2); + smb2_util_unlink(tree2, fname); + + talloc_free(tree1); + talloc_free(tree2); + + return ret; +} + +static bool test_durable_open_lock_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_handle h = {{0}}; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + NTSTATUS status; + char fname[256]; + bool ret = true; + + /* + */ + snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree, fname); + + /* Create with oplock */ + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + ZERO_STRUCT(lck); + ZERO_STRUCT(el); + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Disconnect/Reconnect. */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = &h; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + talloc_free(tree); + + return ret; +} + +/* + Open, take BRL, disconnect, reconnect. +*/ +static bool test_durable_open_lock_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h = {{0}}; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + NTSTATUS status; + char fname[256]; + bool ret = true; + uint64_t lease; + uint32_t caps; + struct smbcli_options options; + + options = tree->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* + * Choose a random name and random lease in case the state is left a + * little funky. + */ + lease = random(); + snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree, fname); + + /* Create with lease */ + + smb2_lease_create(&io, &ls, false /* dir */, fname, lease, + smb2_util_lease_state("RWH")); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease); + CHECK_VAL(io.out.lease_response.lease_state, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + + ZERO_STRUCT(lck); + ZERO_STRUCT(el); + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Disconnect/Reconnect. */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = &h; + io.in.lease_request = &ls; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + talloc_free(tree); + + return ret; +} + +/** + * Open with a RH lease, disconnect, open in another tree, reconnect. + * + * This test actually demonstrates a minimum level of respect for the durable + * open in the face of another open. As long as this test shows an inability to + * reconnect after an open, the oplock/lease tests above will certainly + * demonstrate an error on reconnect. + */ +static bool test_durable_open_open2_lease(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1, io2; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + char fname[256]; + bool ret = true; + uint64_t lease; + uint32_t caps; + struct smbcli_options options; + + options = tree1->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* + * Choose a random name and random lease in case the state is left a + * little funky. + */ + lease = random(); + snprintf(fname, 256, "durable_open_open2_lease_%s.dat", + generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree1, fname); + + /* Create with lease */ + smb2_lease_create_share(&io1, &ls, false /* dir */, fname, + smb2_util_share_access(""), + lease, + smb2_util_lease_state("RH")); + io1.in.durable_open = true; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_VAL(io1.out.durable_open, true); + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease); + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("RH")); + + /* Disconnect */ + talloc_free(tree1); + tree1 = NULL; + + /* Open the file in tree2 */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + + /* Reconnect */ + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io1); + io1.in.fname = fname; + io1.in.durable_handle = &h1; + io1.in.lease_request = &ls; + + /* + * Windows7 (build 7000) will give away an open immediately if the + * original client is gone. (ZML: This seems like a bug. It should give + * some time for the client to reconnect!) + */ + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + h1 = io1.out.file.handle; + + done: + if (tree1 != NULL){ + smb2_util_close(tree1, h1); + smb2_util_unlink(tree1, fname); + talloc_free(tree1); + } + + smb2_util_close(tree2, h2); + smb2_util_unlink(tree2, fname); + talloc_free(tree2); + + return ret; +} + +/** + * Open with a batch oplock, disconnect, open in another tree, reconnect. + * + * This test actually demonstrates a minimum level of respect for the durable + * open in the face of another open. As long as this test shows an inability to + * reconnect after an open, the oplock/lease tests above will certainly + * demonstrate an error on reconnect. + */ +static bool test_durable_open_open2_oplock(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1, io2; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + char fname[256]; + bool ret = true; + + /* + * Choose a random name and random lease in case the state is left a + * little funky. + */ + snprintf(fname, 256, "durable_open_open2_oplock_%s.dat", + generate_random_str(tctx, 8)); + + /* Clean slate */ + smb2_util_unlink(tree1, fname); + + /* Create with batch oplock */ + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + io1.in.durable_open = true; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, true); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* Disconnect */ + talloc_free(tree1); + tree1 = NULL; + + /* Open the file in tree2 */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + /* Reconnect */ + if (!torture_smb2_connection(tctx, &tree1)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io1); + io1.in.fname = fname; + io1.in.durable_handle = &h1; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + h1 = io1.out.file.handle; + + done: + smb2_util_close(tree2, h2); + smb2_util_unlink(tree2, fname); + if (tree1 != NULL) { + smb2_util_close(tree1, h1); + smb2_util_unlink(tree1, fname); + } + + talloc_free(tree1); + talloc_free(tree2); + + return ret; +} + +/** + * test behaviour with initial allocation size + */ +static bool test_durable_open_alloc_size(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + bool ret = true; + uint64_t previous_session_id; + uint64_t alloc_size_step; + uint64_t initial_alloc_size = 0x1000; + const uint8_t *b = NULL; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_alloc_size_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + io.in.alloc_size = initial_alloc_size; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_NOT_VAL(io.out.alloc_size, 0); + alloc_size_step = io.out.alloc_size; + CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, + alloc_size_step, 0); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + /* prepare buffer */ + b = talloc_zero_size(mem_ctx, alloc_size_step); + CHECK_NOT_NULL(b); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* disconnect, reconnect and then do durable reopen */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, + alloc_size_step, 0); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* write one byte */ + status = smb2_util_write(tree, *h, b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, reconnect and then do durable reopen */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, + alloc_size_step, 1); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* write more byte than initial allocation size */ + status = smb2_util_write(tree, *h, b, 1, alloc_size_step); + + /* disconnect, reconnect and then do durable reopen */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, + alloc_size_step * 2, alloc_size_step + 1); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test behaviour when a disconnect happens while creating a read-only file + */ +static bool test_durable_open_read_only(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + bool ret = true; + uint64_t previous_session_id; + const uint8_t b = 0; + uint64_t alloc_size = 0; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_initial_alloc_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + io.in.file_attributes = FILE_ATTRIBUTE_READONLY; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* write one byte */ + status = smb2_util_write(tree, *h, &b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, reconnect and then do durable reopen */ + talloc_free(tree); + tree = NULL; + + if (!torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree)) + { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + alloc_size = io.out.alloc_size; + CHECK_CREATED_SIZE(&io, EXISTED, + FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE, + alloc_size, 1); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + /* write one byte */ + status = smb2_util_write(tree, *h, &b, 1, 1); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + if (h != NULL) { + union smb_setfileinfo sfinfo; + + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.basic_info.in.file.handle = *h; + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + smb2_setinfo_file(tree, &sfinfo); + + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * durable open with oplock, disconnect, exit + */ +static bool test_durable_open_oplock_disconnect(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + NTSTATUS status; + char fname[256]; + bool ret = true; + + snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat", + generate_random_str(mem_ctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH); + io.in.durable_open = true; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + _h = io.out.file.handle; + h = &_h; + + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* disconnect */ + talloc_free(tree); + tree = NULL; + +done: + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + } + talloc_free(mem_ctx); + return ret; +} + +/** + * durable stat open with lease. + */ +static bool test_durable_open_stat_open(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_lease ls; + NTSTATUS status; + char fname[256]; + bool ret = true; + uint64_t lease; + + snprintf(fname, 256, "durable_open_stat_open_%s.dat", + generate_random_str(mem_ctx, 8)); + + /* Ensure file doesn't exist. */ + smb2_util_unlink(tree, fname); + + /* Create a normal file. */ + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + /* Close. */ + smb2_util_close(tree, *h); + h = NULL; + + /* Now try a leased, durable handle stat open. */ + lease = random(); + /* Create with lease */ + smb2_lease_create(&io, + &ls, + false /* dir */, + fname, + lease, + smb2_util_lease_state("RH")); + io.in.durable_open = true; + io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, true); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "durable-open"); + + torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock); + torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease); + torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1); + torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a); + torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease); + torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2); + torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease); + torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2); + torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a); + torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3); + torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4); + torture_suite_add_1smb2_test(suite, "delete_on_close1", + test_durable_open_delete_on_close1); + torture_suite_add_1smb2_test(suite, "delete_on_close2", + test_durable_open_delete_on_close2); + torture_suite_add_1smb2_test(suite, "file-position", + test_durable_open_file_position); + torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock); + torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease); + torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock); + torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease); + torture_suite_add_2smb2_test(suite, "open2-lease", + test_durable_open_open2_lease); + torture_suite_add_2smb2_test(suite, "open2-oplock", + test_durable_open_open2_oplock); + torture_suite_add_1smb2_test(suite, "alloc-size", + test_durable_open_alloc_size); + torture_suite_add_1smb2_test(suite, "read-only", + test_durable_open_read_only); + torture_suite_add_1smb2_test(suite, "stat-open", + test_durable_open_stat_open); + + suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests"); + + return suite; +} + +struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, + "durable-open-disconnect"); + + torture_suite_add_1smb2_test(suite, "open-oplock-disconnect", + test_durable_open_oplock_disconnect); + + suite->description = talloc_strdup(suite, + "SMB2-DURABLE-OPEN-DISCONNECT tests"); + + return suite; +} diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c new file mode 100644 index 0000000..9b9af11 --- /dev/null +++ b/source4/torture/smb2/durable_v2_open.c @@ -0,0 +1,2371 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 version two of durable opens + + Copyright (C) Michael Adam 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "librpc/ndr/libndr.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +static struct { + int count; + struct smb2_close cl; +} break_info; + +static void torture_oplock_close_callback(struct smb2_request *req) +{ + smb2_close_recv(req, &break_info.cl); +} + +/* A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. */ +static bool torture_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.count++; + + ZERO_STRUCT(break_info.cl); + break_info.cl.in.file.handle = *handle; + + req = smb2_close_send(tree, &break_info.cl); + req->async.fn = torture_oplock_close_callback; + req->async.private_data = NULL; + return true; +} + +/** + * testing various create blob combinations. + */ +bool test_durable_v2_open_create_blob(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + + /* disconnect */ + TALLOC_FREE(tree); + + /* create a new session (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * check invalid combinations of durable handle + * request and reconnect blobs + * See MS-SMB2: 3.3.5.9.12 + * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ + io.in.durable_open = true; /* durable v1 handle request */ + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; /* durable v1 reconnect request */ + io.in.durable_open_v2 = true; /* durable v2 handle request */ + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; /* durable v1 reconnect request */ + io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ + io.in.durable_open_v2 = true; /* durable v2 handle request */ + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + + +/** + * basic durable_open test. + * durable state should only be granted when requested + * along with a batch oplock or a handle lease. + * + * This test tests durable open with all possible oplock types. + */ + +struct durable_open_vs_oplock { + const char *level; + const char *share_mode; + bool durable; + bool persistent; +}; + +#define NUM_OPLOCK_TYPES 4 +#define NUM_SHARE_MODES 8 +#define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES ) +static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] = +{ + { "", "", false, false }, + { "", "R", false, false }, + { "", "W", false, false }, + { "", "D", false, false }, + { "", "RD", false, false }, + { "", "RW", false, false }, + { "", "WD", false, false }, + { "", "RWD", false, false }, + + { "s", "", false, false }, + { "s", "R", false, false }, + { "s", "W", false, false }, + { "s", "D", false, false }, + { "s", "RD", false, false }, + { "s", "RW", false, false }, + { "s", "WD", false, false }, + { "s", "RWD", false, false }, + + { "x", "", false, false }, + { "x", "R", false, false }, + { "x", "W", false, false }, + { "x", "D", false, false }, + { "x", "RD", false, false }, + { "x", "RW", false, false }, + { "x", "WD", false, false }, + { "x", "RWD", false, false }, + + { "b", "", true, false }, + { "b", "R", true, false }, + { "b", "W", true, false }, + { "b", "D", true, false }, + { "b", "RD", true, false }, + { "b", "RW", true, false }, + { "b", "WD", true, false }, + { "b", "RWD", true, false }, +}; + +static bool test_one_durable_v2_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + bool request_persistent, + struct durable_open_vs_oplock test) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + bool ret = true; + struct smb2_create io; + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(test.share_mode), + smb2_util_oplock_level(test.level)); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = request_persistent; + io.in.create_guid = GUID_random(); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, test.durable); + CHECK_VAL(io.out.persistent_open, test.persistent); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level)); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_durable_v2_open_oplock_table(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + bool request_persistent, + struct durable_open_vs_oplock *table, + uint8_t num_tests) +{ + bool ret = true; + uint8_t i; + + smb2_util_unlink(tree, fname); + + for (i = 0; i < num_tests; i++) { + ret = test_one_durable_v2_open_oplock(tctx, + tree, + fname, + request_persistent, + table[i]); + if (ret == false) { + goto done; + } + } + +done: + smb2_util_unlink(tree, fname); + + return ret; +} + +bool test_durable_v2_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret; + char fname[256]; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_oplock_%s.dat", + generate_random_str(tctx, 8)); + + ret = test_durable_v2_open_oplock_table(tctx, tree, fname, + false, /* request_persistent */ + durable_open_vs_oplock_table, + NUM_OPLOCK_OPEN_TESTS); + + talloc_free(tree); + + return ret; +} + +/** + * basic durable handle open test. + * persistent state should only be granted when requested + * along with a batch oplock or a handle lease. + * + * This test tests persistent open with all valid lease types. + */ + +struct durable_open_vs_lease { + const char *type; + const char *share_mode; + bool durable; + bool persistent; +}; + +#define NUM_LEASE_TYPES 5 +#define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES ) +static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] = +{ + { "", "", false, false }, + { "", "R", false, false }, + { "", "W", false, false }, + { "", "D", false, false }, + { "", "RW", false, false }, + { "", "RD", false, false }, + { "", "WD", false, false }, + { "", "RWD", false, false }, + + { "R", "", false, false }, + { "R", "R", false, false }, + { "R", "W", false, false }, + { "R", "D", false, false }, + { "R", "RW", false, false }, + { "R", "RD", false, false }, + { "R", "DW", false, false }, + { "R", "RWD", false, false }, + + { "RW", "", false, false }, + { "RW", "R", false, false }, + { "RW", "W", false, false }, + { "RW", "D", false, false }, + { "RW", "RW", false, false }, + { "RW", "RD", false, false }, + { "RW", "WD", false, false }, + { "RW", "RWD", false, false }, + + { "RH", "", true, false }, + { "RH", "R", true, false }, + { "RH", "W", true, false }, + { "RH", "D", true, false }, + { "RH", "RW", true, false }, + { "RH", "RD", true, false }, + { "RH", "WD", true, false }, + { "RH", "RWD", true, false }, + + { "RHW", "", true, false }, + { "RHW", "R", true, false }, + { "RHW", "W", true, false }, + { "RHW", "D", true, false }, + { "RHW", "RW", true, false }, + { "RHW", "RD", true, false }, + { "RHW", "WD", true, false }, + { "RHW", "RWD", true, false }, +}; + +static bool test_one_durable_v2_open_lease(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + bool request_persistent, + struct durable_open_vs_lease test) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + bool ret = true; + struct smb2_create io; + struct smb2_lease ls; + uint64_t lease; + + smb2_util_unlink(tree, fname); + + lease = random(); + + smb2_lease_create_share(&io, &ls, false /* dir */, fname, + smb2_util_share_access(test.share_mode), + lease, + smb2_util_lease_state(test.type)); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = request_persistent; + io.in.create_guid = GUID_random(); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, test.durable); + CHECK_VAL(io.out.persistent_open, test.persistent); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state(test.type)); +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_durable_v2_open_lease_table(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + bool request_persistent, + struct durable_open_vs_lease *table, + uint8_t num_tests) +{ + bool ret = true; + uint8_t i; + + smb2_util_unlink(tree, fname); + + for (i = 0; i < num_tests; i++) { + ret = test_one_durable_v2_open_lease(tctx, + tree, + fname, + request_persistent, + table[i]); + if (ret == false) { + goto done; + } + } + +done: + smb2_util_unlink(tree, fname); + + return ret; +} + +bool test_durable_v2_open_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + char fname[256]; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8)); + + ret = test_durable_v2_open_lease_table(tctx, tree, fname, + false, /* request_persistent */ + durable_open_vs_lease_table, + NUM_LEASE_OPEN_TESTS); + + talloc_free(tree); + return ret; +} + +/** + * basic test for doing a durable open + * and do a durable reopen on the same connection + * while the first open is still active (fails) + */ +bool test_durable_v2_open_reopen1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + + /* try a durable reconnect while the file is still open */ + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * Basic test for doing a durable open + * and do a session reconnect while the first + * session is still active and the handle is + * still open in the client. + * This closes the original session and a + * durable reconnect on the new session succeeds. + */ +bool test_durable_v2_open_reopen1a(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + struct smb2_tree *tree2 = NULL; + struct smb2_tree *tree3 = NULL; + uint64_t previous_session_id; + struct smbcli_options options; + struct GUID orig_client_guid; + + options = tree->session->transport->options; + orig_client_guid = options.client_guid; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + + /* + * a session reconnect on a second tcp connection + */ + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* for oplocks, the client guid can be different: */ + options.client_guid = GUID_random(); + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree2); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + + TALLOC_FREE(tree); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 0); + _h = io.out.file.handle; + h = &_h; + + /* + * a session reconnect on a second tcp connection + */ + + previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli); + + /* it works the same with the original guid */ + options.client_guid = orig_client_guid; + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree3); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + TALLOC_FREE(tree2); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + status = smb2_create(tree3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 0); + _h = io.out.file.handle; + h = &_h; + +done: + if (tree == NULL) { + tree = tree2; + } + + if (tree == NULL) { + tree = tree3; + } + + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease variant of reopen1a + * + * Basic test for doing a durable open and doing a session + * reconnect while the first session is still active and the + * handle is still open in the client. + * This closes the original session and a durable reconnect on + * the new session succeeds depending on the client guid: + * + * Durable reconnect on a session with a different client guid fails. + * Durable reconnect on a session with the original client guid succeeds. + */ +bool test_durable_v2_open_reopen1a_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smb2_tree *tree2 = NULL; + struct smb2_tree *tree3 = NULL; + uint64_t previous_session_id; + struct smbcli_options options; + struct GUID orig_client_guid; + + options = tree->session->transport->options; + orig_client_guid = options.client_guid; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_create(&io, &ls, false /* dir */, fname, + lease_key, smb2_util_lease_state("RWH")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* + * a session reconnect on a second tcp connection + * with a different client_guid does not allow + * the durable reconnect. + */ + + options.client_guid = GUID_random(); + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree2); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + TALLOC_FREE(tree); + + /* + * but a durable reconnect on the new session with the wrong + * client guid fails + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + + /* + * now a session reconnect on a second tcp connection + * with original client_guid allows the durable reconnect. + */ + + options.client_guid = orig_client_guid; + //options.client_guid = GUID_random(); + + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree3); + torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect"); + + /* + * check that this has deleted the old session + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + status = smb2_create(tree2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + TALLOC_FREE(tree2); + + /* + * but a durable reconnect on the new session succeeds: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + status = smb2_create(tree3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 0); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + +done: + if (tree == NULL) { + tree = tree2; + } + + if (tree == NULL) { + tree = tree3; + } + + if (tree != NULL) { + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + } + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +bool test_durable_v2_open_reopen2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + struct GUID create_guid_invalid = GUID_random(); + bool ret = true; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * first a few failure cases + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* a non-zero but non-matching create_guid does not change it: */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid_invalid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * now success: + * The important difference is that the create_guid is provided. + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + + /* disconnect one more time */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + /* These are completely ignored by the server */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + io.in.fname = "__non_existing_fname__"; + + /* + * only io.in.durable_handle_v2 and + * io.in.create_guid are checked + */ + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * durable reconnect test: + * connect with v2, reconnect with v1 + */ +bool test_durable_v2_open_reopen2b(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; /* durable v2 reconnect */ + io.in.create_guid = GUID_zero(); /* but zero create GUID */ + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle = h; /* durable v1 (!) reconnect */ + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} +/** + * durable reconnect test: + * connect with v1, reconnect with v2 : fails (no create_guid...) + */ +bool test_durable_v2_open_reopen2c(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + struct smbcli_options options; + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = true; + io.in.durable_open_v2 = false; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, true); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 0); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; /* durable v2 reconnect */ + io.in.create_guid = create_guid; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease variant of reopen2 + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smbcli_options options; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + options = tree->session->transport->options; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = generate_random_u64(); + smb2_lease_create(&io, &ls, false /* dir */, fname, + lease_key, smb2_util_lease_state("RWH")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + /* disconnect, reconnect and then do durable reopen */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* a few failure tests: */ + + /* + * several attempts without lease attached: + * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND + * irrespective of file name provided + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * attempt with lease provided, but + * with a changed lease key. => fails + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + /* a wrong lease key lets the request fail */ + ls.lease_key.data[0]++; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* restore the correct lease key */ + ls.lease_key.data[0]--; + + /* + * this last failing attempt is almost correct: + * only problem is: we use the wrong filename... + * Note that this gives INVALID_PARAMETER. + * This is different from oplocks! + */ + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Now for a succeeding reconnect: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + + /* disconnect one more time */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * demonstrate that various parameters are ignored + * in the reconnect + */ + + ZERO_STRUCT(io); + /* + * These are completely ignored by the server + */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + + /* + * only these are checked: + * - io.in.fname + * - io.in.durable_handle_v2, + * - io.in.create_guid + * - io.in.lease_request->lease_key + */ + + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request = &ls; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response.lease_flags, 0); + CHECK_VAL(io.out.lease_response.lease_duration, 0); + + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * lease_v2 variant of reopen2 + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smbcli_options options; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + options = tree->session->transport->options; + + smb2_deltree(tree, __func__); + status = torture_smb2_testdir(tree, __func__, &_h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, _h); + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "%s\\durable_v2_open_reopen2_%s.dat", + __func__, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_v2_create(&io, &ls, false /* dir */, fname, + lease_key, 0, /* parent lease key */ + smb2_util_lease_state("RWH"), 0 /* lease epoch */); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + + /* disconnect, reconnect and then do durable reopen */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* a few failure tests: */ + + /* + * several attempts without lease attached: + * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND + * irrespective of file name provided + */ + + ZERO_STRUCT(io); + io.in.fname = ""; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_handle_v2 = h; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * attempt with lease provided, but + * with a changed lease key. => fails + */ + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + /* a wrong lease key lets the request fail */ + ls.lease_key.data[0]++; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* restore the correct lease key */ + ls.lease_key.data[0]--; + + + /* + * this last failing attempt is almost correct: + * only problem is: we use the wrong filename... + * Note that this gives INVALID_PARAMETER. + * This is different from oplocks! + */ + ZERO_STRUCT(io); + io.in.fname = "__non_existing_fname__"; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* + * Now for a succeeding reconnect: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + + /* disconnect one more time */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * demonstrate that various parameters are ignored + * in the reconnect + */ + + ZERO_STRUCT(io); + /* + * These are completely ignored by the server + */ + io.in.security_flags = 0x78; + io.in.oplock_level = 0x78; + io.in.impersonation_level = 0x12345678; + io.in.create_flags = 0x12345678; + io.in.reserved = 0x12345678; + io.in.desired_access = 0x12345678; + io.in.file_attributes = 0x12345678; + io.in.share_access = 0x12345678; + io.in.create_disposition = 0x12345678; + io.in.create_options = 0x12345678; + io.in.fname = "__non_existing_fname__"; + + /* + * only these are checked: + * - io.in.fname + * - io.in.durable_handle_v2, + * - io.in.create_guid + * - io.in.lease_request_v2->lease_key + */ + + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request_v2 = &ls; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, __func__); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test durable request / reconnect with AppInstanceId + */ +bool test_durable_v2_open_app_instance(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1, _h2; + struct smb2_handle *h1 = NULL, *h2 = NULL; + struct smb2_create io1, io2; + bool ret = true; + struct GUID create_guid_1 = GUID_random(); + struct GUID create_guid_2 = GUID_random(); + struct GUID app_instance_id = GUID_random(); + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid_1; + io1.in.app_instance_id = &app_instance_id; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.persistent_open, false); + CHECK_VAL(io1.out.timeout, 300*1000); + + /* + * try to open the file as durable from a second tree with + * a different create guid but the same app_instance_id + * while the first handle is still open. + */ + + smb2_oplock_create_share(&io2, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = create_guid_2; + io2.in.app_instance_id = &app_instance_id; + io2.in.timeout = UINT32_MAX; + + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = io2.out.file.handle; + h2 = &_h2; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(io2.out.durable_open_v2, true); + CHECK_VAL(io2.out.persistent_open, false); + CHECK_VAL(io2.out.timeout, 300*1000); + + CHECK_VAL(break_info.count, 0); + + status = smb2_util_close(tree1, *h1); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + h1 = NULL; + +done: + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree2, *h2); + } + + smb2_util_unlink(tree2, fname); + + talloc_free(tree1); + talloc_free(tree2); + + talloc_free(mem_ctx); + + return ret; +} + + +/** + * basic persistent open test. + * + * This test tests durable open with all possible oplock types. + */ + +struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] = +{ + { "", "", true, true }, + { "", "R", true, true }, + { "", "W", true, true }, + { "", "D", true, true }, + { "", "RD", true, true }, + { "", "RW", true, true }, + { "", "WD", true, true }, + { "", "RWD", true, true }, + + { "s", "", true, true }, + { "s", "R", true, true }, + { "s", "W", true, true }, + { "s", "D", true, true }, + { "s", "RD", true, true }, + { "s", "RW", true, true }, + { "s", "WD", true, true }, + { "s", "RWD", true, true }, + + { "x", "", true, true }, + { "x", "R", true, true }, + { "x", "W", true, true }, + { "x", "D", true, true }, + { "x", "RD", true, true }, + { "x", "RW", true, true }, + { "x", "WD", true, true }, + { "x", "RWD", true, true }, + + { "b", "", true, true }, + { "b", "R", true, true }, + { "b", "W", true, true }, + { "b", "D", true, true }, + { "b", "RD", true, true }, + { "b", "RW", true, true }, + { "b", "WD", true, true }, + { "b", "RWD", true, true }, +}; + +bool test_persistent_open_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + char fname[256]; + bool ret = true; + uint32_t share_capabilities; + bool share_is_ca = false; + struct durable_open_vs_oplock *table; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8)); + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; + + if (share_is_ca) { + table = persistent_open_oplock_ca_table; + } else { + table = durable_open_vs_oplock_table; + } + + ret = test_durable_v2_open_oplock_table(tctx, tree, fname, + true, /* request_persistent */ + table, + NUM_OPLOCK_OPEN_TESTS); + + talloc_free(tree); + + return ret; +} + +/** + * basic persistent handle open test. + * persistent state should only be granted when requested + * along with a batch oplock or a handle lease. + * + * This test tests persistent open with all valid lease types. + */ + +struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] = +{ + { "", "", true, true }, + { "", "R", true, true }, + { "", "W", true, true }, + { "", "D", true, true }, + { "", "RW", true, true }, + { "", "RD", true, true }, + { "", "WD", true, true }, + { "", "RWD", true, true }, + + { "R", "", true, true }, + { "R", "R", true, true }, + { "R", "W", true, true }, + { "R", "D", true, true }, + { "R", "RW", true, true }, + { "R", "RD", true, true }, + { "R", "DW", true, true }, + { "R", "RWD", true, true }, + + { "RW", "", true, true }, + { "RW", "R", true, true }, + { "RW", "W", true, true }, + { "RW", "D", true, true }, + { "RW", "RW", true, true }, + { "RW", "RD", true, true }, + { "RW", "WD", true, true }, + { "RW", "RWD", true, true }, + + { "RH", "", true, true }, + { "RH", "R", true, true }, + { "RH", "W", true, true }, + { "RH", "D", true, true }, + { "RH", "RW", true, true }, + { "RH", "RD", true, true }, + { "RH", "WD", true, true }, + { "RH", "RWD", true, true }, + + { "RHW", "", true, true }, + { "RHW", "R", true, true }, + { "RHW", "W", true, true }, + { "RHW", "D", true, true }, + { "RHW", "RW", true, true }, + { "RHW", "RD", true, true }, + { "RHW", "WD", true, true }, + { "RHW", "RWD", true, true }, +}; + +bool test_persistent_open_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + char fname[256]; + bool ret = true; + uint32_t caps; + uint32_t share_capabilities; + bool share_is_ca; + struct durable_open_vs_lease *table; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8)); + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; + + if (share_is_ca) { + table = persistent_open_lease_ca_table; + } else { + table = durable_open_vs_lease_table; + } + + ret = test_durable_v2_open_lease_table(tctx, tree, fname, + true, /* request_persistent */ + table, + NUM_LEASE_OPEN_TESTS); + + talloc_free(tree); + + return ret; +} + +/** + * setfileinfo test for doing a durable open + * create the file with lease and durable handle, + * write to it (via set end-of-file), tcp disconnect, + * reconnect, do a durable reopen - should succeed. + * + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15022 + */ +bool test_durable_v2_setinfo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + union smb_setfileinfo si; + struct GUID create_guid = GUID_random(); + struct smb2_lease ls; + uint64_t lease_key; + bool ret = true; + struct smbcli_options options; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + options = tree->session->transport->options; + + smb2_deltree(tree, __func__); + status = torture_smb2_testdir(tree, __func__, &_h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, _h); + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "%s\\durable_v2_setinfo%s.dat", + __func__, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + lease_key = random(); + smb2_lease_v2_create(&io, &ls, false /* dir */, fname, + lease_key, 0, /* parent lease key */ + smb2_util_lease_state("RWH"), 0 /* lease epoch */); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.timeout, 300*1000); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + + /* + * Set EOF to 0x100000. + * Mimics an Apple client test, but most importantly + * causes the mtime timestamp on disk to be updated. + */ + ZERO_STRUCT(si); + si.generic.level = SMB_SFILEINFO_END_OF_FILE_INFORMATION; + si.generic.in.file.handle = io.out.file.handle; + si.end_of_file_info.in.size = 0x100000; + status = smb2_setinfo_file(tree, &si); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, reconnect and then do durable reopen */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* + * Now for a succeeding reconnect: + */ + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = h; + io.in.create_guid = create_guid; + io.in.lease_request_v2 = &ls; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + + /* the requested lease state is irrelevant */ + ls.lease_state = smb2_util_lease_state(""); + + h = NULL; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 0x100000); \ + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ + CHECK_VAL(io.out.persistent_open, false); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); + CHECK_VAL(io.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); + CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); + _h = io.out.file.handle; + h = &_h; + +done: + + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, __func__); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "durable-v2-open"); + + torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob); + torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock); + torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease); + torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1); + torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a); + torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_v2_open_reopen1a_lease); + torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2); + torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b); + torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c); + torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease); + torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2); + torture_suite_add_1smb2_test(suite, "durable-v2-setinfo", test_durable_v2_setinfo); + torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance); + torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock); + torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease); + + suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests"); + + return suite; +} + +/** + * basic test for doing a durable open + * tcp disconnect, reconnect, do a durable reopen (succeeds) + */ +static bool test_durable_v2_reconnect_delay(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + struct smbcli_options options; + uint64_t previous_session_id; + uint8_t b = 0; + bool ret = true; + bool ok; + + options = tree->session->transport->options; + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, + sizeof(fname), + "durable_v2_reconnect_delay_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = 0; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + _h = io.out.file.handle; + h = &_h; + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + + status = smb2_util_write(tree, *h, &b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + h = NULL; + + ok = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree); + torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n"); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = &_h; + io.in.create_guid = create_guid; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + TALLOC_FREE(tree); + + smb2_util_unlink(tree2, fname); + + TALLOC_FREE(tree2); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a durable open with 1msec cleanup time + * tcp disconnect, wait a bit, reconnect, do a durable reopen (fails) + */ +static bool test_durable_v2_reconnect_delay_msec(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct smb2_lease ls; + struct GUID create_guid = GUID_random(); + struct smbcli_options options; + uint64_t previous_session_id; + uint8_t b = 0; + bool ret = true; + bool ok; + + options = tree->session->transport->options; + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, + sizeof(fname), + "durable_v2_reconnect_delay_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_lease_create( + &io, + &ls, + false /* dir */, + fname, + generate_random_u64(), + smb2_util_lease_state("RWH")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = 1; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + _h = io.out.file.handle; + h = &_h; + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io.out.durable_open_v2, true); + + status = smb2_util_write(tree, *h, &b, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + h = NULL; + + ok = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree); + torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n"); + + sleep(10); + + ZERO_STRUCT(io); + io.in.fname = fname; + io.in.durable_open_v2 = false; + io.in.durable_handle_v2 = &_h; + io.in.create_guid = create_guid; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + _h = io.out.file.handle; + h = &_h; + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + TALLOC_FREE(tree); + + smb2_util_unlink(tree2, fname); + + TALLOC_FREE(tree2); + + talloc_free(mem_ctx); + + return ret; +} + +struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "durable-v2-delay"); + + torture_suite_add_2smb2_test(suite, + "durable_v2_reconnect_delay", + test_durable_v2_reconnect_delay); + torture_suite_add_2smb2_test(suite, + "durable_v2_reconnect_delay_msec", + test_durable_v2_reconnect_delay_msec); + + return suite; +} diff --git a/source4/torture/smb2/ea.c b/source4/torture/smb2/ea.c new file mode 100644 index 0000000..987d90d --- /dev/null +++ b/source4/torture/smb2/ea.c @@ -0,0 +1,152 @@ +/* + Unix SMB/CIFS implementation. + SMB2 EA tests + + Copyright (C) Ralph Boehme 2022 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "ntstatus_gen.h" +#include "system/time.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#define BASEDIR "test_ea" + +static bool find_returned_ea(union smb_fileinfo *finfo2, + const char *eaname) +{ + unsigned int i; + unsigned int num_eas = finfo2->all_eas.out.num_eas; + struct ea_struct *eas = finfo2->all_eas.out.eas; + + for (i = 0; i < num_eas; i++) { + if (eas[i].name.s == NULL) { + continue; + } + /* Windows capitalizes returned EA names. */ + if (strequal(eas[i].name.s, eaname)) { + return true; + } + } + return false; +} + +static bool torture_smb2_acl_xattr(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_acl_xattr"; + const char *xattr_name = NULL; + struct smb2_handle h1; + struct ea_struct ea; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + NTSTATUS status; + bool ret = true; + + torture_comment(tctx, "Verify NTACL xattr can't be accessed\n"); + + xattr_name = torture_setting_string(tctx, "acl_xattr_name", NULL); + torture_assert_not_null(tctx, xattr_name, "Missing acl_xattr_name option\n"); + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + /* + * 1. Set an EA, so we have something to list + */ + ZERO_STRUCT(ea); + ea.name.s = "void"; + ea.name.private_length = strlen("void") + 1; + ea.value = data_blob_string_const("testme"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_FULL_EA_INFORMATION; + sfinfo.generic.in.file.handle = h1; + sfinfo.full_ea_information.in.eas.num_eas = 1; + sfinfo.full_ea_information.in.eas.eas = &ea; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Setting EA should fail\n"); + + /* + * 2. Verify NT ACL EA is not listed + */ + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_SMB2_ALL_EAS; + finfo.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + + if (find_returned_ea(&finfo, xattr_name)) { + torture_result(tctx, TORTURE_FAIL, + "%s: NTACL EA leaked\n", + __location__); + ret = false; + goto done; + } + + /* + * 3. Try to set EA, should fail + */ + ZERO_STRUCT(ea); + ea.name.s = xattr_name; + ea.name.private_length = strlen(xattr_name) + 1; + ea.value = data_blob_string_const("testme"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_FULL_EA_INFORMATION; + sfinfo.generic.in.file.handle = h1; + sfinfo.full_ea_information.in.eas.num_eas = 1; + sfinfo.full_ea_information.in.eas.eas = &ea; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_ACCESS_DENIED, + ret, done, "Setting EA should fail\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + + smb2_deltree(tree, BASEDIR); + + return ret; +} + +struct torture_suite *torture_smb2_ea(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ea"); + suite->description = talloc_strdup(suite, "SMB2-EA tests"); + + torture_suite_add_1smb2_test(suite, "acl_xattr", torture_smb2_acl_xattr); + + return suite; +} diff --git a/source4/torture/smb2/getinfo.c b/source4/torture/smb2/getinfo.c new file mode 100644 index 0000000..539090a --- /dev/null +++ b/source4/torture/smb2/getinfo.c @@ -0,0 +1,951 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 getinfo test suite + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" + +static struct { + const char *name; + uint16_t level; + NTSTATUS fstatus; + NTSTATUS dstatus; + union smb_fileinfo finfo; + union smb_fileinfo dinfo; +} file_levels[] = { +#define LEVEL(x) .name = #x, .level = x + { LEVEL(RAW_FILEINFO_BASIC_INFORMATION) }, + { LEVEL(RAW_FILEINFO_STANDARD_INFORMATION) }, + { LEVEL(RAW_FILEINFO_INTERNAL_INFORMATION) }, + { LEVEL(RAW_FILEINFO_EA_INFORMATION) }, + { LEVEL(RAW_FILEINFO_ACCESS_INFORMATION) }, + { LEVEL(RAW_FILEINFO_POSITION_INFORMATION) }, + { LEVEL(RAW_FILEINFO_MODE_INFORMATION) }, + { LEVEL(RAW_FILEINFO_ALIGNMENT_INFORMATION) }, + { LEVEL(RAW_FILEINFO_ALL_INFORMATION) }, + { LEVEL(RAW_FILEINFO_ALT_NAME_INFORMATION) }, + { LEVEL(RAW_FILEINFO_STREAM_INFORMATION) }, + { LEVEL(RAW_FILEINFO_COMPRESSION_INFORMATION) }, + { LEVEL(RAW_FILEINFO_NETWORK_OPEN_INFORMATION) }, + { LEVEL(RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION) }, + + { LEVEL(RAW_FILEINFO_SMB2_ALL_EAS) }, + + { LEVEL(RAW_FILEINFO_SMB2_ALL_INFORMATION) }, + { LEVEL(RAW_FILEINFO_SEC_DESC) } +}; + +static struct { + const char *name; + uint16_t level; + NTSTATUS status; + union smb_fsinfo info; +} fs_levels[] = { + { LEVEL(RAW_QFS_VOLUME_INFORMATION) }, + { LEVEL(RAW_QFS_SIZE_INFORMATION) }, + { LEVEL(RAW_QFS_DEVICE_INFORMATION) }, + { LEVEL(RAW_QFS_ATTRIBUTE_INFORMATION) }, + { LEVEL(RAW_QFS_QUOTA_INFORMATION) }, + { LEVEL(RAW_QFS_FULL_SIZE_INFORMATION) }, + { LEVEL(RAW_QFS_OBJECTID_INFORMATION) }, + { LEVEL(RAW_QFS_SECTOR_SIZE_INFORMATION) }, +}; + +#define FNAME "testsmb2_file.dat" +#define DNAME "testsmb2_dir" + +/* + test fileinfo levels +*/ +static bool torture_smb2_fileinfo(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_handle hfile, hdir; + NTSTATUS status; + int i; + + status = torture_smb2_testfile(tree, FNAME, &hfile); + torture_assert_ntstatus_ok(tctx, status, "Unable to create test file " + FNAME "\n"); + + status = torture_smb2_testdir(tree, DNAME, &hdir); + torture_assert_ntstatus_ok(tctx, status, "Unable to create test dir " + DNAME "\n"); + + torture_comment(tctx, "Testing file info levels\n"); + torture_smb2_all_info(tctx, tree, hfile); + torture_smb2_all_info(tctx, tree, hdir); + + for (i=0;isession->transport->conn); + + d1 = talloc_asprintf(tctx, "torture_dIr1N"); + torture_assert_not_null(tctx, d1, "d1"); + d1l = strlower_talloc(tctx, d1); + torture_assert_not_null(tctx, d1l, "d1l"); + d1u = strupper_talloc(tctx, d1); + torture_assert_not_null(tctx, d1u, "d1u"); + + d2 = talloc_asprintf(tctx, "%s\\dIr2Na", d1); + torture_assert_not_null(tctx, d2, "d2"); + d2l = strlower_talloc(tctx, d2); + torture_assert_not_null(tctx, d2l, "d2l"); + d2u = strupper_talloc(tctx, d2); + torture_assert_not_null(tctx, d2u, "d2u"); + + d3 = talloc_asprintf(tctx, "%s\\dIr3NaM", d2); + torture_assert_not_null(tctx, d3, "d3"); + d3l = strlower_talloc(tctx, d3); + torture_assert_not_null(tctx, d3l, "d3l"); + d3u = strupper_talloc(tctx, d3); + torture_assert_not_null(tctx, d3u, "d3u"); + + d3s = talloc_asprintf(tctx, "%s:sTrEaM3", d3); + torture_assert_not_null(tctx, d3s, "d3s"); + d3sl = strlower_talloc(tctx, d3s); + torture_assert_not_null(tctx, d3sl, "d3sl"); + d3su = strupper_talloc(tctx, d3s); + torture_assert_not_null(tctx, d3su, "d3su"); + d3sd = talloc_asprintf(tctx, "%s:$DaTa", d3s); + torture_assert_not_null(tctx, d3sd, "d3sd"); + + f4 = talloc_asprintf(tctx, "%s\\fIlE4NaMe", d3); + torture_assert_not_null(tctx, f4, "f4"); + f4l = strlower_talloc(tctx, f4); + torture_assert_not_null(tctx, f4l, "f4l"); + f4u = strupper_talloc(tctx, f4); + torture_assert_not_null(tctx, f4u, "f4u"); + f4d = talloc_asprintf(tctx, "%s::$dAtA", f4); + torture_assert_not_null(tctx, f4d, "f4d"); + + f4s = talloc_asprintf(tctx, "%s:StReAm4", f4); + torture_assert_not_null(tctx, f4s, "f4s"); + f4sl = strlower_talloc(tctx, f4s); + torture_assert_not_null(tctx, f4sl, "f4sl"); + f4su = strupper_talloc(tctx, f4s); + torture_assert_not_null(tctx, f4su, "f4su"); + f4sd = talloc_asprintf(tctx, "%s:$dAtA", f4s); + torture_assert_not_null(tctx, f4sd, "f4sd"); + + status = smb2_util_roothandle(tree, &hroot); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle"); + + info.normalized_name_info.in.file.handle = hroot; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + if (protocol < PROTOCOL_SMB3_11) { + /* + * Only SMB 3.1.1 and above should offer this. + */ + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_SUPPORTED, + "getinfo hroot"); + torture_skip(tctx, "SMB 3.1.1 not supported"); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + /* + * Not all servers support this. + * (only Windows 10 1803 and higher) + */ + torture_skip(tctx, "NORMALIZED_NAME_INFORMATION not supported"); + } + torture_assert_ntstatus_ok(tctx, status, "getinfo hroot"); + torture_assert(tctx, info.normalized_name_info.out.fname.s == NULL, + "getinfo hroot should be empty"); + + smb2_deltree(tree, d1); + + status = torture_smb2_testdir(tree, d1, &hd1); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd1"); + status = torture_smb2_open(tree, d1l, SEC_RIGHTS_FILE_ALL, &hd1l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd1l"); + status = torture_smb2_open(tree, d1u, SEC_RIGHTS_FILE_ALL, &hd1u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd1u"); + + status = torture_smb2_testdir(tree, d2, &hd2); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd2"); + status = torture_smb2_open(tree, d2l, SEC_RIGHTS_FILE_ALL, &hd2l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd2l"); + status = torture_smb2_open(tree, d2u, SEC_RIGHTS_FILE_ALL, &hd2u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd2u"); + + status = torture_smb2_testdir(tree, d3, &hd3); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd3"); + status = torture_smb2_open(tree, d3l, SEC_RIGHTS_FILE_ALL, &hd3l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3l"); + status = torture_smb2_open(tree, d3u, SEC_RIGHTS_FILE_ALL, &hd3u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3u"); + + status = torture_smb2_testfile(tree, d3s, &hd3s); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd3s"); + status = torture_smb2_open(tree, d3sl, SEC_RIGHTS_FILE_ALL, &hd3sl); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3sl"); + status = torture_smb2_open(tree, d3su, SEC_RIGHTS_FILE_ALL, &hd3su); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3su"); + status = torture_smb2_open(tree, d3sd, SEC_RIGHTS_FILE_ALL, &hd3sd); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3sd"); + + status = torture_smb2_testfile(tree, f4, &hf4); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hf4"); + status = torture_smb2_open(tree, f4l, SEC_RIGHTS_FILE_ALL, &hf4l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4l"); + status = torture_smb2_open(tree, f4u, SEC_RIGHTS_FILE_ALL, &hf4u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4u"); + status = torture_smb2_open(tree, f4d, SEC_RIGHTS_FILE_ALL, &hf4d); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4d"); + + status = torture_smb2_testfile(tree, f4s, &hf4s); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hf4s"); + status = torture_smb2_open(tree, f4sl, SEC_RIGHTS_FILE_ALL, &hf4sl); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4sl"); + status = torture_smb2_open(tree, f4su, SEC_RIGHTS_FILE_ALL, &hf4su); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4su"); + status = torture_smb2_open(tree, f4sd, SEC_RIGHTS_FILE_ALL, &hf4sd); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4sd"); + + info.normalized_name_info.in.file.handle = hd1; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1"); + info.normalized_name_info.in.file.handle = hd1l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1l"); + info.normalized_name_info.in.file.handle = hd1u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1u"); + + info.normalized_name_info.in.file.handle = hd2; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2"); + info.normalized_name_info.in.file.handle = hd2l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2l"); + info.normalized_name_info.in.file.handle = hd2u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2u"); + + info.normalized_name_info.in.file.handle = hd3; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3"); + info.normalized_name_info.in.file.handle = hd3l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3l"); + info.normalized_name_info.in.file.handle = hd3u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3u"); + + info.normalized_name_info.in.file.handle = hd3s; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3s"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3s"); + info.normalized_name_info.in.file.handle = hd3sl; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3sl"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3sl"); + info.normalized_name_info.in.file.handle = hd3su; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3su"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3su"); + info.normalized_name_info.in.file.handle = hd3sd; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3sd"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3sd"); + + info.normalized_name_info.in.file.handle = hf4; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4"); + info.normalized_name_info.in.file.handle = hf4l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4l"); + info.normalized_name_info.in.file.handle = hf4u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4u"); + info.normalized_name_info.in.file.handle = hf4d; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4d"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4d"); + + info.normalized_name_info.in.file.handle = hf4s; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4s"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4s"); + info.normalized_name_info.in.file.handle = hf4sl; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4sl"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4sl"); + info.normalized_name_info.in.file.handle = hf4su; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4su"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4su"); + info.normalized_name_info.in.file.handle = hf4sd; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4sd"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4sd"); + + /* Set max protocol to SMB 3.0.2 */ + options3_0 = tree->session->transport->options; + options3_0.max_protocol = PROTOCOL_SMB3_02; + options3_0.client_guid = GUID_zero(); + ret = torture_smb2_connection_ext(tctx, 0, &options3_0, &tree_3_0); + torture_assert(tctx, ret, "connection with SMB < 3.1.1 failed"); + + status = smb2_util_roothandle(tree_3_0, &hroot_3_0); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle 3_0"); + + info.normalized_name_info.in.file.handle = hroot_3_0; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree_3_0, tree_3_0, &info); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_SUPPORTED, + "getinfo hroot"); + + return true; +} + +/* + test fsinfo levels +*/ +static bool torture_smb2_fsinfo(struct torture_context *tctx) +{ + bool ret; + struct smb2_tree *tree; + int i; + NTSTATUS status; + struct smb2_handle handle; + + torture_comment(tctx, "Testing fsinfo levels\n"); + + ret = torture_smb2_connection(tctx, &tree); + torture_assert(tctx, ret, "connection failed"); + + status = smb2_util_roothandle(tree, &handle); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle"); + + for (i=0;iin.output_buffer_length = i; + + status = smb2_getinfo(tree, tree, b); + + if (i < fixed) { + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_INFO_LENGTH_MISMATCH, + "Wrong error code small buffer"); + continue; + } + + if (iout.blob.data); + continue; + } + + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_OK, + "Wrong error code for right sized buffer"); + } + + return true; +} + +struct level_buffersize { + int level; + size_t fixed; +}; + +static bool torture_smb2_qfs_buffercheck(struct torture_context *tctx) +{ + bool ret; + struct smb2_tree *tree; + NTSTATUS status; + struct smb2_handle handle; + int i; + + struct level_buffersize levels[] = { + { 1, 24 }, /* We don't have proper defines here */ + { 3, 24 }, + { 4, 8 }, + { 5, 16 }, + { 6, 48 }, + { 7, 32 }, + { 11, 28 }, + }; + + torture_comment(tctx, "Testing SMB2_GETINFO_FS buffer sizes\n"); + + ret = torture_smb2_connection(tctx, &tree); + torture_assert(tctx, ret, "connection failed"); + + status = smb2_util_roothandle(tree, &handle); + torture_assert_ntstatus_ok( + tctx, status, "Unable to create root handle"); + + for (i=0; i. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "librpc/gen_ndr/ndr_ioctl.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/resolve/resolve.h" +#include "lib/param/param.h" +#include "lib/util/tevent_ntstatus.h" + +#define FNAME "testfsctl.dat" +#define FNAME2 "testfsctl2.dat" +#define DNAME "testfsctl_dir" + +/* + basic testing of SMB2 shadow copy calls +*/ +static bool test_ioctl_get_shadow_copy(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle h; + uint8_t buf[100]; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok(torture, status, "create write"); + + ZERO_ARRAY(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok(torture, status, "write"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = h; + ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS; + ioctl.smb2.in.max_output_response = 16; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) + || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) { + torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n"); + } + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS"); + + return true; +} + +/* + basic testing of the SMB2 server side copy ioctls +*/ +static bool test_ioctl_req_resume_key(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle h; + uint8_t buf[100]; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct req_resume_key_rsp res_key; + enum ndr_err_code ndr_ret; + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok(torture, status, "create write"); + + ZERO_ARRAY(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok(torture, status, "write"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = h; + ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; + ioctl.smb2.in.max_output_response = 32; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_req_resume_key_rsp"); + + NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key); + + talloc_free(tmp_ctx); + return true; +} + +/* + testing fetching a resume key twice for one file handle +*/ +static bool test_ioctl_req_two_resume_keys(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle h; + uint8_t buf[100]; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct req_resume_key_rsp res_key; + enum ndr_err_code ndr_ret; + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok(torture, status, "create write"); + + ZERO_ARRAY(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok(torture, status, "write"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = h; + ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; + ioctl.smb2.in.max_output_response = 32; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_req_resume_key_rsp"); + + NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = h; + ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; + ioctl.smb2.in.max_output_response = 32; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_req_resume_key_rsp"); + + NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key); + + talloc_free(tmp_ctx); + return true; +} + +static uint64_t patt_hash(uint64_t off) +{ + return off; +} + +static bool write_pattern(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len, + uint64_t patt_off) +{ + NTSTATUS status; + uint64_t i; + uint8_t *buf; + uint64_t io_sz = MIN(1024 * 64, len); + + if (len == 0) { + return true; + } + + torture_assert(torture, (len % 8) == 0, "invalid write len"); + + buf = talloc_zero_size(mem_ctx, io_sz); + torture_assert(torture, (buf != NULL), "no memory for file data buf"); + + while (len > 0) { + for (i = 0; i <= io_sz - 8; i += 8) { + SBVAL(buf, i, patt_hash(patt_off)); + patt_off += 8; + } + + status = smb2_util_write(tree, h, + buf, off, io_sz); + torture_assert_ntstatus_ok(torture, status, "file write"); + + len -= io_sz; + off += io_sz; + } + + talloc_free(buf); + + return true; +} + +static bool check_pattern(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len, + uint64_t patt_off) +{ + if (len == 0) { + return true; + } + + torture_assert(torture, (len % 8) == 0, "invalid read len"); + + while (len > 0) { + uint64_t i; + struct smb2_read r; + NTSTATUS status; + uint64_t io_sz = MIN(1024 * 64, len); + + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = io_sz; + r.in.offset = off; + status = smb2_read(tree, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "read"); + + torture_assert_u64_equal(torture, r.out.data.length, io_sz, + "read data len mismatch"); + + for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) { + uint64_t data = BVAL(r.out.data.data, i); + torture_assert_u64_equal(torture, data, patt_hash(patt_off), + talloc_asprintf(torture, "read data " + "pattern bad at %llu\n", + (unsigned long long)off + i)); + } + talloc_free(r.out.data.data); + len -= io_sz; + off += io_sz; + } + + return true; +} + +static bool check_zero(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len) +{ + uint64_t i; + struct smb2_read r; + NTSTATUS status; + + if (len == 0) { + return true; + } + + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = len; + r.in.offset = off; + status = smb2_read(tree, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "read"); + + torture_assert_u64_equal(torture, r.out.data.length, len, + "read data len mismatch"); + + for (i = 0; i <= len - 8; i += 8) { + uint64_t data = BVAL(r.out.data.data, i); + torture_assert_u64_equal(torture, data, 0, + talloc_asprintf(mem_ctx, "read zero " + "bad at %llu\n", + (unsigned long long)i)); + } + + talloc_free(r.out.data.data); + return true; +} + +static bool test_setup_open(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + const char *fname, + struct smb2_handle *fh, + uint32_t desired_access, + uint32_t file_attributes) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.desired_access = desired_access; + io.in.file_attributes = file_attributes; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) { + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + } + io.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "file create"); + + *fh = io.out.file.handle; + + return true; +} + +static bool test_setup_create_fill(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + const char *fname, + struct smb2_handle *fh, + uint64_t size, + uint32_t desired_access, + uint32_t file_attributes) +{ + bool ok; + uint32_t initial_access = desired_access; + + if (size > 0) { + initial_access |= SEC_FILE_APPEND_DATA; + } + + smb2_util_unlink(tree, fname); + + ok = test_setup_open(torture, tree, mem_ctx, + fname, + fh, + initial_access, + file_attributes); + torture_assert(torture, ok, "file create"); + + if (size > 0) { + ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0); + torture_assert(torture, ok, "write pattern"); + } + + if (initial_access != desired_access) { + smb2_util_close(tree, *fh); + ok = test_setup_open(torture, tree, mem_ctx, + fname, + fh, + desired_access, + file_attributes); + torture_assert(torture, ok, "file open"); + } + + return true; +} + +static bool test_setup_copy_chunk(struct torture_context *torture, + struct smb2_tree *src_tree, + struct smb2_tree *dst_tree, + TALLOC_CTX *mem_ctx, + uint32_t nchunks, + const char *src_name, + struct smb2_handle *src_h, + uint64_t src_size, + uint32_t src_desired_access, + const char *dst_name, + struct smb2_handle *dest_h, + uint64_t dest_size, + uint32_t dest_desired_access, + struct srv_copychunk_copy *cc_copy, + union smb_ioctl *ioctl) +{ + struct req_resume_key_rsp res_key; + bool ok; + NTSTATUS status; + enum ndr_err_code ndr_ret; + + ok = test_setup_create_fill(torture, src_tree, mem_ctx, src_name, + src_h, src_size, src_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "src file create fill"); + + ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name, + dest_h, dest_size, dest_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "dest file create fill"); + + ZERO_STRUCTPN(ioctl); + ioctl->smb2.level = RAW_IOCTL_SMB2; + ioctl->smb2.in.file.handle = *src_h; + ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; + /* Allow for Key + ContextLength + Context */ + ioctl->smb2.in.max_output_response = 32; + ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_REQUEST_RESUME_KEY"); + + ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_req_resume_key_rsp"); + + ZERO_STRUCTPN(ioctl); + ioctl->smb2.level = RAW_IOCTL_SMB2; + ioctl->smb2.in.file.handle = *dest_h; + ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK; + ioctl->smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp); + ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_STRUCTPN(cc_copy); + memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key)); + cc_copy->chunk_count = nchunks; + cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks); + torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks"); + + return true; +} + + +static bool check_copy_chunk_rsp(struct torture_context *torture, + struct srv_copychunk_rsp *cc_rsp, + uint32_t ex_chunks_written, + uint32_t ex_chunk_bytes_written, + uint32_t ex_total_bytes_written) +{ + torture_assert_int_equal(torture, cc_rsp->chunks_written, + ex_chunks_written, "num chunks"); + torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written, + ex_chunk_bytes_written, "chunk bytes written"); + torture_assert_int_equal(torture, cc_rsp->total_bytes_written, + ex_total_bytes_written, "chunk total bytes"); + return true; +} + +static bool test_ioctl_copy_chunk_simple(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* copy all src file data (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_multi(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 2, /* chunks */ + FNAME, + &src_h, 8192, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* copy all src file data via two chunks */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + cc_copy.chunks[1].source_off = 4096; + cc_copy.chunks[1].target_off = 4096; + cc_copy.chunks[1].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 2, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 8192); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 2, /* chunks */ + FNAME, + &src_h, 96, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* copy all src file data via two chunks, sub block size chunks */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 48; + + cc_copy.chunks[1].source_off = 48; + cc_copy.chunks[1].target_off = 48; + cc_copy.chunks[1].length = 48; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 2, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 96); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_over(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 2, /* chunks */ + FNAME, + &src_h, 8192, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 4096, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* first chunk overwrites existing dest data */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + /* second chunk overwrites the first */ + cc_copy.chunks[1].source_off = 4096; + cc_copy.chunks[1].target_off = 0; + cc_copy.chunks[1].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 2, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 8192); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_append(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 2, /* chunks */ + FNAME, + &src_h, 4096, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + /* second chunk appends the same data to the first */ + cc_copy.chunks[1].source_off = 0; + cc_copy.chunks[1].target_off = 4096; + cc_copy.chunks[1].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 2, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 8192); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_limits(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* chunks */ + FNAME, + &src_h, 4096, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* send huge chunk length request */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = UINT_MAX; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, "marshalling request"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "bad oversize chunk response"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response"); + + torture_comment(torture, "limit max chunks, got %u\n", + cc_rsp.chunks_written); + torture_comment(torture, "limit max chunk len, got %u\n", + cc_rsp.chunk_bytes_written); + torture_comment(torture, "limit max total bytes, got %u\n", + cc_rsp.total_bytes_written); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle src_h2; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* chunks */ + FNAME, + &src_h, 4096, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + /* open and lock the copychunk src file */ + status = torture_smb2_testfile(tree, FNAME, &src_h2); + torture_assert_ntstatus_ok(torture, status, "2nd src open"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = src_h2; + lck.in.locks = el; + el[0].offset = cc_copy.chunks[0].source_off; + el[0].length = cc_copy.chunks[0].length; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "lock"); + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + /* + * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success... + * + * Edgar Olougouna @ MS wrote: + * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT + * discrepancy observed between Windows versions, we confirm that the + * behavior change is expected. + * + * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs + * to move the chunks from the source to the destination. + * These ReadFile/WriteFile APIs go through the byte-range lock checks, + * and this explains the observed STATUS_FILE_LOCK_CONFLICT error. + * + * Prior to Windows Server 2012, CopyChunk used mapped sections to move + * the data. And byte range locks are not enforced on mapped I/O, and + * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2. + */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_FILE_LOCK_CONFLICT, + "FSCTL_SRV_COPYCHUNK locked"); + + /* should get cc response data with the lock conflict status */ + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 0, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 0); /* total bytes written */ + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000001; + lck.in.file.handle = src_h2; + lck.in.locks = el; + el[0].offset = cc_copy.chunks[0].source_off; + el[0].length = cc_copy.chunks[0].length; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "unlock"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK unlocked"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h2); + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + struct smb2_handle dest_h2; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* chunks */ + FNAME, + &src_h, 4096, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 4096, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + /* open and lock the copychunk dest file */ + status = torture_smb2_testfile(tree, FNAME2, &dest_h2); + torture_assert_ntstatus_ok(torture, status, "2nd src open"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = dest_h2; + lck.in.locks = el; + el[0].offset = cc_copy.chunks[0].target_off; + el[0].length = cc_copy.chunks[0].length; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "lock"); + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_FILE_LOCK_CONFLICT, + "FSCTL_SRV_COPYCHUNK locked"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000001; + lck.in.file.handle = dest_h2; + lck.in.locks = el; + el[0].offset = cc_copy.chunks[0].target_off; + el[0].length = cc_copy.chunks[0].length; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "unlock"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK unlocked"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, dest_h2); + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, + FNAME, + &src_h, 4096, + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* overwrite the resume key with a bogus value */ + memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24); + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, + FNAME, + &src_h, 8192, + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* the source is also the destination */ + ioctl.smb2.in.file.handle = src_h; + + /* non-overlapping */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 4096; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +/* + * Test a single-chunk copychunk request, where the source and target ranges + * overlap, and the SourceKey refers to the same target file. E.g: + * + * Initial State + * ------------- + * File: src_and_dest + * Offset: 0123456789 + * Data: abcdefghij + * + * Request + * ------- + * FSCTL_SRV_COPYCHUNK(src_and_dest) + * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest) + * ChunkCount = 1 + * Chunks[0].SourceOffset = 0 + * Chunks[0].TargetOffset = 4 + * Chunks[0].Length = 6 + * + * Resultant State + * --------------- + * File: src_and_dest + * Offset: 0123456789 + * Data: abcdabcdef + * + * The resultant contents of src_and_dest is dependent on the server's + * copy algorithm. In the above example, the server uses an IO buffer + * large enough to hold the entire six-byte source data before writing + * to TargetOffset. If the server were to use a four-byte IO buffer and + * started reads/writes from the lowest offset, then the two overlapping + * bytes in the above example would be overwritten before being read. The + * resultant file contents would be abcdabcdab. + * + * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes + * after this offset are written before being read. Windows 2012 on the + * other hand appears to use a buffer large enough to hold its maximum + * supported chunk size (1M). Samba currently uses a 64k copy buffer by + * default (vfs_cc_state.buf). + * + * This test uses an 8-byte overlap at 2040-2048, so that it passes against + * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears + * to use a different copy algorithm to 2008r2. + */ +static bool +test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + /* exceed the vfs_default copy buffer */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, + FNAME, + &src_h, 2048 * 2, + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* the source is also the destination */ + ioctl.smb2.in.file.handle = src_h; + + /* 8 bytes overlap between source and target ranges */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 2048 - 8; + cc_copy.chunks[0].length = 2048; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 2048); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + bool ok; + /* read permission on src */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */ + FNAME, &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, + FNAME2, &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob( + &ioctl.smb2.in.out, tmp_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* execute permission on src */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */ + FNAME, &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE, + FNAME2, &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob( + &ioctl.smb2.in.out, tmp_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* neither read nor execute permission on src */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */ + FNAME, &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h, + 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* no write permission on dest */ + ok = test_setup_copy_chunk( + torture, tree, tree, tmp_ctx, 1, /* 1 chunk */ + FNAME, &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h, + 0, /* 0 byte dest file */ + (SEC_RIGHTS_FILE_ALL & + ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)), + &cc_copy, &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* no read permission on dest */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */ + FNAME, &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, + FNAME2, &dest_h, 0, /* 0 byte dest file */ + (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA), + &cc_copy, &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + /* + * FSCTL_SRV_COPYCHUNK requires read permission on dest, + * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not. + */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + + return true; +} + +static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + bool ok; + + /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */ + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + (SEC_RIGHTS_FILE_WRITE + | SEC_RIGHTS_FILE_EXECUTE), + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE; + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK_WRITE"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + + return true; +} + +static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* Request copy where off + length exceeds size of src */ + cc_copy.chunks[0].source_off = 1024; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_VIEW_SIZE, + "FSCTL_SRV_COPYCHUNK oversize"); + + /* Request copy where length exceeds size of src */ + cc_copy.chunks[0].source_off = 1024; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 3072; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_COPYCHUNK just right"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 3072); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool +test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 2, /* 2 chunks */ + FNAME, + &src_h, 8192, /* fill 8192 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* Request copy where off + length exceeds size of src */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + cc_copy.chunks[1].source_off = 4096; + cc_copy.chunks[1].target_off = 4096; + cc_copy.chunks[1].length = 8192; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_VIEW_SIZE, + "FSCTL_SRV_COPYCHUNK oversize"); + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response"); + + /* first chunk should still be written */ + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + struct smb2_read r; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + int i; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* copy all src file data (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 4096; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail(torture, "bad copy chunk response data"); + } + + /* check for zeros in first 4k */ + ZERO_STRUCT(r); + r.in.file.handle = dest_h; + r.in.length = 4096; + r.in.offset = 0; + status = smb2_read(tree, tmp_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "read"); + + torture_assert_u64_equal(torture, r.out.data.length, 4096, + "read data len mismatch"); + + for (i = 0; i < 4096; i++) { + torture_assert(torture, (r.out.data.data[i] == 0), + "sparse did not pass class"); + } + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +/* + * set the ioctl MaxOutputResponse size to less than + * sizeof(struct srv_copychunk_rsp) + */ +static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + /* req is valid, but use undersize max_output_response */ + ioctl.smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp) - 1; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_SRV_COPYCHUNK"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + union smb_fileinfo q; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + if (!ok) { + torture_fail(torture, "setup copy chunk error"); + } + + /* zero length server-side copy (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 0; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "bad zero-length chunk response"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response"); + + ZERO_STRUCT(q); + q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + q.all_info2.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, torture, &q); + torture_assert_ntstatus_ok(torture, status, "getinfo"); + + torture_assert_int_equal(torture, q.all_info2.out.size, 0, + "size after zero len clone"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool copy_one_stream(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *tmp_ctx, + const char *src_sname, + const char *dst_sname) +{ + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + NTSTATUS status; + union smb_ioctl io; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok = false; + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* 1 chunk */ + src_sname, + &src_h, 256, /* fill 256 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + dst_sname, + &dest_h, 0, /* 0 byte dest file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + &cc_copy, + &io); + torture_assert_goto(torture, ok == true, ok, done, + "setup copy chunk error\n"); + + /* copy all src file data (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 256; + + ndr_ret = ndr_push_struct_blob( + &io.smb2.in.out, tmp_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree, tmp_ctx, &io.smb2); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, + "FSCTL_SRV_COPYCHUNK\n"); + + ndr_ret = ndr_pull_struct_blob( + &io.smb2.out.out, tmp_ctx, &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_pull_srv_copychunk_rsp\n"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 256); /* total bytes written */ + torture_assert_goto(torture, ok == true, ok, done, + "bad copy chunk response data\n"); + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data\n"); + } + +done: + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree, dest_h); + } + + return ok; +} + +/** + * Create a file + **/ +static bool torture_setup_file(TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + const char *name) +{ + struct smb2_create io; + NTSTATUS status; + + smb2_util_unlink(tree, name); + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = name; + + status = smb2_create(tree, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + status = smb2_util_close(tree, io.out.file.handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + return true; +} + +static bool test_copy_chunk_streams(struct torture_context *torture, + struct smb2_tree *tree) +{ + const char *src_name = "src"; + const char *dst_name = "dst"; + struct names { + const char *src_sname; + const char *dst_sname; + } names[] = { + { "src:foo", "dst:foo" } + }; + int i; + TALLOC_CTX *tmp_ctx = NULL; + bool ok = false; + + tmp_ctx = talloc_new(tree); + torture_assert_not_null_goto(torture, tmp_ctx, ok, done, + "torture_setup_file\n"); + + ok = torture_setup_file(torture, tree, src_name); + torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n"); + ok = torture_setup_file(torture, tree, dst_name); + torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n"); + + for (i = 0; i < ARRAY_SIZE(names); i++) { + ok = copy_one_stream(torture, tree, tmp_ctx, + names[i].src_sname, + names[i].dst_sname); + torture_assert_goto(torture, ok == true, ok, done, + "copy_one_stream failed\n"); + } + +done: + smb2_util_unlink(tree, src_name); + smb2_util_unlink(tree, dst_name); + talloc_free(tmp_ctx); + return ok; +} + +static bool test_copy_chunk_across_shares(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = NULL; + struct smb2_tree *tree2 = NULL; + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + union smb_ioctl ioctl; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + NTSTATUS status; + bool ok = false; + + mem_ctx = talloc_new(tctx); + torture_assert_not_null_goto(tctx, mem_ctx, ok, done, + "talloc_new\n"); + + ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2); + torture_assert_goto(tctx, ok == true, ok, done, + "torture_smb2_tree_connect failed\n"); + + ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + torture_assert_goto(tctx, ok == true, ok, done, + "test_setup_copy_chunk failed\n"); + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob( + &ioctl.smb2.in.out, mem_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "FSCTL_SRV_COPYCHUNK\n"); + + ndr_ret = ndr_pull_struct_blob( + &ioctl.smb2.out.out, mem_ctx, &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + + torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done, + "ndr_pull_srv_copychunk_rsp\n"); + + ok = check_copy_chunk_rsp(tctx, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + torture_assert_goto(tctx, ok == true, ok, done, + "bad copy chunk response data\n"); + + ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0); + torture_assert_goto(tctx, ok == true, ok, done, + "inconsistent file data\n"); + +done: + TALLOC_FREE(mem_ctx); + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree2, dest_h); + } + smb2_util_unlink(tree, FNAME); + smb2_util_unlink(tree2, FNAME2); + if (tree2 != NULL) { + smb2_tdis(tree2); + } + return ok; +} + +/* Test closing the src handle */ +static bool test_copy_chunk_across_shares2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = NULL; + struct smb2_tree *tree2 = NULL; + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + union smb_ioctl ioctl; + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + NTSTATUS status; + bool ok = false; + + mem_ctx = talloc_new(tctx); + torture_assert_not_null_goto(tctx, mem_ctx, ok, done, + "talloc_new\n"); + + ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2); + torture_assert_goto(tctx, ok == true, ok, done, + "torture_smb2_tree_connect failed\n"); + + ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx, + 1, /* 1 chunk */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + torture_assert_goto(tctx, ok == true, ok, done, + "test_setup_copy_chunk failed\n"); + + status = smb2_util_close(tree, src_h); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(src_h); + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob( + &ioctl.smb2.in.out, mem_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ok, done, "smb2_ioctl failed\n"); + +done: + TALLOC_FREE(mem_ctx); + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree2, dest_h); + } + smb2_util_unlink(tree, FNAME); + smb2_util_unlink(tree2, FNAME2); + if (tree2 != NULL) { + smb2_tdis(tree2); + } + return ok; +} + +/* Test offset works */ +static bool test_copy_chunk_across_shares3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = NULL; + struct smb2_tree *tree2 = NULL; + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + union smb_ioctl ioctl; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + NTSTATUS status; + bool ok = false; + + mem_ctx = talloc_new(tctx); + torture_assert_not_null_goto(tctx, mem_ctx, ok, done, + "talloc_new\n"); + + ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2); + torture_assert_goto(tctx, ok == true, ok, done, + "torture_smb2_tree_connect failed\n"); + + ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx, + 2, /* 2 chunks */ + FNAME, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + torture_assert_goto(tctx, ok == true, ok, done, + "test_setup_copy_chunk failed\n"); + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + /* second chunk appends the same data to the first */ + cc_copy.chunks[1].source_off = 0; + cc_copy.chunks[1].target_off = 4096; + cc_copy.chunks[1].length = 4096; + + ndr_ret = ndr_push_struct_blob( + &ioctl.smb2.in.out, mem_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "smb2_ioctl failed\n"); + + ndr_ret = ndr_pull_struct_blob( + &ioctl.smb2.out.out, mem_ctx, &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + + torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done, + "ndr_pull_srv_copychunk_rsp\n"); + + ok = check_copy_chunk_rsp(tctx, &cc_rsp, + 2, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 8192); /* total bytes written */ + torture_assert_goto(tctx, ok == true, ok, done, + "check_copy_chunk_rsp failed\n"); + + ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0); + torture_assert_goto(tctx, ok == true, ok, done, + "check_pattern failed\n"); + + ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 4096, 4096, 0); + torture_assert_goto(tctx, ok == true, ok, done, + "check_pattern failed\n"); + +done: + TALLOC_FREE(mem_ctx); + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree2, dest_h); + } + smb2_util_unlink(tree, FNAME); + smb2_util_unlink(tree2, FNAME2); + if (tree2 != NULL) { + smb2_tdis(tree2); + } + return ok; +} + +static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb2_handle *fh, + bool *compress_support) +{ + NTSTATUS status; + union smb_fsinfo info; + + ZERO_STRUCT(info); + info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION; + info.generic.handle = *fh; + status = smb2_getinfo_fs(tree, tree, &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) { + *compress_support = true; + } else { + *compress_support = false; + } + return NT_STATUS_OK; +} + +static NTSTATUS test_ioctl_compress_get(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + uint16_t *_compression_fmt) +{ + union smb_ioctl ioctl; + struct compression_state cmpr_state; + enum ndr_err_code ndr_ret; + NTSTATUS status; + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_GET_COMPRESSION; + ioctl.smb2.in.max_output_response = sizeof(struct compression_state); + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx, + &cmpr_state, + (ndr_pull_flags_fn_t)ndr_pull_compression_state); + + if (ndr_ret != NDR_ERR_SUCCESS) { + return NT_STATUS_INTERNAL_ERROR; + } + + *_compression_fmt = cmpr_state.format; + return NT_STATUS_OK; +} + +static NTSTATUS test_ioctl_compress_set(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + uint16_t compression_fmt) +{ + union smb_ioctl ioctl; + struct compression_state cmpr_state; + enum ndr_err_code ndr_ret; + NTSTATUS status; + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_COMPRESSION; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + cmpr_state.format = compression_fmt; + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx, + &cmpr_state, + (ndr_push_flags_fn_t)ndr_push_compression_state); + if (ndr_ret != NDR_ERR_SUCCESS) { + return NT_STATUS_INTERNAL_ERROR; + } + + status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2); + return status; +} + +static bool test_ioctl_compress_file_flag(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint16_t compression_fmt; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "initial compression state not NONE"); + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "invalid compression state after set"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_dir_inherit(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle dirh; + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + uint16_t compression_fmt; + bool ok; + char path_buf[PATH_MAX]; + + smb2_deltree(tree, DNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_DIRECTORY); + torture_assert(torture, ok, "setup compression directory"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + torture_skip(torture, "FS compression not supported\n"); + } + + /* set compression on parent dir, then check for inheritance */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh, + COMPRESSION_FORMAT_LZNT1); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "invalid compression state after set"); + + snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "compression attr not inherited by new file"); + + /* check compressed data is consistent */ + ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0); + + /* disable dir compression attr, file should remain compressed */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh, + COMPRESSION_FORMAT_NONE); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "file compression attr removed after dir change"); + smb2_util_close(tree, fh); + + /* new files should no longer inherit compression attr */ + snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression attr present on new file"); + + smb2_util_close(tree, fh); + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_invalid_format(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint16_t compression_fmt; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + 0x0042); /* bogus */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "invalid FSCTL_SET_COMPRESSION"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "initial compression state not NONE"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_invalid_buf(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + union smb_ioctl ioctl; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_GET_COMPRESSION; + ioctl.smb2.in.max_output_response = 0; /* no room for rsp data */ + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER) + && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + /* neither Server 2k12 nor 2k8r2 response status */ + torture_assert(torture, true, + "invalid FSCTL_SET_COMPRESSION"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_query_file_attr(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_fileinfo io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0), + "compression attr before set"); + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED), + "no compression attr after set"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* + * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this + * attribute. + */ +static bool test_ioctl_compress_create_with_attr(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh2; + union smb_fileinfo io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + uint16_t compression_fmt; + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL, + (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED)); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh2); + torture_skip(torture, "FS compression not supported\n"); + } + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "initial compression state not NONE"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh2; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0), + "incorrect compression attr"); + + smb2_util_close(tree, fh2); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_inherit_disable(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + struct smb2_handle dirh; + char path_buf[PATH_MAX]; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint16_t compression_fmt; + + struct smb2_create io; + + smb2_deltree(tree, DNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_DIRECTORY); + torture_assert(torture, ok, "setup compression directory"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + torture_skip(torture, "FS compression not supported\n"); + } + + /* set compression on parent dir, then check for inheritance */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh, + COMPRESSION_FORMAT_LZNT1); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "invalid compression state after set"); + smb2_util_close(tree, dirh); + + snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1), + "compression attr not inherited by new file"); + smb2_util_close(tree, fh); + + snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2); + + /* NO_COMPRESSION option should block inheritance */ + ZERO_STRUCT(io); + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.fname = path_buf; + + status = smb2_create(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "file create"); + + fh = io.out.file.handle; + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression attr inherited by NO_COMPRESSION file"); + smb2_util_close(tree, fh); + + + snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME); + ZERO_STRUCT(io); + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION + | NTCREATEX_OPTIONS_DIRECTORY); + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.fname = path_buf; + + status = smb2_create(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "dir create"); + + dirh = io.out.file.handle; + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression attr inherited by NO_COMPRESSION dir"); + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + + talloc_free(tmp_ctx); + return true; +} + +/* attempting to set compression via SetInfo should not stick */ +static bool test_ioctl_compress_set_file_attr(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + struct smb2_handle dirh; + union smb_fileinfo io; + union smb_setfileinfo set_io; + uint16_t compression_fmt; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0), + "compression attr before set"); + + ZERO_STRUCT(set_io); + set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + set_io.basic_info.in.file.handle = fh; + set_io.basic_info.in.create_time = io.basic_info.out.create_time; + set_io.basic_info.in.access_time = io.basic_info.out.access_time; + set_io.basic_info.in.write_time = io.basic_info.out.write_time; + set_io.basic_info.in.change_time = io.basic_info.out.change_time; + set_io.basic_info.in.attrib = (io.basic_info.out.attrib + | FILE_ATTRIBUTE_COMPRESSED); + status = smb2_setinfo_file(tree, &set_io); + torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0), + "compression attr after set"); + + smb2_util_close(tree, fh); + smb2_deltree(tree, DNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_DIRECTORY); + torture_assert(torture, ok, "setup compression directory"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + io.generic.in.file.handle = dirh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0), + "compression attr before set"); + + ZERO_STRUCT(set_io); + set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + set_io.basic_info.in.file.handle = dirh; + set_io.basic_info.in.create_time = io.basic_info.out.create_time; + set_io.basic_info.in.access_time = io.basic_info.out.access_time; + set_io.basic_info.in.write_time = io.basic_info.out.write_time; + set_io.basic_info.in.change_time = io.basic_info.out.change_time; + set_io.basic_info.in.attrib = (io.basic_info.out.attrib + | FILE_ATTRIBUTE_COMPRESSED); + status = smb2_setinfo_file(tree, &set_io); + torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "dir compression set after SetInfo"); + + smb2_util_close(tree, dirh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_perms(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + uint16_t compression_fmt; + union smb_fileinfo io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + smb2_util_close(tree, fh); + if (!ok) { + torture_skip(torture, "FS compression not supported\n"); + } + + /* attempt get compression without READ_ATTR permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE + | SEC_STD_READ_CONTROL + | SEC_FILE_READ_EA)), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression set after create"); + smb2_util_close(tree, fh); + + /* set compression without WRITE_ATTR permission should succeed */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE + | SEC_STD_WRITE_DAC + | SEC_FILE_WRITE_EA)), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + smb2_util_close(tree, fh); + + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED), + "incorrect compression attr"); + smb2_util_close(tree, fh); + + /* attempt get compression without READ_DATA permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression enabled after set"); + smb2_util_close(tree, fh); + + /* attempt get compression with only SYNCHRONIZE permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + SEC_STD_SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "compression not enabled after set"); + smb2_util_close(tree, fh); + + /* attempt to set compression without WRITE_DATA permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SET_COMPRESSION permission"); + smb2_util_close(tree, fh); + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_NONE); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SET_COMPRESSION permission"); + smb2_util_close(tree, fh); + + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_notsup_get(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint16_t compression_fmt; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + /* skip if the server DOES support compression */ + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression supported\n"); + } + + /* + * Despite not supporting compression, we should get a successful + * response indicating that the file is uncompressed - like WS2016. + */ + status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh, + &compression_fmt); + torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION"); + + torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE), + "initial compression state not NONE"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_compress_notsup_set(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup compression file"); + + /* skip if the server DOES support compression */ + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression supported\n"); + } + + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_NOT_SUPPORTED, + "FSCTL_SET_COMPRESSION default"); + + /* + * Despite not supporting compression, we should get a successful + * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS. + */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_NONE); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SET_COMPRESSION none"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* + basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl +*/ +static bool test_ioctl_network_interface_info(struct torture_context *torture, + struct smb2_tree *tree) +{ + union smb_ioctl ioctl; + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_net_iface_info net_iface; + enum ndr_err_code ndr_ret; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + fh.data[0] = UINT64_MAX; + fh.data[1] = UINT64_MAX; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO; + ioctl.smb2.in.max_output_response = 0x10000; /* Windows client sets this to 64KiB */ + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface, + (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_fsctl_net_iface_info"); + + NDR_PRINT_DEBUG(fsctl_net_iface_info, &net_iface); + + talloc_free(tmp_ctx); + return true; +} + +/* + * Check whether all @fs_support_flags are set in the server's + * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response. + */ +static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb2_handle *fh, + uint64_t fs_support_flags, + bool *supported) +{ + NTSTATUS status; + union smb_fsinfo info; + + ZERO_STRUCT(info); + info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION; + info.generic.handle = *fh; + status = smb2_getinfo_fs(tree, tree, &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if ((info.attribute_info.out.fs_attr & fs_support_flags) + == fs_support_flags) { + *supported = true; + } else { + *supported = false; + } + return NT_STATUS_OK; +} + +static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + bool set) +{ + union smb_ioctl ioctl; + NTSTATUS status; + uint8_t set_sparse; + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + set_sparse = (set ? 0xFF : 0x0); + ioctl.smb2.in.out.data = &set_sparse; + ioctl.smb2.in.out.length = sizeof(set_sparse); + + status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2); + return status; +} + +static NTSTATUS test_sparse_get(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + bool *_is_sparse) +{ + union smb_fileinfo io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE); + + return status; +} + +/* + * Manually test setting and clearing sparse flag. Intended for file system + * specific tests to toggle the flag through SMB and check the status in the + * file system. + */ +bool test_ioctl_set_sparse(struct torture_context *tctx) +{ + bool set, ret = true; + const char *filename = NULL; + struct smb2_create create = { }; + struct smb2_tree *tree = NULL; + NTSTATUS status; + + set = torture_setting_bool(tctx, "set_sparse", true); + filename = torture_setting_string(tctx, "filename", NULL); + + if (filename == NULL) { + torture_fail(tctx, "Need to provide filename through " + "--option=torture:filename=testfile\n"); + return false; + } + + if (!torture_smb2_connection(tctx, &tree)) { + torture_comment(tctx, "Initializing smb2 connection failed.\n"); + return false; + } + + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = 0; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.fname = filename; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE failed.\n"); + + status = test_ioctl_sparse_req(tctx, tctx, tree, + create.out.file.handle, set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "FSCTL_SET_SPARSE failed.\n"); +done: + + return ret; +} + +static bool test_ioctl_sparse_file_flag(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_fileinfo io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE"); + + torture_assert(torture, + ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0), + "sparse attr before set"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr after unset"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_file_attr(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE)); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr on open"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_dir_flag(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle dirh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + smb2_deltree(tree, DNAME); + ok = test_setup_create_fill(torture, tree, tmp_ctx, + DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_DIRECTORY); + torture_assert(torture, ok, "setup sparse directory"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + torture_skip(torture, "Sparse files not supported\n"); + } + + /* set sparse dir should fail, check for 2k12 & 2k8 response */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "dir FSCTL_SET_SPARSE status"); + + smb2_util_close(tree, dirh); + smb2_deltree(tree, DNAME); + talloc_free(tmp_ctx); + return true; +} + +/* + * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse + * buffer to indicate whether the flag should be set or cleared. When sent + * without a buffer, it must be handled as if SetSparse=TRUE. + */ +static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */ + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + /* second non-SetSparse request shouldn't toggle sparse */ + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after 2nd set"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr after unset"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_set_oversize(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + uint8_t buf[100]; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + /* + * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER + * Windows still successfully processes the request. + */ + ZERO_ARRAY(buf); + buf[0] = 0xFF; /* attempt to set sparse */ + ioctl.smb2.in.out.data = buf; + ioctl.smb2.in.out.length = ARRAY_SIZE(buf); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_ARRAY(buf); /* clear sparse */ + ioctl.smb2.in.out.data = buf; + ioctl.smb2.in.out.length = ARRAY_SIZE(buf); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr after clear"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static NTSTATUS test_ioctl_qar_req(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + int64_t req_off, + int64_t req_len, + struct file_alloced_range_buf **_rsp, + uint64_t *_rsp_count) +{ + union smb_ioctl ioctl; + NTSTATUS status; + enum ndr_err_code ndr_ret; + struct file_alloced_range_buf far_buf; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES; + ioctl.smb2.in.max_output_response = 1024; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + far_buf.file_off = req_off; + far_buf.len = req_len; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &far_buf, + (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); + if (ndr_ret != NDR_ERR_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + if (!NT_STATUS_IS_OK(status)) { + goto err_out; + } + + if (ioctl.smb2.out.out.length == 0) { + goto done; + } + + if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) { + torture_comment(torture, "invalid qry_alloced rsp len: %zd:", + ioctl.smb2.out.out.length); + status = NT_STATUS_INVALID_VIEW_SIZE; + goto err_out; + } + + far_count = (ioctl.smb2.out.out.length / sizeof(far_buf)); + far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf, + far_count); + if (far_rsp == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + for (i = 0; i < far_count; i++) { + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &far_rsp[i], + (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf); + if (ndr_ret != NDR_ERR_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + /* move to next buffer */ + ioctl.smb2.out.out.data += sizeof(far_buf); + ioctl.smb2.out.out.length -= sizeof(far_buf); + } + +done: + *_rsp = far_rsp; + *_rsp_count = far_count; + status = NT_STATUS_OK; +err_out: + talloc_free(tmp_ctx); + return status; +} + +static bool test_ioctl_sparse_qar(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + /* zero length file, shouldn't have any ranges */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 0, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + /* write into the (now) sparse file at 4k offset */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 4096, /* off */ + 1024, /* len */ + 4096); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* + * Query range before write off. Whether it's allocated or not is FS + * dependent. NTFS deallocates chunks in 64K increments, but others + * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks. + */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + if (far_count == 0) { + torture_comment(torture, "FS deallocated 4K chunk\n"); + } else { + /* expect fully allocated */ + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset"); + torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len"); + } + + /* + * Query range before and past write, it should be allocated up to the + * end of the write. + */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 8192, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + /* FS dependent */ + if (far_rsp[0].file_off == 4096) { + /* 4K chunk unallocated */ + torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len"); + } else { + /* expect fully allocated */ + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset"); + torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + struct file_alloced_range_buf far_buf; + NTSTATUS status; + enum ndr_err_code ndr_ret; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + size_t old_len; + + /* zero length file, shouldn't have any ranges */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + /* no allocated ranges, no space for range response, should pass */ + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + far_buf.file_off = 0; + far_buf.len = 1024; + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &far_buf, + (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); + torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES"); + + /* write into the file at 4k offset */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + 1024, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* allocated range, no space for range response, should fail */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_BUFFER_TOO_SMALL, "qar no space"); + + /* oversize (2x) file_alloced_range_buf in request, should pass */ + ioctl.smb2.in.max_output_response = 1024; + old_len = ioctl.smb2.in.out.length; + ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out, + (ioctl.smb2.in.out.length * 2)); + torture_assert(torture, ok, "2x data buffer"); + memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data, + old_len); + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "qar too big"); + + /* no file_alloced_range_buf in request, should fail */ + data_blob_free(&ioctl.smb2.in.out); + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, "qar empty"); + + return true; +} + +bool test_ioctl_alternate_data_stream(struct torture_context *tctx) +{ + bool ret = false; + const char *fname = DNAME "\\test_stream_ioctl_dir"; + const char *sname = DNAME "\\test_stream_ioctl_dir:stream"; + NTSTATUS status; + struct smb2_create create = {}; + struct smb2_tree *tree = NULL; + struct smb2_handle h1 = {{0}}; + union smb_ioctl ioctl; + + if (!torture_smb2_connection(tctx, &tree)) { + torture_comment(tctx, "Initializing smb2 connection failed.\n"); + return false; + } + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_HIDDEN, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + h1 = create.out.file.handle; + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = sname, + }; + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = create.out.file.handle; + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = h1; + ioctl.smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID, + ioctl.smb2.in.max_output_response = 64; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + status = smb2_ioctl(tree, tctx, &ioctl.smb2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_ioctl failed\n"); + ret = true; + +done: + + smb2_util_close(tree, h1); + smb2_deltree(tree, DNAME); + return ret; +} + +/* + * 2.3.57 FSCTL_SET_ZERO_DATA Request + * + * How an implementation zeros data within a file is implementation-dependent. + * A file system MAY choose to deallocate regions of disk space that have been + * zeroed.<50> + * <50> + * ... NTFS might deallocate disk space in the file if the file is stored on an + * NTFS volume, and the file is sparse or compressed. It will free any allocated + * space in chunks of 64 kilobytes that begin at an offset that is a multiple of + * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte + * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not + * deallocated. + */ +static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + int64_t off, + int64_t beyond_final_zero) +{ + union smb_ioctl ioctl; + NTSTATUS status; + enum ndr_err_code ndr_ret; + struct file_zero_data_info zdata_info; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA; + ioctl.smb2.in.max_output_response = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + zdata_info.file_off = off; + zdata_info.beyond_final_zero = beyond_final_zero; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &zdata_info, + (ndr_push_flags_fn_t)ndr_push_file_zero_data_info); + if (ndr_ret != NDR_ERR_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + if (!NT_STATUS_IS_OK(status)) { + goto err_out; + } + + status = NT_STATUS_OK; +err_out: + talloc_free(tmp_ctx); + return status; +} + +bool test_ioctl_zero_data(struct torture_context *tctx) +{ + bool ret = true; + int offset, beyond_final_zero; + const char *filename; + NTSTATUS status; + struct smb2_create create = { }; + struct smb2_tree *tree = NULL; + + offset = torture_setting_int(tctx, "offset", -1); + + if (offset < 0) { + torture_fail(tctx, "Need to provide non-negative offset " + "through --option=torture:offset=NNN\n"); + return false; + } + + beyond_final_zero = torture_setting_int(tctx, "beyond_final_zero", + -1); + if (beyond_final_zero < 0) { + torture_fail(tctx, "Need to provide non-negative " + "'beyond final zero' through " + "--option=torture:beyond_final_zero=NNN\n"); + return false; + } + filename = torture_setting_string(tctx, "filename", NULL); + if (filename == NULL) { + torture_fail(tctx, "Need to provide filename through " + "--option=torture:filename=testfile\n"); + return false; + } + + if (!torture_smb2_connection(tctx, &tree)) { + torture_comment(tctx, "Initializing smb2 connection failed.\n"); + return false; + } + + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = 0; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = filename; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE failed.\n"); + + status = test_ioctl_zdata_req(tctx, tctx, tree, + create.out.file.handle, + offset, + beyond_final_zero); + torture_assert_ntstatus_ok_goto(tctx, + status, + ret, + done, + "FSCTL_SET_ZERO_DATA failed.\n"); + +done: + return ret; +} + +static bool test_ioctl_sparse_punch(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + /* zero (hole-punch) the data, without sparse flag */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + + /* expect fully allocated */ + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, 4096, + "unexpected far len"); + /* check that the data is now zeroed */ + ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096); + torture_assert(torture, ok, "non-sparse zeroed range"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* still fully allocated on NTFS, see note below for Samba */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + /* + * FS specific: Samba uses PUNCH_HOLE to zero the range, and + * subsequently uses fallocate() to allocate the punched range if the + * file is marked non-sparse and "strict allocate" is enabled. In both + * cases, the zeroed range will not be detected by SEEK_DATA, so the + * range won't be present in QAR responses until the file is marked + * non-sparse again. + */ + if (far_count == 0) { + torture_comment(torture, "non-sparse zeroed range disappeared " + "after marking sparse\n"); + } else { + /* NTFS: range remains fully allocated */ + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, 4096, + "unexpected far len"); + } + + /* zero (hole-punch) the data, _with_ sparse flag */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* the range should no longer be alloced */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096); + torture_assert(torture, ok, "sparse zeroed range"); + + /* remove sparse flag, this should "unsparse" the zeroed range */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + /* expect fully allocated */ + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, 4096, + "unexpected far len"); + + ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096); + torture_assert(torture, ok, "sparse zeroed range"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* + * Find the point at which a zeroed range in a sparse file is deallocated by the + * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k + * increments. Also check whether zeroed neighbours are merged for deallocation. + */ +static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t file_size; + uint64_t hlen; + uint64_t dealloc_chunk_len = 0; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file 1"); + + /* check for FS sparse file */ + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + file_size = 1024 * 1024; + + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* check allocated ranges, should be fully allocated */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + + /* punch holes in sizes of 1k increments */ + for (hlen = 0; hlen <= file_size; hlen += 4096) { + + /* punch a hole from zero to the current increment */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + hlen); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* ensure hole is zeroed, and pattern is consistent */ + ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, hlen, + file_size - hlen, hlen); + torture_assert(torture, ok, "allocated pattern range"); + + /* Check allocated ranges, hole might have been deallocated */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES"); + if ((hlen == file_size) && (far_count == 0)) { + /* hole covered entire file, deallocation occurred */ + dealloc_chunk_len = file_size; + break; + } + + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off != 0) { + /* + * We now know the hole punch length needed to trigger a + * deallocation on this FS... + */ + dealloc_chunk_len = hlen; + torture_comment(torture, "hole punch %ju@0 resulted in " + "deallocation of %ju@0\n", + (uintmax_t)hlen, + (uintmax_t)far_rsp[0].file_off); + torture_assert_u64_equal(torture, + file_size - far_rsp[0].len, + far_rsp[0].file_off, + "invalid alloced range"); + break; + } + } + + if (dealloc_chunk_len == 0) { + torture_comment(torture, "strange, this FS never deallocates" + "zeroed ranges in sparse files\n"); + return true; /* FS specific, not a failure */ + } + + /* + * Check whether deallocation occurs when the (now known) + * deallocation chunk size is punched via two ZERO_DATA requests. + * I.e. Does the FS merge the two ranges and deallocate the chunk? + * NTFS on Windows Server 2012 does not. + */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* divide dealloc chunk size by two, to use as punch length */ + hlen = dealloc_chunk_len >> 1; + + /* + * /half of dealloc chunk size 1M\ + * | | + * /offset 0 | /dealloc chunk size | + * |------------------ |-------------------|-------------------| + * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern | + */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + hlen); /* beyond final zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + hlen, /* off */ + dealloc_chunk_len); /* beyond final */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* ensure holes are zeroed, and pattern is consistent */ + ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len, + file_size - dealloc_chunk_len, dealloc_chunk_len); + torture_assert(torture, ok, "allocated pattern range"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + + if ((far_count == 0) && (dealloc_chunk_len == file_size)) { + torture_comment(torture, "holes merged for deallocation of " + "full file\n"); + return true; + } + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off == dealloc_chunk_len) { + torture_comment(torture, "holes merged for deallocation of " + "%ju chunk\n", (uintmax_t)dealloc_chunk_len); + torture_assert_u64_equal(torture, + file_size - far_rsp[0].len, + far_rsp[0].file_off, + "invalid alloced range"); + } else { + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected deallocation"); + torture_comment(torture, "holes not merged for deallocation\n"); + } + + smb2_util_close(tree, fh); + + /* + * Check whether an unwritten range is allocated when a sparse file is + * written to at an offset past the dealloc chunk size: + * + * /dealloc chunk size + * /offset 0 | + * |------------------ |-------------------| + * | unwritten | pattern | + */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file 1"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + ok = write_pattern(torture, tree, tmp_ctx, fh, + dealloc_chunk_len, /* off */ + 1024, /* len */ + dealloc_chunk_len); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off == 0) { + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + torture_comment(torture, "unwritten range fully allocated\n"); + } else { + torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len, + "unexpected deallocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, + "unexpected far len"); + torture_comment(torture, "unwritten range not allocated\n"); + } + + ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len, + 1024, dealloc_chunk_len); + torture_assert(torture, ok, "allocated pattern range"); + + /* unsparse, should now be fully allocated */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected deallocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* check whether a file with compression and sparse attrs can be deallocated */ +static bool test_ioctl_sparse_compressed(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t file_size = 1024 * 1024; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file 1"); + + /* check for FS sparse file and compression support */ + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "FS compression not supported\n"); + } + + /* set compression and write some data */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* set sparse - now sparse and compressed */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* check allocated ranges, should be fully alloced */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + + /* zero (hole-punch) all data, with sparse and compressed attrs */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* + * Windows Server 2012 still deallocates a zeroed range when a sparse + * file carries the compression attribute. + */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + if (far_count == 0) { + torture_comment(torture, "sparse & compressed file " + "deallocated after hole-punch\n"); + } else { + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + torture_comment(torture, "sparse & compressed file fully " + "allocated after hole-punch\n"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* + * Create a sparse file, then attempt to copy unallocated and allocated ranges + * into a target file using FSCTL_SRV_COPYCHUNK. + */ +static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */ + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + union smb_ioctl ioctl; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + /* check for FS sparse file support */ + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + smb2_util_close(tree, src_h); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + } + + ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, + 1, /* chunks */ + FNAME, + &src_h, 0, /* src file */ + SEC_RIGHTS_FILE_ALL, + FNAME2, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + torture_assert(torture, ok, "setup copy chunk error"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* start after dealloc_chunk_len, to create an unwritten sparse range */ + ok = write_pattern(torture, tree, tmp_ctx, src_h, + dealloc_chunk_len, /* off */ + 1024, /* len */ + dealloc_chunk_len); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* Skip test if 64k chunk is allocated - FS specific */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off == 0) { + torture_skip(torture, "unwritten range fully allocated\n"); + } + + torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, + "unexpected far len"); + + /* copy-chunk unallocated + written ranges into non-sparse dest */ + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = dealloc_chunk_len + 1024; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + dealloc_chunk_len + 1024); /* bytes written */ + torture_assert(torture, ok, "bad copy chunk response data"); + + ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len, + 1024, dealloc_chunk_len); + torture_assert(torture, ok, "copychunked range"); + + /* copied range should be allocated in non-sparse dest */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + + /* set dest as sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* zero (hole-punch) all data */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h, + 0, /* off */ + dealloc_chunk_len + 1024); + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* zeroed range might be deallocated */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + if (far_count == 0) { + /* FS specific (e.g. NTFS) */ + torture_comment(torture, "FS deallocates file on full-range " + "punch\n"); + } else { + /* FS specific (e.g. EXT4) */ + torture_comment(torture, "FS doesn't deallocate file on " + "full-range punch\n"); + } + ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, + dealloc_chunk_len + 1024); + torture_assert(torture, ok, "punched zeroed range"); + + /* copy-chunk again, this time with sparse dest */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + dealloc_chunk_len + 1024); /* bytes written */ + torture_assert(torture, ok, "bad copy chunk response data"); + + ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len, + 1024, dealloc_chunk_len); + torture_assert(torture, ok, "copychunked range"); + + /* copied range may be allocated in sparse dest */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h, + 0, /* off */ + dealloc_chunk_len + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + /* + * FS specific: sparse region may be unallocated in dest if copy-chunk + * is handled in a sparse preserving way - E.g. vfs_btrfs + * with BTRFS_IOC_CLONE_RANGE. + */ + if (far_rsp[0].file_off == dealloc_chunk_len) { + torture_comment(torture, "copy-chunk sparse range preserved\n"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, + "unexpected far len"); + } else { + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + int i; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + /* loop twice, without and with sparse attrib */ + for (i = 0; i <= 1; i++) { + union smb_fileinfo io; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + /* get size before & after. zero data should never change it */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "getinfo"); + torture_assert_int_equal(torture, (int)io.all_info2.out.size, + 4096, "size after IO"); + + /* valid 8 byte zero data, but after EOF */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 4096, /* off */ + 4104); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* valid 8 byte zero data, but after EOF */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 8192, /* off */ + 8200); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "getinfo"); + torture_assert_int_equal(torture, (int)io.all_info2.out.size, + 4096, "size after IO"); + + /* valid 0 byte zero data, without sparse flag */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 4095, /* off */ + 4095); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* INVALID off is past beyond_final_zero */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 4096, /* off */ + 4095); /* beyond_final_zero */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "invalid zero_data"); + + /* zero length QAR - valid */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 0, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + /* QAR after EOF - valid */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 4096, /* off */ + 1024, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, + true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_perms(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + smb2_util_close(tree, fh); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + } + + /* set sparse without WRITE_ATTR permission should succeed */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE + | SEC_STD_WRITE_DAC + | SEC_FILE_WRITE_EA)), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + smb2_util_close(tree, fh); + + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "sparse after set"); + smb2_util_close(tree, fh); + + /* attempt get sparse without READ_DATA permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA), + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse set"); + smb2_util_close(tree, fh); + + /* attempt to set sparse with only WRITE_ATTR permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + SEC_FILE_WRITE_ATTRIBUTE, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + smb2_util_close(tree, fh); + + /* attempt to set sparse with only WRITE_DATA permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + smb2_util_close(tree, fh); + + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "sparse after set"); + smb2_util_close(tree, fh); + + /* attempt to set sparse with only APPEND_DATA permission */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + SEC_FILE_APPEND_DATA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + smb2_util_close(tree, fh); + + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "sparse after set"); + smb2_util_close(tree, fh); + + /* attempt to set sparse with only WRITE_EA permission - should fail */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, + SEC_FILE_WRITE_EA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_SET_SPARSE permission"); + smb2_util_close(tree, fh); + + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse after set"); + smb2_util_close(tree, fh); + + /* attempt QAR with only READ_ATTR permission - should fail */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_READ_ATTRIBUTE, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 4096, /* off */ + 1024, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_QUERY_ALLOCATED_RANGES req passed"); + smb2_util_close(tree, fh); + + /* attempt QAR with only READ_DATA permission */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_READ_DATA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 1024, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + smb2_util_close(tree, fh); + + /* attempt QAR with only READ_EA permission - should fail */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_READ_EA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 4096, /* off */ + 1024, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "FSCTL_QUERY_ALLOCATED_RANGES req passed"); + smb2_util_close(tree, fh); + + /* setup file for ZERO_DATA permissions tests */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 8192, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + smb2_util_close(tree, fh); + + /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "zero_data permission"); + smb2_util_close(tree, fh); + + /* attempt ZERO_DATA with only WRITE_DATA permission */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + smb2_util_close(tree, fh); + + /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_APPEND_DATA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "zero_data permission"); + smb2_util_close(tree, fh); + + /* attempt ZERO_DATA with only WRITE_EA permission - should fail */ + ok = test_setup_open(torture, tree, tmp_ctx, + FNAME, &fh, SEC_FILE_WRITE_EA, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_ACCESS_DENIED, + "zero_data permission"); + smb2_util_close(tree, fh); + + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_lck(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + struct smb2_handle fh2; + NTSTATUS status; + uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */ + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh, + dealloc_chunk_len, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + smb2_util_close(tree, fh); + } + + /* open and lock via separate fh2 */ + status = torture_smb2_testfile(tree, FNAME, &fh2); + torture_assert_ntstatus_ok(torture, status, "2nd src open"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = fh2; + lck.in.locks = el; + el[0].offset = 0; + el[0].length = dealloc_chunk_len; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "lock"); + + /* set sparse while locked */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "sparse attr after set"); + + /* zero data over locked range should fail */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096); /* beyond_final_zero */ + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_FILE_LOCK_CONFLICT, + "zero_data locked"); + + /* QAR over locked range should pass */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES locked"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + 4096, + "unexpected far len"); + + /* zero data over range past EOF should pass */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + dealloc_chunk_len, /* off */ + dealloc_chunk_len + 4096); + torture_assert_ntstatus_ok(torture, status, + "zero_data past EOF locked"); + + /* QAR over range past EOF should pass */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + dealloc_chunk_len, /* off */ + 4096, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000001; + lck.in.file.handle = fh2; + lck.in.locks = el; + el[0].offset = 0; + el[0].length = dealloc_chunk_len; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(torture, status, "unlock"); + + smb2_util_close(tree, fh2); + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* alleviate QAR off-by-one bug paranoia - help me ob1 */ +static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */ + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, dealloc_chunk_len * 2, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + smb2_util_close(tree, fh); + } + + /* non-sparse QAR with range one before EOF */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len * 2 - 1, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len * 2 - 1, + "unexpected far len"); + + /* non-sparse QAR with range one after EOF */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len * 2 + 1, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len * 2, + "unexpected far len"); + + /* non-sparse QAR with range one after EOF from off=1 */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 1, /* off */ + dealloc_chunk_len * 2, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 1, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len * 2 - 1, + "unexpected far len"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* punch out second chunk */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + dealloc_chunk_len, /* off */ + dealloc_chunk_len * 2); + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* sparse QAR with range one before hole */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len - 1, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len - 1, + "unexpected far len"); + + /* sparse QAR with range one after hole */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len + 1, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len, + "unexpected far len"); + + /* sparse QAR with range one after hole from off=1 */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 1, /* off */ + dealloc_chunk_len, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 1, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len - 1, + "unexpected far len"); + + /* sparse QAR with range one before EOF from off=chunk_len-1 */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + dealloc_chunk_len - 1, /* off */ + dealloc_chunk_len, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, + dealloc_chunk_len - 1, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + 1, "unexpected far len"); + + /* sparse QAR with range one after EOF from off=chunk_len+1 */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + dealloc_chunk_len + 1, /* off */ + dealloc_chunk_len, /* len */ + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* test QAR with multi-range responses */ +static bool test_ioctl_sparse_qar_multi(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */ + uint64_t this_off; + int i; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, dealloc_chunk_len * 2, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + smb2_util_close(tree, fh); + } + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* each loop, write out two chunks and punch the first out */ + for (i = 0; i < 10; i++) { + this_off = i * dealloc_chunk_len * 2; + + ok = write_pattern(torture, tree, tmp_ctx, fh, + this_off, /* off */ + dealloc_chunk_len * 2, /* len */ + this_off); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + this_off, /* off */ + this_off + dealloc_chunk_len); + torture_assert_ntstatus_ok(torture, status, "zero_data"); + } + + /* should now have one separate region for each iteration */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, + 10 * dealloc_chunk_len * 2, + &far_rsp, &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + if (far_count == 1) { + torture_comment(torture, "this FS doesn't deallocate 64K" + "zeroed ranges in sparse files\n"); + return true; /* FS specific, not a failure */ + } + torture_assert_u64_equal(torture, far_count, 10, + "unexpected response len"); + for (i = 0; i < 10; i++) { + this_off = i * dealloc_chunk_len * 2; + + torture_assert_u64_equal(torture, far_rsp[i].file_off, + this_off + dealloc_chunk_len, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[i].len, + dealloc_chunk_len, + "unexpected far len"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + struct file_alloced_range_buf far_buf; + NTSTATUS status; + enum ndr_err_code ndr_ret; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh, + FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + /* no allocated ranges, no space for range response, should pass */ + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES; + ioctl.smb2.in.max_output_response = 1024; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + /* off + length wraps around to 511 */ + far_buf.file_off = 512; + far_buf.len = 0xffffffffffffffffLL; + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &far_buf, + (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); + torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_QUERY_ALLOCATED_RANGES overflow"); + + return true; +} + +static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb2_handle *fh, + bool *trim_support) +{ + NTSTATUS status; + union smb_fsinfo info; + + ZERO_STRUCT(info); + info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION; + info.generic.handle = *fh; + status = smb2_getinfo_fs(tree, tree, &info); + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + /* + * Windows < Server 2012, 8 etc. don't support this info level + * or the trim ioctl. Ignore the error and let the caller skip. + */ + *trim_support = false; + return NT_STATUS_OK; + } else if (!NT_STATUS_IS_OK(status)) { + return status; + } + + torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, " + "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n", + (unsigned)info.sector_size_info.out.logical_bytes_per_sector, + (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic, + (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf, + (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic, + (unsigned)info.sector_size_info.out.flags, + (unsigned)info.sector_size_info.out.byte_off_sector_align, + (unsigned)info.sector_size_info.out.byte_off_partition_align); + + if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) { + *trim_support = true; + } else { + *trim_support = false; + } + return NT_STATUS_OK; +} + +static bool test_setup_trim(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + uint32_t num_ranges, + struct smb2_handle *fh, + uint64_t file_size, + uint32_t desired_access, + struct fsctl_file_level_trim_req *trim_req, + union smb_ioctl *ioctl) +{ + bool ok; + + ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME, + fh, file_size, desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "src file create fill"); + + ZERO_STRUCTPN(ioctl); + ioctl->smb2.level = RAW_IOCTL_SMB2; + ioctl->smb2.in.file.handle = *fh; + ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM; + ioctl->smb2.in.max_output_response + = sizeof(struct fsctl_file_level_trim_rsp); + ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_STRUCTPN(trim_req); + /* leave key as zero for now. TODO test locking with differing keys */ + trim_req->num_ranges = num_ranges; + trim_req->ranges = talloc_zero_array(mem_ctx, + struct file_level_trim_range, + num_ranges); + torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges"); + + return true; +} + +static bool test_ioctl_trim_simple(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + union smb_ioctl ioctl; + bool trim_supported; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_file_level_trim_req trim_req; + struct fsctl_file_level_trim_rsp trim_rsp; + uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */ + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_trim(torture, tree, tmp_ctx, + 1, /* 1 range */ + &fh, 2 * trim_chunk_len, /* fill 128K file */ + SEC_RIGHTS_FILE_ALL, + &trim_req, + &ioctl); + if (!ok) { + torture_fail(torture, "setup trim error"); + } + + status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh, + &trim_supported); + torture_assert_ntstatus_ok(torture, status, "fsinfo"); + if (!trim_supported) { + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + torture_skip(torture, "trim not supported\n"); + } + + /* trim first chunk, leave second */ + trim_req.ranges[0].off = 0; + trim_req.ranges[0].len = trim_chunk_len; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req, + (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_fsctl_file_level_trim_req"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &trim_rsp, + (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_fsctl_file_level_trim_rsp"); + + torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, ""); + + /* second half of the file should remain consistent */ + ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len, + trim_chunk_len, trim_chunk_len); + torture_assert(torture, ok, "non-trimmed range inconsistent"); + + return true; +} + +static bool test_setup_dup_extents(struct torture_context *tctx, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb2_handle *src_h, + uint64_t src_size, + uint32_t src_desired_access, + struct smb2_handle *dest_h, + uint64_t dest_size, + uint32_t dest_desired_access, + struct fsctl_dup_extents_to_file *dup_ext_buf, + union smb_ioctl *ioctl) +{ + bool ok; + + ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME, + src_h, src_size, src_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(tctx, ok, "src file create fill"); + + ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2, + dest_h, dest_size, dest_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(tctx, ok, "dest file create fill"); + + ZERO_STRUCTPN(ioctl); + ioctl->smb2.level = RAW_IOCTL_SMB2; + ioctl->smb2.in.file.handle = *dest_h; + ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE; + ioctl->smb2.in.max_output_response = 0; + ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_STRUCTPN(dup_ext_buf); + smb2_push_handle(dup_ext_buf->source_fid, src_h); + + return true; +} + +static bool test_ioctl_dup_extents_simple(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* extend dest to match src len */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = + RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 4096; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + /* the file size shouldn't have been changed by this operation! */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 4096, "size after IO"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* reopen for pattern check */ + ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h, + SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL); + torture_assert_ntstatus_ok(tctx, status, "src open after dup"); + ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h, + SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL); + torture_assert_ntstatus_ok(tctx, status, "dest open after dup"); + + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0); + if (!ok) { + torture_fail(tctx, "inconsistent src file data"); + } + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(tctx, "inconsistent dest file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 32768; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); +#if 0 + /* + * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but + * passes against WS2016 RTM! + */ + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED, + "FSCTL_DUP_EXTENTS_TO_FILE"); +#endif + + /* the file sizes shouldn't have been changed */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = src_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 32768, "size after IO"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 32768; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + /* reissue ioctl, now with enough space */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + /* exceed src file len */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 32768 * 2; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + /* the file sizes shouldn't have been changed */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = src_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 32768, "size after IO"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 0; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + /* the file sizes shouldn't have been changed */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = src_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 32768, "size after IO"); + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = dest_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 0, "size after IO"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 0, /* filled after sparse flag */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING + | FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, + "block refcounting and sparse files not supported\n"); + } + + /* set sparse flag on src */ + status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE"); + + ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0); + torture_assert(tctx, ok, "write pattern"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 4096; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + /* + * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE + * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source + * is a non-sparse file. + */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING + | FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, + "block refcounting and sparse files not supported\n"); + } + + /* set sparse flag on dest */ + status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + /* + * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE + * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source + * is a non-sparse file. + */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 0, /* fill 4096 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* 0 byte dest file */ + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING + | FILE_SUPPORTS_SPARSE_FILES, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, + "block refcounting and sparse files not supported\n"); + } + + /* set sparse flag on src and dest */ + status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE"); + status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE"); + + ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0); + torture_assert(tctx, ok, "write pattern"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 4096; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + + /* reopen for pattern check */ + ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h, + SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL); + torture_assert_ntstatus_ok(tctx, status, "dest open ater dup"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + /* dest_h not needed for this test */ + smb2_util_close(tree, dest_h); + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* src and dest are the same file handle */ + ioctl.smb2.in.file.handle = src_h; + + /* no overlap between src and tgt */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 16384; + dup_ext_buf.byte_count = 16384; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + /* the file size shouldn't have been changed */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = src_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 32768, "size after IO"); + + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + talloc_free(tmp_ctx); + return true; +} + +/* + * unlike copy-chunk, dup extents doesn't support overlapping ranges between + * source and target. This makes it a *lot* cleaner to implement on the server. + */ +static bool +test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_fileinfo io; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + /* dest_h not needed for this test */ + smb2_util_close(tree, dest_h); + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* src and dest are the same file handle */ + ioctl.smb2.in.file.handle = src_h; + + /* 8K overlap between src and tgt */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 8192; + dup_ext_buf.byte_count = 16384; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + /* the file size and data should match beforehand */ + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = src_h; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + torture_assert_ntstatus_ok(tctx, status, "getinfo"); + torture_assert_int_equal(tctx, (int)io.all_info2.out.size, + 32768, "size after IO"); + + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + talloc_free(tmp_ctx); + return true; +} + +/* + * The compression tests won't run against Windows servers yet - ReFS doesn't + * (yet) offer support for compression. + */ +static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 0, /* filled after compressed flag */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING + | FILE_FILE_COMPRESSION, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, + "block refcounting and compressed files not supported\n"); + } + + /* set compressed flag on src */ + status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION"); + + ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0); + torture_assert(tctx, ok, "write pattern"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 4096; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + union smb_setfileinfo sinfo; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 4096, + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING + | FILE_FILE_COMPRESSION, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, + "block refcounting and compressed files not supported\n"); + } + + /* set compressed flag on dest */ + status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION"); + + /* extend dest */ + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = dest_h; + sinfo.end_of_file_info.in.size = 4096; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file"); + + /* copy all src file data */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + struct smb2_handle bogus_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 32768, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* open and close a file, keeping the handle as now a "bogus" handle */ + ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file", + &bogus_h, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(tctx, ok, "bogus file create fill"); + smb2_util_close(tree, bogus_h); + + /* bogus dest file handle */ + ioctl.smb2.in.file.handle = bogus_h; + + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 32768; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + /* reinstate dest, add bogus src file handle */ + ioctl.smb2.in.file.handle = dest_h; + smb2_push_handle(dup_ext_buf.source_fid, &bogus_h); + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE, + "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle src_h2; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + bool ok; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* dest pattern is different to src */ + ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768); + torture_assert(tctx, ok, "write pattern"); + + /* setup dup ext req, values used for locking */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 32768; + + /* open and lock the dup extents src file */ + status = torture_smb2_testfile(tree, FNAME, &src_h2); + torture_assert_ntstatus_ok(tctx, status, "2nd src open"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = src_h2; + lck.in.locks = el; + el[0].offset = dup_ext_buf.source_off; + el[0].length = dup_ext_buf.byte_count; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(tctx, status, "lock"); + + status = smb2_util_write(tree, src_h, + "conflicted", 0, sizeof("conflicted")); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_LOCK_CONFLICT, "file write"); + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + /* + * In contrast to copy-chunk, dup extents doesn't cause a lock conflict + * here. + */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000001; + lck.in.file.handle = src_h2; + lck.in.locks = el; + el[0].offset = dup_ext_buf.source_off; + el[0].length = dup_ext_buf.byte_count; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(tctx, status, "unlock"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE unlocked"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h2); + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + struct smb2_handle dest_h2; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct fsctl_dup_extents_to_file dup_ext_buf; + enum ndr_err_code ndr_ret; + bool ok; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + ok = test_setup_dup_extents(tctx, tree, tmp_ctx, + &src_h, 32768, /* fill 32768 byte src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, + SEC_RIGHTS_FILE_ALL, + &dup_ext_buf, + &ioctl); + if (!ok) { + torture_fail(tctx, "setup dup extents error"); + } + + status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h, + FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok); + torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + talloc_free(tmp_ctx); + torture_skip(tctx, "block refcounting not supported\n"); + } + + /* dest pattern is different to src */ + ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768); + torture_assert(tctx, ok, "write pattern"); + + /* setup dup ext req, values used for locking */ + dup_ext_buf.source_off = 0; + dup_ext_buf.target_off = 0; + dup_ext_buf.byte_count = 32768; + + /* open and lock the dup extents dest file */ + status = torture_smb2_testfile(tree, FNAME2, &dest_h2); + torture_assert_ntstatus_ok(tctx, status, "2nd src open"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = dest_h2; + lck.in.locks = el; + el[0].offset = dup_ext_buf.source_off; + el[0].length = dup_ext_buf.byte_count; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(tctx, status, "lock"); + + status = smb2_util_write(tree, dest_h, + "conflicted", 0, sizeof("conflicted")); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_FILE_LOCK_CONFLICT, "file write"); + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &dup_ext_buf, + (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file); + torture_assert_ndr_success(tctx, ndr_ret, + "ndr_push_fsctl_dup_extents_to_file"); + + /* + * In contrast to copy-chunk, dup extents doesn't cause a lock conflict + * here. + */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000001; + lck.in.file.handle = dest_h2; + lck.in.locks = el; + el[0].offset = dup_ext_buf.source_off; + el[0].length = dup_ext_buf.byte_count; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok(tctx, status, "unlock"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(tctx, status, + "FSCTL_DUP_EXTENTS_TO_FILE unlocked"); + + ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0); + if (!ok) { + torture_fail(tctx, "inconsistent file data"); + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + smb2_util_close(tree, dest_h2); + talloc_free(tmp_ctx); + return true; +} + +/* + basic regression test for BUG 14607 + https://bugzilla.samba.org/show_bug.cgi?id=14607 +*/ +static bool test_ioctl_bug14607(struct torture_context *torture, + struct smb2_tree *tree) +{ + TALLOC_CTX *tmp_ctx = talloc_new(tree); + uint32_t timeout_msec; + NTSTATUS status; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + status = smb2cli_ioctl(tree->session->transport->conn, + timeout_msec, + tree->session->smbXcli, + tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + tmp_ctx, + &out_input_buffer, + &out_output_buffer); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) || + NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED) || + NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) + { + torture_comment(torture, + "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8: %s\n", + nt_errstr(status)); + torture_skip(torture, "server doesn't support FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8\n"); + } + torture_assert_ntstatus_ok(torture, status, "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8"); + + torture_assert_int_equal(torture, out_output_buffer.length, 1, + "output length"); + torture_assert_int_equal(torture, out_output_buffer.data[0], 8, + "output buffer byte should be 8"); + + talloc_free(tmp_ctx); + return true; +} + +/* + basic regression test for BUG 14769 + https://bugzilla.samba.org/show_bug.cgi?id=14769 +*/ +static bool test_ioctl_bug14769(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + const char *fname = "bug14769"; + bool ret = false; + struct smb2_handle h; + struct smb2_ioctl ioctl; + struct smb2_close cl; + struct smb2_request *smb2arr[2] = { 0 }; + uint8_t tosend_msec = 200; + DATA_BLOB send_buf = { &tosend_msec, 1 }; + + /* Create a test file. */ + smb2_util_unlink(tree, fname); + status = torture_smb2_testfile(tree, fname, &h); + torture_assert_ntstatus_ok(torture, status, "create bug14769"); + + /* + * Send (not receive) the FSCTL. + * This should go async with a wait time of 200 msec. + */ + ZERO_STRUCT(ioctl); + ioctl.in.file.handle = h; + ioctl.in.function = FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP; + ioctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + ioctl.in.out = send_buf; + + smb2arr[0] = smb2_ioctl_send(tree, &ioctl); + torture_assert_goto(torture, + smb2arr[0] != NULL, + ret, + done, + "smb2_ioctl_send failed\n"); + /* Immediately send the close. */ + ZERO_STRUCT(cl); + cl.in.file.handle = h; + cl.in.flags = 0; + smb2arr[1] = smb2_close_send(tree, &cl); + torture_assert_goto(torture, + smb2arr[1] != NULL, + ret, + done, + "smb2_close_send failed\n"); + + /* Now get the FSCTL reply. */ + /* + * If we suffer from bug #14769 this will fail as + * the ioctl will return with NT_STATUS_FILE_CLOSED, + * as the close will have closed the handle without + * waiting for the ioctl to complete. The server shouldn't + * complete the close until the ioctl finishes. + */ + status = smb2_ioctl_recv(smb2arr[0], tree, &ioctl); + torture_assert_ntstatus_ok_goto(torture, + status, + ret, + done, + "smb2_ioctl_recv failed\n"); + + /* Followed by the close reply. */ + status = smb2_close_recv(smb2arr[1], &cl); + torture_assert_ntstatus_ok_goto(torture, + status, + ret, + done, + "smb2_ioctl_close failed\n"); + ret = true; + + done: + smb2_util_unlink(tree, fname); + return ret; +} + +/* + basic regression test for BUG 14788, + with FSCTL_VALIDATE_NEGOTIATE_INFO + https://bugzilla.samba.org/show_bug.cgi?id=14788 +*/ +static bool test_ioctl_bug14788_VALIDATE_NEGOTIATE(struct torture_context *torture, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(torture, "host", NULL); + const char *share = torture_setting_string(torture, "share", NULL); + const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm"); + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_tree *tree = NULL; + struct smb2_session *session = NULL; + uint16_t noperm_flags = 0; + const char *noperm_unc = NULL; + struct smb2_tree *noperm_tree = NULL; + uint32_t timeout_msec; + struct tevent_req *subreq = NULL; + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(torture, "Can't test without SMB 3 support"); + } + + options = transport0->options; + options.client_guid = GUID_random(); + options.min_protocol = PROTOCOL_SMB3_00; + options.max_protocol = PROTOCOL_SMB3_02; + + status = smb2_connect(torture, + host, + lpcfg_smb_ports(torture->lp_ctx), + share, + lpcfg_resolve_context(torture->lp_ctx), + credentials, + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed"); + session = tree->session; + transport = session->transport; + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + subreq = smb2cli_validate_negotiate_info_send(torture, + torture->ev, + transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli); + torture_assert(torture, + tevent_req_poll_ntstatus(subreq, torture->ev, &status), + "tevent_req_poll_ntstatus"); + status = smb2cli_validate_negotiate_info_recv(subreq); + torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info"); + + noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share); + torture_assert(torture, noperm_unc != NULL, "talloc_asprintf"); + + noperm_tree = smb2_tree_init(session, torture, false); + torture_assert(torture, noperm_tree != NULL, "smb2_tree_init"); + + status = smb2cli_raw_tcon(transport->conn, + SMB2_HDR_FLAG_SIGNED, + 0, /* clear_flags */ + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + noperm_flags, + noperm_unc); + if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) { + torture_skip(torture, talloc_asprintf(torture, + "noperm_unc[%s] %s", + noperm_unc, nt_errstr(status))); + } + torture_assert_ntstatus_ok(torture, status, + talloc_asprintf(torture, + "smb2cli_tcon(%s)", + noperm_unc)); + + subreq = smb2cli_validate_negotiate_info_send(torture, + torture->ev, + transport->conn, + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli); + torture_assert(torture, + tevent_req_poll_ntstatus(subreq, torture->ev, &status), + "tevent_req_poll_ntstatus"); + status = smb2cli_validate_negotiate_info_recv(subreq); + torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info noperm"); + + return true; +} + +/* + basic regression test for BUG 14788, + with FSCTL_QUERY_NETWORK_INTERFACE_INFO + https://bugzilla.samba.org/show_bug.cgi?id=14788 +*/ +static bool test_ioctl_bug14788_NETWORK_INTERFACE(struct torture_context *torture, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(torture, "host", NULL); + const char *share = torture_setting_string(torture, "share", NULL); + const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm"); + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_tree *tree = NULL; + struct smb2_session *session = NULL; + uint16_t noperm_flags = 0; + const char *noperm_unc = NULL; + struct smb2_tree *noperm_tree = NULL; + uint32_t timeout_msec; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(torture, "Can't test without SMB 3 support"); + } + + options = transport0->options; + options.client_guid = GUID_random(); + options.min_protocol = PROTOCOL_SMB3_00; + options.max_protocol = PROTOCOL_SMB3_02; + + status = smb2_connect(torture, + host, + lpcfg_smb_ports(torture->lp_ctx), + share, + lpcfg_resolve_context(torture->lp_ctx), + credentials, + &tree, + torture->ev, + &options, + lpcfg_socket_options(torture->lp_ctx), + lpcfg_gensec_settings(torture, torture->lp_ctx) + ); + torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed"); + session = tree->session; + transport = session->transport; + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + /* + * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + UINT16_MAX, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* is being UINT64_MAX and + * in_max_output_length = 1. + * + * This demonstrates NT_STATUS_BUFFER_TOO_SMALL + * if the server is not able to return the + * whole response buffer to the client. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_BUFFER_TOO_SMALL, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* not being UINT64_MAX. + * + * This gives INVALID_PARAMETER instead + * of FILE_CLOSED. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli, + INT64_MAX, /* in_fid_persistent */ + INT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + UINT16_MAX, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* not being UINT64_MAX and + * in_max_output_length = 1. + * + * This proves INVALID_PARAMETER instead + * of BUFFER_TOO_SMALL. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli, + INT64_MAX, /* in_fid_persistent */ + INT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share); + torture_assert(torture, noperm_unc != NULL, "talloc_asprintf"); + + noperm_tree = smb2_tree_init(session, torture, false); + torture_assert(torture, noperm_tree != NULL, "smb2_tree_init"); + + status = smb2cli_raw_tcon(transport->conn, + SMB2_HDR_FLAG_SIGNED, + 0, /* clear_flags */ + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + noperm_flags, + noperm_unc); + if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) { + torture_skip(torture, talloc_asprintf(torture, + "noperm_unc[%s] %s", + noperm_unc, nt_errstr(status))); + } + torture_assert_ntstatus_ok(torture, status, + talloc_asprintf(torture, + "smb2cli_tcon(%s)", + noperm_unc)); + + /* + * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + UINT16_MAX, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* is being UINT64_MAX and + * in_max_output_length = 1. + * + * This demonstrates NT_STATUS_BUFFER_TOO_SMALL + * if the server is not able to return the + * whole response buffer to the client. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_BUFFER_TOO_SMALL, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* not being UINT64_MAX. + * + * This gives INVALID_PARAMETER instead + * of FILE_CLOSED. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + INT64_MAX, /* in_fid_persistent */ + INT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + UINT16_MAX, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + /* + * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO, + * with file_id_* not being UINT64_MAX and + * in_max_output_length = 1. + * + * This proves INVALID_PARAMETER instead + * of BUFFER_TOO_SMALL. + */ + status = smb2cli_ioctl(transport->conn, + timeout_msec, + session->smbXcli, + noperm_tree->smbXcli, + INT64_MAX, /* in_fid_persistent */ + INT64_MAX, /* in_fid_volatile */ + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + torture, + &out_input_buffer, + &out_output_buffer); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO"); + + return true; +} + +/* + * testing of SMB2 ioctls + */ +struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ioctl"); + struct torture_suite *bug14788 = torture_suite_create(ctx, "bug14788"); + + torture_suite_add_1smb2_test(suite, "shadow_copy", + test_ioctl_get_shadow_copy); + torture_suite_add_1smb2_test(suite, "req_resume_key", + test_ioctl_req_resume_key); + torture_suite_add_1smb2_test(suite, "req_two_resume_keys", + test_ioctl_req_two_resume_keys); + torture_suite_add_1smb2_test(suite, "copy_chunk_simple", + test_ioctl_copy_chunk_simple); + torture_suite_add_1smb2_test(suite, "copy_chunk_multi", + test_ioctl_copy_chunk_multi); + torture_suite_add_1smb2_test(suite, "copy_chunk_tiny", + test_ioctl_copy_chunk_tiny); + torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite", + test_ioctl_copy_chunk_over); + torture_suite_add_1smb2_test(suite, "copy_chunk_append", + test_ioctl_copy_chunk_append); + torture_suite_add_1smb2_test(suite, "copy_chunk_limits", + test_ioctl_copy_chunk_limits); + torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock", + test_ioctl_copy_chunk_src_lck); + torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock", + test_ioctl_copy_chunk_dest_lck); + torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key", + test_ioctl_copy_chunk_bad_key); + torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest", + test_ioctl_copy_chunk_src_is_dest); + torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap", + test_ioctl_copy_chunk_src_is_dest_overlap); + torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access", + test_ioctl_copy_chunk_bad_access); + torture_suite_add_1smb2_test(suite, "copy_chunk_write_access", + test_ioctl_copy_chunk_write_access); + torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed", + test_ioctl_copy_chunk_src_exceed); + torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi", + test_ioctl_copy_chunk_src_exceed_multi); + torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest", + test_ioctl_copy_chunk_sparse_dest); + torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz", + test_ioctl_copy_chunk_max_output_sz); + torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length", + test_ioctl_copy_chunk_zero_length); + torture_suite_add_1smb2_test(suite, "copy-chunk streams", + test_copy_chunk_streams); + torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares", + test_copy_chunk_across_shares); + torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2", + test_copy_chunk_across_shares2); + torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3", + test_copy_chunk_across_shares3); + torture_suite_add_1smb2_test(suite, "compress_file_flag", + test_ioctl_compress_file_flag); + torture_suite_add_1smb2_test(suite, "compress_dir_inherit", + test_ioctl_compress_dir_inherit); + torture_suite_add_1smb2_test(suite, "compress_invalid_format", + test_ioctl_compress_invalid_format); + torture_suite_add_1smb2_test(suite, "compress_invalid_buf", + test_ioctl_compress_invalid_buf); + torture_suite_add_1smb2_test(suite, "compress_query_file_attr", + test_ioctl_compress_query_file_attr); + torture_suite_add_1smb2_test(suite, "compress_create_with_attr", + test_ioctl_compress_create_with_attr); + torture_suite_add_1smb2_test(suite, "compress_inherit_disable", + test_ioctl_compress_inherit_disable); + torture_suite_add_1smb2_test(suite, "compress_set_file_attr", + test_ioctl_compress_set_file_attr); + torture_suite_add_1smb2_test(suite, "compress_perms", + test_ioctl_compress_perms); + torture_suite_add_1smb2_test(suite, "compress_notsup_get", + test_ioctl_compress_notsup_get); + torture_suite_add_1smb2_test(suite, "compress_notsup_set", + test_ioctl_compress_notsup_set); + torture_suite_add_1smb2_test(suite, "network_interface_info", + test_ioctl_network_interface_info); + torture_suite_add_1smb2_test(suite, "sparse_file_flag", + test_ioctl_sparse_file_flag); + torture_suite_add_1smb2_test(suite, "sparse_file_attr", + test_ioctl_sparse_file_attr); + torture_suite_add_1smb2_test(suite, "sparse_dir_flag", + test_ioctl_sparse_dir_flag); + torture_suite_add_1smb2_test(suite, "sparse_set_nobuf", + test_ioctl_sparse_set_nobuf); + torture_suite_add_1smb2_test(suite, "sparse_set_oversize", + test_ioctl_sparse_set_oversize); + torture_suite_add_1smb2_test(suite, "sparse_qar", + test_ioctl_sparse_qar); + torture_suite_add_1smb2_test(suite, "sparse_qar_malformed", + test_ioctl_sparse_qar_malformed); + torture_suite_add_1smb2_test(suite, "sparse_punch", + test_ioctl_sparse_punch); + torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc", + test_ioctl_sparse_hole_dealloc); + torture_suite_add_1smb2_test(suite, "sparse_compressed", + test_ioctl_sparse_compressed); + torture_suite_add_1smb2_test(suite, "sparse_copy_chunk", + test_ioctl_sparse_copy_chunk); + torture_suite_add_1smb2_test(suite, "sparse_punch_invalid", + test_ioctl_sparse_punch_invalid); + torture_suite_add_1smb2_test(suite, "sparse_perms", + test_ioctl_sparse_perms); + torture_suite_add_1smb2_test(suite, "sparse_lock", + test_ioctl_sparse_lck); + torture_suite_add_1smb2_test(suite, "sparse_qar_ob1", + test_ioctl_sparse_qar_ob1); + torture_suite_add_1smb2_test(suite, "sparse_qar_multi", + test_ioctl_sparse_qar_multi); + torture_suite_add_1smb2_test(suite, "sparse_qar_overflow", + test_ioctl_sparse_qar_overflow); + torture_suite_add_1smb2_test(suite, "trim_simple", + test_ioctl_trim_simple); + torture_suite_add_1smb2_test(suite, "dup_extents_simple", + test_ioctl_dup_extents_simple); + torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest", + test_ioctl_dup_extents_len_beyond_dest); + torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src", + test_ioctl_dup_extents_len_beyond_src); + torture_suite_add_1smb2_test(suite, "dup_extents_len_zero", + test_ioctl_dup_extents_len_zero); + torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src", + test_ioctl_dup_extents_sparse_src); + torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest", + test_ioctl_dup_extents_sparse_dest); + torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both", + test_ioctl_dup_extents_sparse_both); + torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest", + test_ioctl_dup_extents_src_is_dest); + torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap", + test_ioctl_dup_extents_src_is_dest_overlap); + torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src", + test_ioctl_dup_extents_compressed_src); + torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest", + test_ioctl_dup_extents_compressed_dest); + torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle", + test_ioctl_dup_extents_bad_handle); + torture_suite_add_1smb2_test(suite, "dup_extents_src_lock", + test_ioctl_dup_extents_src_lck); + torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock", + test_ioctl_dup_extents_dest_lck); + torture_suite_add_1smb2_test(suite, "bug14607", + test_ioctl_bug14607); + torture_suite_add_1smb2_test(suite, "bug14769", + test_ioctl_bug14769); + + torture_suite_add_1smb2_test(bug14788, "VALIDATE_NEGOTIATE", + test_ioctl_bug14788_VALIDATE_NEGOTIATE); + torture_suite_add_1smb2_test(bug14788, "NETWORK_INTERFACE", + test_ioctl_bug14788_NETWORK_INTERFACE); + torture_suite_add_suite(suite, bug14788); + + suite->description = talloc_strdup(suite, "SMB2-IOCTL tests"); + + return suite; +} + diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c new file mode 100644 index 0000000..30bbefd --- /dev/null +++ b/source4/torture/smb2/lease.c @@ -0,0 +1,4819 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 leases + + Copyright (C) Zachary Loafman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" +#include "libcli/smb/smbXcli_base.h" +#include "libcli/security/security.h" +#include "lib/param/param.h" +#include "lease_break_handler.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)(correct)); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \ + do { \ + CHECK_VAL((__io)->out.lease_response.lease_version, 1); \ + if (__oplevel) { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \ + } else { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \ + CHECK_VAL((__io)->out.lease_response.lease_state, 0); \ + } \ + \ + CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \ + CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \ + } while(0) + +#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \ + do { \ + CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \ + if (__oplevel) { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \ + } else { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \ + } \ + \ + CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \ + if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \ + CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \ + CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \ + } \ + CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \ + } while(0) + +static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull; +static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull; +static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull; +static const uint64_t LEASE4 = 0xBAD0FFEDD00DF00Dull; + +#define NREQUEST_RESULTS 8 +static const char *request_results[NREQUEST_RESULTS][2] = { + { "", "" }, + { "R", "R" }, + { "H", "" }, + { "W", "" }, + { "RH", "RH" }, + { "RW", "RW" }, + { "HW", "" }, + { "RHW", "RHW" }, +}; + +static bool test_lease_request(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname = "lease_request.dat"; + const char *fname2 = "lease_request.2.dat"; + const char *sname = "lease_request.dat:stream"; + const char *dname = "lease_request.dir"; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname2); + smb2_util_rmdir(tree, dname); + + /* Win7 is happy to grant RHW leases on files. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + + /* But will reject leases on directories. */ + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree, io.out.file.handle); + } + + /* Also rejects multiple files leased under the same key. */ + smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* And grants leases on streams (with separate leasekey). */ + smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + h2 = io.out.file.handle; + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE2, 0); + smb2_util_close(tree, h2); + + smb2_util_close(tree, h1); + + /* Now see what combos are actually granted. */ + for (i = 0; i < NREQUEST_RESULTS; i++) { + torture_comment(tctx, "Requesting lease type %s(%x)," + " expecting %s(%x)\n", + request_results[i][0], smb2_util_lease_state(request_results[i][0]), + request_results[i][1], smb2_util_lease_state(request_results[i][1])); + smb2_lease_create(&io, &ls, false, fname, LEASE1, + smb2_util_lease_state(request_results[i][0])); + status = smb2_create(tree, mem_ctx, &io); + h2 = io.out.file.handle; + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, request_results[i][1], true, LEASE1, 0); + smb2_util_close(tree, io.out.file.handle); + } + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname2); + smb2_util_rmdir(tree, dname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_upgrade(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h = {{0}}; + struct smb2_handle hnew = {{0}}; + NTSTATUS status; + const char *fname = "lease_upgrade.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Grab a RH lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); + h = io.out.file.handle; + + /* Upgrades (sidegrades?) to RW leave us with an RH. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); + hnew = io.out.file.handle; + + smb2_util_close(tree, hnew); + + /* Upgrade to RHW lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + hnew = io.out.file.handle; + + smb2_util_close(tree, h); + h = hnew; + + /* Attempt to downgrade - original lease state is maintained. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + hnew = io.out.file.handle; + + smb2_util_close(tree, hnew); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * upgrade2 test. + * full matrix of lease upgrade combinations + * (non-contended case) + * + * The summary of the behaviour is this: + * ------------------------------------- + * An uncontended lease upgrade results in a change + * if and only if the requested lease state is + * - valid, and + * - strictly a superset of the lease state already held. + * + * In that case the resulting lease state is the one + * requested in the upgrade. + */ +struct lease_upgrade2_test { + const char *initial; + const char *upgrade_to; + const char *expected; +}; + +#define NUM_LEASE_TYPES 5 +#define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES ) +struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = { + { "", "", "" }, + { "", "R", "R" }, + { "", "RH", "RH" }, + { "", "RW", "RW" }, + { "", "RWH", "RWH" }, + + { "R", "", "R" }, + { "R", "R", "R" }, + { "R", "RH", "RH" }, + { "R", "RW", "RW" }, + { "R", "RWH", "RWH" }, + + { "RH", "", "RH" }, + { "RH", "R", "RH" }, + { "RH", "RH", "RH" }, + { "RH", "RW", "RH" }, + { "RH", "RWH", "RWH" }, + + { "RW", "", "RW" }, + { "RW", "R", "RW" }, + { "RW", "RH", "RW" }, + { "RW", "RW", "RW" }, + { "RW", "RWH", "RWH" }, + + { "RWH", "", "RWH" }, + { "RWH", "R", "RWH" }, + { "RWH", "RH", "RWH" }, + { "RWH", "RW", "RWH" }, + { "RWH", "RWH", "RWH" }, +}; + +static bool test_lease_upgrade2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h, hnew; + NTSTATUS status; + struct smb2_create io; + struct smb2_lease ls; + const char *fname = "lease_upgrade2.dat"; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + for (i = 0; i < NUM_UPGRADE_TESTS; i++) { + struct lease_upgrade2_test t = lease_upgrade2_tests[i]; + + smb2_util_unlink(tree, fname); + + /* Grab a lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, t.initial, true, LEASE1, 0); + h = io.out.file.handle; + + /* Upgrade. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, t.expected, true, LEASE1, 0); + hnew = io.out.file.handle; + + smb2_util_close(tree, hnew); + smb2_util_close(tree, h); + } + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + + +/** + * upgrade3: + * full matrix of lease upgrade combinations + * (contended case) + * + * We start with 2 leases, and check how one can + * be upgraded + * + * The summary of the behaviour is this: + * ------------------------------------- + * + * If we have two leases (lease1 and lease2) on the same file, + * then attempt to upgrade lease1 results in a change if and only + * if the requested lease state: + * - is valid, + * - is strictly a superset of lease1, and + * - can held together with lease2. + * + * In that case, the resulting lease state of the upgraded lease1 + * is the state requested in the upgrade. lease2 is not broken + * and remains unchanged. + * + * Note that this contrasts the case of directly opening with + * an initial requested lease state, in which case you get that + * portion of the requested state that can be shared with the + * already existing leases (or the states that they get broken to). + */ +struct lease_upgrade3_test { + const char *held1; + const char *held2; + const char *upgrade_to; + const char *upgraded_to; +}; + +#define NUM_UPGRADE3_TESTS ( 20 ) +struct lease_upgrade3_test lease_upgrade3_tests[NUM_UPGRADE3_TESTS] = { + {"R", "R", "", "R" }, + {"R", "R", "R", "R" }, + {"R", "R", "RW", "R" }, + {"R", "R", "RH", "RH" }, + {"R", "R", "RHW", "R" }, + + {"R", "RH", "", "R" }, + {"R", "RH", "R", "R" }, + {"R", "RH", "RW", "R" }, + {"R", "RH", "RH", "RH" }, + {"R", "RH", "RHW", "R" }, + + {"RH", "R", "", "RH" }, + {"RH", "R", "R", "RH" }, + {"RH", "R", "RW", "RH" }, + {"RH", "R", "RH", "RH" }, + {"RH", "R", "RHW", "RH" }, + + {"RH", "RH", "", "RH" }, + {"RH", "RH", "R", "RH" }, + {"RH", "RH", "RW", "RH" }, + {"RH", "RH", "RH", "RH" }, + {"RH", "RH", "RHW", "RH" }, +}; + +static bool test_lease_upgrade3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h, h2, hnew; + NTSTATUS status; + struct smb2_create io; + struct smb2_lease ls; + const char *fname = "lease_upgrade3.dat"; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + smb2_util_unlink(tree, fname); + + for (i = 0; i < NUM_UPGRADE3_TESTS; i++) { + struct lease_upgrade3_test t = lease_upgrade3_tests[i]; + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* grab first lease */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.held1)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, t.held1, true, LEASE1, 0); + h = io.out.file.handle; + + /* grab second lease */ + smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(t.held2)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, t.held2, true, LEASE2, 0); + h2 = io.out.file.handle; + + /* no break has happened */ + CHECK_VAL(lease_break_info.count, 0); + CHECK_VAL(lease_break_info.failures, 0); + + /* try to upgrade lease1 */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, t.upgraded_to, true, LEASE1, 0); + hnew = io.out.file.handle; + + /* no break has happened */ + CHECK_VAL(lease_break_info.count, 0); + CHECK_VAL(lease_break_info.failures, 0); + + smb2_util_close(tree, hnew); + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + } + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + + + +/* + break_results should be read as "held lease, new lease, hold broken to, new + grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2 + tries for RW, key1 will be broken to RH (in this case, not broken at all) + and key2 will be granted R. + + Note: break_results only includes things that Win7 will actually grant (see + request_results above). + */ +#define NBREAK_RESULTS 16 +static const char *break_results[NBREAK_RESULTS][4] = { + {"R", "R", "R", "R"}, + {"R", "RH", "R", "RH"}, + {"R", "RW", "R", "R"}, + {"R", "RHW", "R", "RH"}, + + {"RH", "R", "RH", "R"}, + {"RH", "RH", "RH", "RH"}, + {"RH", "RW", "RH", "R"}, + {"RH", "RHW", "RH", "RH"}, + + {"RW", "R", "R", "R"}, + {"RW", "RH", "R", "RH"}, + {"RW", "RW", "R", "R"}, + {"RW", "RHW", "R", "RH"}, + + {"RHW", "R", "RH", "R"}, + {"RHW", "RH", "RH", "RH"}, + {"RHW", "RW", "RH", "R"}, + {"RHW", "RHW", "RH", "RH"}, +}; + +static bool test_lease_break(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h, h2, h3; + NTSTATUS status; + const char *fname = "lease_break.dat"; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + smb2_util_unlink(tree, fname); + + for (i = 0; i < NBREAK_RESULTS; i++) { + const char *held = break_results[i][0]; + const char *contend = break_results[i][1]; + const char *brokento = break_results[i][2]; + const char *granted = break_results[i][3]; + torture_comment(tctx, "Hold %s(%x), requesting %s(%x), " + "expecting break to %s(%x) and grant of %s(%x)\n", + held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend), + brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted)); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, held, true, LEASE1, 0); + + /* Possibly contend lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, granted, true, LEASE2, 0); + + if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { + CHECK_BREAK_INFO(held, brokento, LEASE1); + } else { + CHECK_NO_BREAK(tctx); + } + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + Now verify that an attempt to upgrade LEASE1 results in no + break and no change in LEASE1. + */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, brokento, true, LEASE1, 0); + CHECK_VAL(lease_break_info.count, 0); + CHECK_VAL(lease_break_info.failures, 0); + + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + } + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_nobreakself(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname = "lease_nobreakself.dat"; + bool ret = true; + uint32_t caps; + char c = 0; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Win7 is happy to grant RHW leases on files. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + + smb2_lease_create(&io, &ls, false, fname, LEASE2, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_LEASE(&io, "R", true, LEASE2, 0); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + /* Make sure we don't break ourselves on write */ + + status = smb2_util_write(tree, h1, &c, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BREAK_INFO("R", "", LEASE2); + + /* Try the other way round. First, upgrade LEASE2 to R again */ + + smb2_lease_create(&io, &ls, false, fname, LEASE2, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "R", true, LEASE2, 0); + smb2_util_close(tree, io.out.file.handle); + + /* Now break LEASE1 via h2 */ + + torture_reset_lease_break_info(tctx, &lease_break_info); + status = smb2_util_write(tree, h2, &c, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BREAK_INFO("R", "", LEASE1); + + /* .. and break LEASE2 via h1 */ + + torture_reset_lease_break_info(tctx, &lease_break_info); + status = smb2_util_write(tree, h1, &c, 0, 1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BREAK_INFO("R", "", LEASE2); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_statopen(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname = "lease_statopen.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Create file. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + smb2_util_close(tree, h1); + + /* Stat open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, 0, LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = FILE_READ_ATTRIBUTES; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + /* Ensure non-stat open doesn't break and gets same lease + state as existing stat open. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + smb2_util_close(tree, h1); + + /* Open with conflicting lease. stat open should break down to RH */ + smb2_lease_create(&io, &ls, false, fname, LEASE2, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_statopen2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + NTSTATUS status; + const char *fname = "lease_statopen2.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + torture_reset_lease_break_info(tctx, &lease_break_info); + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* Open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + /* Stat open */ + ZERO_STRUCT(io); + io.in.desired_access = FILE_READ_ATTRIBUTES; + io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.fname = fname; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h2 = io.out.file.handle; + + /* Open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h3 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + +done: + if (!smb2_util_handle_empty(h3)) { + smb2_util_close(tree, h3); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_statopen3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname = "lease_statopen3.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + torture_reset_lease_break_info(tctx, &lease_break_info); + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* Stat open */ + ZERO_STRUCT(io); + io.in.desired_access = FILE_READ_ATTRIBUTES; + io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.fname = fname; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = io.out.file.handle; + + /* Open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h2 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_statopen4_do(struct torture_context *tctx, + struct smb2_tree *tree, + uint32_t access_mask, + bool expect_stat_open) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + NTSTATUS status; + const char *fname = "lease_statopen2.dat"; + bool ret = true; + + /* Open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = SEC_FILE_ALL; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + /* Stat open */ + ZERO_STRUCT(io); + io.in.desired_access = access_mask; + io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.fname = fname; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h2 = io.out.file.handle; + + if (expect_stat_open) { + CHECK_NO_BREAK(tctx); + if (!ret) { + goto done; + } + } else { + CHECK_VAL(lease_break_info.count, 1); + if (!ret) { + goto done; + } + /* + * Don't bother checking the lease state of an additional open + * below... + */ + goto done; + } + + /* Open file with RWH lease. */ + smb2_lease_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + io.in.desired_access = SEC_FILE_WRITE_DATA; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h3 = io.out.file.handle; + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + +done: + if (!smb2_util_handle_empty(h3)) { + smb2_util_close(tree, h3); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_statopen4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "lease_statopen4.dat"; + struct smb2_handle h1 = {{0}}; + uint32_t caps; + size_t i; + NTSTATUS status; + bool ret = true; + struct { + uint32_t access_mask; + bool expect_stat_open; + } tests[] = { + { + .access_mask = FILE_READ_DATA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_WRITE_DATA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_READ_EA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_WRITE_EA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_EXECUTE, + .expect_stat_open = false, + }, + { + .access_mask = FILE_READ_ATTRIBUTES, + .expect_stat_open = true, + }, + { + .access_mask = FILE_WRITE_ATTRIBUTES, + .expect_stat_open = true, + }, + { + .access_mask = DELETE_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = READ_CONTROL_ACCESS, + .expect_stat_open = true, + }, + { + .access_mask = WRITE_DAC_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = WRITE_OWNER_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = SYNCHRONIZE_ACCESS, + .expect_stat_open = true, + }, + }; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + torture_reset_lease_break_info(tctx, &lease_break_info); + + ret = test_lease_statopen4_do(tctx, + tree, + tests[i].access_mask, + tests[i].expect_stat_open); + if (ret == true) { + continue; + } + torture_result(tctx, TORTURE_FAIL, + "test %zu: access_mask: %s, " + "expect_stat_open: %s\n", + i, + get_sec_mask_str(tree, tests[i].access_mask), + tests[i].expect_stat_open ? "yes" : "no"); + goto done; + } + +done: + smb2_util_unlink(tree, fname); + return ret; +} + +static void torture_oplock_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + ZERO_STRUCT(br); + status = smb2_break_recv(req, &br); + if (!NT_STATUS_IS_OK(status)) + lease_break_info.oplock_failures++; + + return; +} + +/* a oplock break request handler */ +static bool torture_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_request *req; + struct smb2_break br; + + lease_break_info.oplock_handle = *handle; + lease_break_info.oplock_level = level; + lease_break_info.oplock_count++; + + ZERO_STRUCT(br); + br.in.file.handle = *handle; + br.in.oplock_level = level; + + if (lease_break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) { + req = smb2_break_send(tree, &br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + } + lease_break_info.held_oplock_level = level; + + return true; +} + +#define NOPLOCK_RESULTS 12 +static const char *oplock_results[NOPLOCK_RESULTS][4] = { + {"R", "s", "R", "s"}, + {"R", "x", "R", "s"}, + {"R", "b", "R", "s"}, + + {"RH", "s", "RH", ""}, + {"RH", "x", "RH", ""}, + {"RH", "b", "RH", ""}, + + {"RW", "s", "R", "s"}, + {"RW", "x", "R", "s"}, + {"RW", "b", "R", "s"}, + + {"RHW", "s", "RH", ""}, + {"RHW", "x", "RH", ""}, + {"RHW", "b", "RH", ""}, +}; + +static const char *oplock_results_2[NOPLOCK_RESULTS][4] = { + {"s", "R", "s", "R"}, + {"s", "RH", "s", "R"}, + {"s", "RW", "s", "R"}, + {"s", "RHW", "s", "R"}, + + {"x", "R", "s", "R"}, + {"x", "RH", "s", "R"}, + {"x", "RW", "s", "R"}, + {"x", "RHW", "s", "R"}, + + {"b", "R", "s", "R"}, + {"b", "RH", "s", "R"}, + {"b", "RW", "s", "R"}, + {"b", "RHW", "s", "R"}, +}; + +static bool test_lease_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h, h2; + NTSTATUS status; + const char *fname = "lease_oplock.dat"; + bool ret = true; + int i; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + + for (i = 0; i < NOPLOCK_RESULTS; i++) { + const char *held = oplock_results[i][0]; + const char *contend = oplock_results[i][1]; + const char *brokento = oplock_results[i][2]; + const char *granted = oplock_results[i][3]; + torture_comment(tctx, "Hold %s(%x), requesting %s(%x), " + "expecting break to %s(%x) and grant of %s(%x)\n", + held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend), + brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted)); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, held, true, LEASE1, 0); + + /* Does an oplock contend the lease? */ + smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted)); + lease_break_info.held_oplock_level = io.out.oplock_level; + + if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { + CHECK_BREAK_INFO(held, brokento, LEASE1); + } else { + CHECK_NO_BREAK(tctx); + } + + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + } + + for (i = 0; i < NOPLOCK_RESULTS; i++) { + const char *held = oplock_results_2[i][0]; + const char *contend = oplock_results_2[i][1]; + const char *brokento = oplock_results_2[i][2]; + const char *granted = oplock_results_2[i][3]; + torture_comment(tctx, "Hold %s(%x), requesting %s(%x), " + "expecting break to %s(%x) and grant of %s(%x)\n", + held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend), + brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted)); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab an oplock. */ + smb2_oplock_create(&io, fname, smb2_util_oplock_level(held)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held)); + lease_break_info.held_oplock_level = io.out.oplock_level; + + /* Grab lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend)); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, granted, true, LEASE1, 0); + + if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) { + CHECK_OPLOCK_BREAK(brokento); + } else { + CHECK_NO_BREAK(tctx); + } + + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + } + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_multibreak(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_multibreak.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab lease, upgrade to RHW .. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); + + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + + /* Contend with LEASE2. */ + smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + + /* Verify that we were only sent one break. */ + CHECK_BREAK_INFO("RHW", "RH", LEASE1); + + /* Drop LEASE1 / LEASE2 */ + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, h2); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, h3); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab an R lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + + /* Grab a level-II oplock. */ + smb2_oplock_create(&io, fname, smb2_util_oplock_level("s")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + lease_break_info.held_oplock_level = io.out.oplock_level; + + /* Verify no breaks. */ + CHECK_NO_BREAK(tctx); + + /* Open for truncate, force a break. */ + smb2_generic_create(&io, NULL, false, fname, + NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("")); + lease_break_info.held_oplock_level = io.out.oplock_level; + + /* Sleep, use a write to clear the recv queue. */ + smb_msleep(250); + ZERO_STRUCT(w); + w.in.file.handle = h3; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Verify one oplock break, one lease break. */ + CHECK_OPLOCK_BREAK(""); + CHECK_BREAK_INFO("R", "", LEASE1); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_request_parent(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + uint64_t parent = LEASE2; + NTSTATUS status; + const char *fname = "lease_v2_request_parent.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + torture_skip(tctx, "directory leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, &parent, + smb2_util_lease_state("RHW"), + 0x11); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, + SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2, + ls.lease_epoch + 1); + + done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_break_twice(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h1 = {{0}}; + NTSTATUS status; + const char *fname = "lease_break_twice.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + ZERO_STRUCT(io); + + smb2_lease_v2_create_share( + &io, &ls1, false, fname, smb2_util_share_access("RWD"), + LEASE1, NULL, smb2_util_lease_state("RWH"), 0x11); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + smb2_lease_v2_create_share( + &io, &ls2, false, fname, smb2_util_share_access("R"), + LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RW", LEASE1, ls1.lease_epoch + 2); + + smb2_lease_v2_create_share( + &io, &ls2, false, fname, smb2_util_share_access("RWD"), + LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RW", "R", LEASE1, ls1.lease_epoch + 3); + +done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v2_request(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1, ls2, ls2t, ls3, ls4; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + struct smb2_handle h4 = {{0}}; + struct smb2_handle h5 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_v2_request.dat"; + const char *dname = "lease_v2_request.dir"; + const char *dnamefname = "lease_v2_request.dir\\lease.dat"; + const char *dnamefname2 = "lease_v2_request.dir\\lease2.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + torture_skip(tctx, "directory leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, dname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls2, true, dname, + smb2_util_share_access("RWD"), + LEASE2, NULL, + smb2_util_lease_state("RHW"), + 0x22); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls3, false, dnamefname, + smb2_util_share_access("RWD"), + LEASE3, &LEASE2, + smb2_util_lease_state("RHW"), + 0x33); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE3, + SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2, + ls3.lease_epoch + 1); + + CHECK_NO_BREAK(tctx); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls4, false, dnamefname2, + smb2_util_share_access("RWD"), + LEASE4, NULL, + smb2_util_lease_state("RHW"), + 0x44); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h4 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0, ls4.lease_epoch + 1); + + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "", LEASE2, ls2.lease_epoch + 2); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls2t, true, dname, + smb2_util_share_access("RWD"), + LEASE2, NULL, + smb2_util_lease_state("RHW"), + 0x222); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h5 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch+3); + smb2_util_close(tree, h5); + + ZERO_STRUCT(w); + w.in.file.handle = h4; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Wait 4 seconds in order to check if the write time + * was updated (after 2 seconds). + */ + smb_msleep(4000); + CHECK_NO_BREAK(tctx); + + /* + * only the close on the modified file break the + * directory lease. + */ + smb2_util_close(tree, h4); + + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "", LEASE2, ls2.lease_epoch+4); + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + smb2_util_close(tree, h4); + smb2_util_close(tree, h5); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, dname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_epoch1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h; + const char *fname = "lease_v2_epoch1.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1); + smb2_util_close(tree, h); + smb2_util_unlink(tree, fname); + + smb2_lease_v2_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RWH", true, LEASE1, 0, 0, ls.lease_epoch + 1); + smb2_util_close(tree, h); + +done: + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v2_epoch2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1v2, ls1v2t, ls1v1; + struct smb2_handle hv2 = {}, hv1 = {}; + const char *fname = "lease_v2_epoch2.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("R"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "R", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1v2.lease_epoch + 2); + + smb2_util_close(tree, hv2); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 3); + + smb2_util_close(tree, hv2); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RH", LEASE1, ls1v2.lease_epoch + 4); + + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + +done: + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v2_epoch3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1v1 = {}, ls1v1t = {},ls1v2 = {}; + struct smb2_handle hv1 = {}, hv2 = {}; + const char *fname = "lease_v2_epoch3.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RW"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RW", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + smb2_util_close(tree, hv1); + smb2_util_close(tree, hv2); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RWH"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1); + smb2_util_close(tree, hv2); + +done: + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_breaking1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking1.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We ack the lease break. + */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_breaking2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking2.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We ack the lease break. + */ + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_WRITE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_WRITE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_READ; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + /* Try again with the correct state this time. */ + ack.in.lease.lease_state = SMB2_LEASE_NONE;; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); + + /* Get state of the original handle. */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io1, "", true, LEASE1, 0); + smb2_util_close(tree, io1.out.file.handle); + +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_breaking3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct smb2_request *req3 = NULL; + struct lease_break_info lease_break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking3.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + /* + * a conflicting open with NTCREATEX_DISP_OVERWRITE + * doesn't trigger an immediate lease break to none. + */ + lease_break_info_tmp = lease_break_info; + torture_reset_lease_break_info(tctx, &lease_break_info); + smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE); + io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req3 = smb2_create_send(tree, &io3); + torture_assert(tctx, req3 != NULL, "smb2_create_send"); + CHECK_NO_BREAK(tctx); + lease_break_info = lease_break_info_tmp; + + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + /* + * We ack the lease break, but defer acking the next break (to "R") + */ + lease_break_info.lease_skip_ack = true; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + /* + * We got an additional break downgrading to just "R" + * while we defer the ack. + */ + CHECK_BREAK_INFO("RH", "R", LEASE1); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + /* + * We ack the downgrade to "R" and get an immediate break to none + */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1); + + /* + * We get the downgrade to none. + */ + CHECK_BREAK_INFO("R", "", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + torture_assert(tctx, req3->cancel.can_cancel, + "req3 can_cancel"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + status = smb2_create_recv(req3, tctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v2_breaking3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct smb2_request *req3 = NULL; + struct lease_break_info lease_break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "v2_lease_breaking3.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_v2_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + /* Epoch increases on open. */ + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RH", LEASE1, ls1.lease_epoch + 1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* On receiving a lease break, we must sync the new epoch. */ + ls1.lease_epoch = lease_break_info.lease_break.new_epoch; + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); + smb2_util_close(tree, h1b); + + /* + * a conflicting open with NTCREATEX_DISP_OVERWRITE + * doesn't trigger an immediate lease break to none. + */ + lease_break_info_tmp = lease_break_info; + torture_reset_lease_break_info(tctx, &lease_break_info); + smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE); + io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req3 = smb2_create_send(tree, &io3); + torture_assert(tctx, req3 != NULL, "smb2_create_send"); + CHECK_NO_BREAK(tctx); + lease_break_info = lease_break_info_tmp; + + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + /* + * We ack the lease break, but defer acking the next break (to "R") + */ + lease_break_info.lease_skip_ack = true; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + /* + * We got an additional break downgrading to just "R" + * while we defer the ack. + */ + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "R", LEASE1, ls1.lease_epoch); + /* On receiving a lease break, we must sync the new epoch. */ + ls1.lease_epoch = lease_break_info.lease_break.new_epoch; + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + /* + * We ack the downgrade to "R" and get an immediate break to none + */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1); + + /* + * We get the downgrade to none. + */ + CHECK_BREAK_INFO_V2(tree->session->transport, + "R", "", LEASE1, ls1.lease_epoch); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + torture_assert(tctx, req3->cancel.can_cancel, + "req3 can_cancel"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + status = smb2_create_recv(req3, tctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + + +static bool test_lease_breaking4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls1t = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct lease_break_info lease_break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking4.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * We got a break from RH to NONE, we're supported to ack + * this downgrade + */ + CHECK_BREAK_INFO("RH", "", LEASE1); + + lease_break_info_tmp = lease_break_info; + torture_reset_lease_break_info(tctx, &lease_break_info); + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree, h2); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break, even if the lease is in breaking state. + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree, h2); + + CHECK_NO_BREAK(tctx); + + /* + * We now ask the server about the current lease state + * which should still be "RH", but with + * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS. + */ + smb2_lease_create_share(&io3, &ls1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + + /* + * We finally ack the lease break... + */ + CHECK_NO_BREAK(tctx); + lease_break_info = lease_break_info_tmp; + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + CHECK_NO_BREAK(tctx); + +done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_breaking5(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls1t = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct lease_break_info lease_break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking5.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "R", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * We got a break from RH to NONE, we're supported to ack + * this downgrade + */ + CHECK_BREAK_INFO("R", "", LEASE1); + + lease_break_info_tmp = lease_break_info; + torture_reset_lease_break_info(tctx, &lease_break_info); + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); + + /* + * We now ask the server about the current lease state + * which should still be "RH", but with + * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS. + */ + smb2_lease_create_share(&io3, &ls1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "", true, LEASE1, 0); + + /* + * We send an ack without without being asked. + */ + CHECK_NO_BREAK(tctx); + lease_break_info = lease_break_info_tmp; + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + torture_reset_lease_break_info(tctx, &lease_break_info); + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + CHECK_NO_BREAK(tctx); + +done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_breaking6(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking6.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We are asked to break to "RH", but we are allowed to + * break to any of "RH", "R" or NONE. + */ + ack.in.lease.lease_state = SMB2_LEASE_NONE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_lock1(struct torture_context *tctx, + struct smb2_tree *tree1a, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls2 = {}; + struct smb2_lease ls3 = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + const char *fname = "locktest.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + struct smbcli_options options1; + struct smb2_tree *tree1b = NULL; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Set up handlers. */ + tree2->session->transport->lease.handler = torture_lease_handler; + tree2->session->transport->lease.private_data = tree2; + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + ZERO_STRUCT(lck); + + /* Open a handle on tree1a. */ + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* Open a second handle on tree1b. */ + smb2_lease_create_share(&io2, &ls2, false, fname, + smb2_util_share_access("RWD"), + LEASE2, + smb2_util_lease_state("RWH")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE2, 0); + /* And LEASE1 got broken to RH. */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Now open a lease on a different client guid. */ + smb2_lease_create_share(&io3, &ls3, false, fname, + smb2_util_share_access("RWD"), + LEASE3, + smb2_util_lease_state("RWH")); + status = smb2_create(tree2, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE3, 0); + /* Doesn't break. */ + CHECK_NO_BREAK(tctx); + + lck.in.locks = el; + /* + * Try and get get an exclusive byte + * range lock on H1 (LEASE1). + */ + + lck.in.lock_count = 1; + lck.in.lock_sequence = 1; + lck.in.file.handle = h1; + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree1a, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* LEASE2 and LEASE3 should get broken to NONE. */ + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + + CHECK_VAL(lease_break_info.failures, 0); \ + CHECK_VAL(lease_break_info.count, 2); \ + + /* Get state of the H1 (LEASE1) */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + /* Should still be RH. */ + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + smb2_util_close(tree1a, io1.out.file.handle); + + /* Get state of the H2 (LEASE2) */ + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io2, "", true, LEASE2, 0); + smb2_util_close(tree1b, io2.out.file.handle); + + /* Get state of the H3 (LEASE3) */ + smb2_lease_create(&io3, &ls3, false, fname, LEASE3, smb2_util_lease_state("")); + status = smb2_create(tree2, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io3, "", true, LEASE3, 0); + smb2_util_close(tree2, io3.out.file.handle); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * Try and get get an exclusive byte + * range lock on H3 (LEASE3). + */ + lck.in.lock_count = 1; + lck.in.lock_sequence = 2; + lck.in.file.handle = h3; + el[0].offset = 100; + el[0].length = 1; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree2, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + /* LEASE1 got broken to NONE. */ + CHECK_BREAK_INFO("RH", "", LEASE1); + torture_reset_lease_break_info(tctx, &lease_break_info); + +done: + smb2_util_close(tree1a, h1); + smb2_util_close(tree1b, h2); + smb2_util_close(tree2, h3); + + smb2_util_unlink(tree1a, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_complex1(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_complex1.dat"; + bool ret = true; + uint32_t caps; + struct smb2_tree *tree1b = NULL; + struct smbcli_options options1; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "R", true, LEASE1, 0); + + /* Upgrade to RWH over connection 1b */ + ls1.lease_state = smb2_util_lease_state("RWH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE1, 0); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Contend with LEASE2. */ + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "R", true, LEASE2, 0); + + /* Verify that we were only sent one break. */ + CHECK_BREAK_INFO("RHW", "RH", LEASE1); + + /* again RH over connection 1b doesn't change the epoch */ + ls1.lease_state = smb2_util_lease_state("RH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1a, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO("R", "", LEASE2); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h3; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1b, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO("RH", "", LEASE1); + + done: + smb2_util_close(tree1a, h); + smb2_util_close(tree1b, h2); + smb2_util_close(tree1b, h3); + + smb2_util_unlink(tree1a, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_complex1(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_v2_complex1.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + struct smb2_tree *tree1b = NULL; + struct smbcli_options options1; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree1a->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL, + smb2_util_lease_state("R"), 0x4711); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "R", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* Upgrade to RWH over connection 1b */ + ls1.lease_state = smb2_util_lease_state("RWH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Contend with LEASE2. */ + smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, + smb2_util_lease_state("R"), 0x11); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls2.lease_epoch += 1; + CHECK_LEASE_V2(&io2, "R", true, LEASE2, + 0, 0, ls2.lease_epoch); + + /* Verify that we were only sent one break. */ + ls1.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RHW", "RH", LEASE1, ls1.lease_epoch); + + /* again RH over connection 1b doesn't change the epoch */ + ls1.lease_state = smb2_util_lease_state("RH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io1, "RH", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1a, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "R", "", LEASE2, ls2.lease_epoch); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h3; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1b, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RH", "", LEASE1, ls1.lease_epoch); + + done: + smb2_util_close(tree1a, h); + smb2_util_close(tree1b, h2); + smb2_util_close(tree1b, h3); + + smb2_util_unlink(tree1a, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_complex2(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + NTSTATUS status; + const char *fname = "lease_v2_complex2.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + struct smb2_tree *tree1b = NULL; + struct smbcli_options options1; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree1a->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab RWH lease over connection 1a */ + smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL, + smb2_util_lease_state("RWH"), 0x4711); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "RWH", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* + * we defer acking the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + /* Ask for RWH on connection 1b, different lease. */ + smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, + smb2_util_lease_state("RWH"), 0x11); + req2 = smb2_create_send(tree1b, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + ls1.lease_epoch += 1; + + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RWH", "RH", LEASE1, ls1.lease_epoch); + + /* Send the break ACK on tree1b. */ + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = SMB2_LEASE_HANDLE|SMB2_LEASE_READ; + + status = smb2_lease_break_ack(tree1b, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io2, "RH", true, LEASE2, + 0, 0, ls2.lease_epoch+1); + h2 = io2.out.file.handle; + + done: + smb2_util_close(tree1a, h); + smb2_util_close(tree1b, h2); + + smb2_util_unlink(tree1a, fname); + + talloc_free(mem_ctx); + + return ret; +} + + +static bool test_lease_timeout(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h = {{0}}; + struct smb2_handle hnew = {{0}}; + struct smb2_handle h1b = {{0}}; + NTSTATUS status; + const char *fname = "lease_timeout.dat"; + bool ret = true; + struct smb2_lease_break_ack ack = {}; + struct smb2_request *req2 = NULL; + struct smb2_write w; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Grab a RWH lease. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * Just don't ack the lease break. + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + /* Break with a RWH request. */ + smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH")); + req2 = smb2_create_send(tree, &io); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + /* Copy the break request. */ + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + + /* Now wait for the timeout and get the reply. */ + status = smb2_create_recv(req2, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + hnew = io.out.file.handle; + + /* Ack the break after the timeout... */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + /* Get state of the original handle. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree, io.out.file.handle); + + /* Write on the original handle and make sure it's still valid. */ + torture_reset_lease_break_info(tctx, &lease_break_info); + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, '1', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Causes new handle to break to NONE. */ + CHECK_BREAK_INFO("RH", "", LEASE2); + + /* Write on the new handle. */ + torture_reset_lease_break_info(tctx, &lease_break_info); + ZERO_STRUCT(w); + w.in.file.handle = hnew; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 1024); + memset(w.in.data.data, '2', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + /* No break - original handle was already NONE. */ + CHECK_NO_BREAK(tctx); + smb2_util_close(tree, hnew); + + /* Upgrade to R on LEASE1. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + h1b = io.out.file.handle; + smb2_util_close(tree, h1b); + + /* Upgrade to RWH on LEASE1. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h1b = io.out.file.handle; + smb2_util_close(tree, h1b); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + smb2_util_close(tree, h1b); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_rename_wait(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_lease ls3; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + union smb_setfileinfo sinfo; + NTSTATUS status; + const char *fname_src = "lease_rename_src.dat"; + const char *fname_dst = "lease_rename_dst.dat"; + bool ret = true; + struct smb2_lease_break_ack ack = {}; + struct smb2_request *rename_req = NULL; + uint32_t caps; + unsigned int i; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname_src); + smb2_util_unlink(tree, fname_dst); + + /* Short timeout for fails. */ + tree->session->transport->options.request_timeout = 15; + + /* Grab a RH lease. */ + smb2_lease_create(&io, + &ls1, + false, + fname_src, + LEASE1, + smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); + h1 = io.out.file.handle; + + /* Second open with a RH lease. */ + smb2_lease_create(&io, + &ls2, + false, + fname_src, + LEASE2, + smb2_util_lease_state("RH")); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.desired_access = GENERIC_READ_ACCESS; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + h2 = io.out.file.handle; + + /* + * Don't ack a lease break. + */ + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + /* Break with a rename. */ + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname_dst; + rename_req = smb2_setinfo_file_send(tree, &sinfo); + + torture_assert(tctx, + rename_req != NULL, + "smb2_setinfo_file_send"); + torture_assert(tctx, + rename_req->state == SMB2_REQUEST_RECV, + "rename pending"); + + /* Try and open the destination with a RH lease. */ + smb2_lease_create(&io, + &ls3, + false, + fname_dst, + LEASE3, + smb2_util_lease_state("RH")); + /* We want to open, not create. */ + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.desired_access = GENERIC_READ_ACCESS; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + /* + * The smb2_create() I/O should have picked up the break request + * caused by the pending rename. + */ + + /* Copy the break request. */ + ack.in.lease.lease_key = + lease_break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + lease_break_info.lease_break.new_lease_state; + + /* + * Give the server 3 more chances to have renamed + * the file. Better than doing a sleep. + */ + for (i = 0; i < 3; i++) { + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } + + /* Ack the break. The server is now free to rename. */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Get the rename reply. */ + status = smb2_setinfo_recv(rename_req); + CHECK_STATUS(status, NT_STATUS_OK); + + /* The target should now exist. */ + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname_src); + smb2_util_unlink(tree, fname_dst); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_rename(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h = {{0}}; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + union smb_setfileinfo sinfo; + const char *fname = "lease_v2_rename_src.dat"; + const char *fname_dst = "lease_v2_rename_dst.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname_dst); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch); + + /* Now rename - what happens ? */ + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname_dst; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* No lease break. */ + CHECK_NO_BREAK(tctx); + + /* Check we can open another handle on the new name. */ + smb2_lease_v2_create_share(&io, &ls1, false, fname_dst, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state(""), + ls1.lease_epoch); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch); + smb2_util_close(tree, h1); + + /* Try another lease key. */ + smb2_lease_v2_create_share(&io, &ls2, false, fname_dst, + smb2_util_share_access("RWD"), + LEASE2, NULL, + smb2_util_lease_state("RWH"), + 0x44); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls2.lease_epoch += 1; + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch ); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RH", LEASE1, ls1.lease_epoch + 1); + ls1.lease_epoch += 1; + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Now rename back. */ + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Breaks to R on LEASE2. */ + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "R", LEASE2, ls2.lease_epoch + 1); + ls2.lease_epoch += 1; + + /* Check we can open another handle on the current name. */ + smb2_lease_v2_create_share(&io, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state(""), + ls1.lease_epoch); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1.lease_epoch); + smb2_util_close(tree, h1); + +done: + + smb2_util_close(tree, h); + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname_dst); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + + +static bool test_lease_dynamic_share(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_handle h, h1, h2; + struct smb2_write w; + NTSTATUS status; + const char *fname = "dynamic_path.dat"; + bool ret = true; + uint32_t caps; + struct smb2_tree *tree_2 = NULL; + struct smb2_tree *tree_3 = NULL; + struct smbcli_options options; + const char *orig_share = NULL; + + if (!TARGET_IS_SAMBA3(tctx)) { + torture_skip(tctx, "dynamic shares are not supported"); + return true; + } + + options = tree1a->session->transport->options; + options.client_guid = GUID_random(); + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* + * Save off original share name and change it to dynamic_share. + * This must have been pre-created with a dynamic path containing + * %t. It means we'll sleep between the connects in order to + * get a different timestamp for the share path. + */ + + orig_share = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "share"); + orig_share = talloc_strdup(tctx->lp_ctx, orig_share); + if (orig_share == NULL) { + torture_result(tctx, TORTURE_FAIL, __location__ "no memory\n"); + ret = false; + goto done; + } + lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", "dynamic_share"); + + /* create a new connection (same client_guid) */ + sleep(2); + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_2)) { + torture_result(tctx, TORTURE_FAIL, + __location__ "couldn't reconnect " + "max protocol 2.1, bailing\n"); + ret = false; + goto done; + } + + tree_2->session->transport->lease.handler = torture_lease_handler; + tree_2->session->transport->lease.private_data = tree_2; + tree_2->session->transport->oplock.handler = torture_oplock_handler; + tree_2->session->transport->oplock.private_data = tree_2; + + smb2_util_unlink(tree_2, fname); + + /* create a new connection (same client_guid) */ + sleep(2); + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_3)) { + torture_result(tctx, TORTURE_FAIL, + __location__ "couldn't reconnect " + "max protocol 3.0, bailing\n"); + ret = false; + goto done; + } + + tree_3->session->transport->lease.handler = torture_lease_handler; + tree_3->session->transport->lease.private_data = tree_3; + tree_3->session->transport->oplock.handler = torture_oplock_handler; + tree_3->session->transport->oplock.private_data = tree_3; + + smb2_util_unlink(tree_3, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Get RWH lease over connection 2 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + + /* Write some data into it. */ + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, '1', w.in.data.length); + status = smb2_write(tree_2, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Open the same name over connection 3. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + /* h1 should have replied with NONE. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + + /* We should have broken h to NONE. */ + CHECK_BREAK_INFO("RWH", "", LEASE1); + + /* Try to upgrade to RWH over connection 2 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 4096); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + /* Should have been denied. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree_2, h2); + + /* Try to upgrade to RWH over connection 3 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 0); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + /* Should have been denied. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree_3, h2); + + /* Write some data into it. */ + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 1024); + memset(w.in.data.data, '2', w.in.data.length); + status = smb2_write(tree_3, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Close everything.. */ + smb2_util_close(tree_2, h); + smb2_util_close(tree_3, h1); + + /* And ensure we can get a lease ! */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + /* And the file is the right size. */ + CHECK_VAL(io.out.size, 4096); \ + /* Close it. */ + smb2_util_close(tree_2, h); + + /* And ensure we can get a lease ! */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + /* And the file is the right size. */ + CHECK_VAL(io.out.size, 1024); \ + /* Close it. */ + smb2_util_close(tree_3, h); + + done: + + if (tree_2 != NULL) { + smb2_util_close(tree_2, h); + smb2_util_unlink(tree_2, fname); + } + if (tree_3 != NULL) { + smb2_util_close(tree_3, h1); + smb2_util_close(tree_3, h2); + + smb2_util_unlink(tree_3, fname); + } + + /* Set sharename back. */ + lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", orig_share); + + talloc_free(mem_ctx); + + return ret; +} + +/* + * Test identifies a bug where the Samba server will not trigger a lease break + * for a handle caching lease held by a client when the underlying file is + * deleted. + * Test: + * Connect session2. + * open file in session1 + * session1 should have RWH lease. + * open file in session2 + * lease break sent to session1 to downgrade lease to RH + * close file in session 2 + * unlink file in session 2 + * lease break sent to session1 to downgrade lease to R + * Cleanup + */ +static bool test_lease_unlink(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + bool ret = true; + struct smbcli_options transport2_options; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2; + struct smb2_handle h1 = {{ 0 }}; + struct smb2_handle h2 = {{ 0 }}; + const char *fname = "lease_unlink.dat"; + uint32_t caps; + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + + caps = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Connect 2nd connection */ + transport2_options = transport1->options; + transport2_options.client_guid = GUID_random(); + if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + return false; + } + transport2 = tree2->session->transport; + + /* Set lease handlers */ + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + transport2->lease.handler = torture_lease_handler; + transport2->lease.private_data = tree2; + + + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, + smb2_util_lease_state("RHW")); + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, + smb2_util_lease_state("RHW")); + + smb2_util_unlink(tree1, fname); + + torture_comment(tctx, "Client opens fname with session 1\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE1, 0); + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "Client opens fname with session 2\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + status = smb2_create(tree2, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE2, 0); + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RHW", "RH", LEASE1); + + torture_comment(tctx, + "Client closes and then unlinks fname with session 2\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + smb2_util_close(tree2, h2); + smb2_util_unlink(tree2, fname); + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RH", "R", LEASE1); + +done: + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_unlink(tree1, fname); + + return ret; +} + +static bool test_lease_timeout_disconnect(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + bool ret = true; + struct smbcli_options transport2_options; + struct smbcli_options transport3_options; + struct smb2_tree *tree2 = NULL; + struct smb2_tree *tree3 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2; + struct smb2_transport *transport3; + const char *fname = "lease_timeout_logoff.dat" ; + uint32_t caps; + struct smb2_create io1; + struct smb2_create io2; + struct smb2_request *req2 = NULL; + struct smb2_lease ls1; + + caps = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree1, fname); + + /* Connect 2nd connection */ + torture_comment(tctx, "connect tree2 with the same client_guid\n"); + transport2_options = transport1->options; + if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + return false; + } + transport2 = tree2->session->transport; + + /* Connect 3rd connection */ + torture_comment(tctx, "connect tree3 with the same client_guid\n"); + transport3_options = transport1->options; + if (!torture_smb2_connection_ext(tctx, 0, &transport3_options, &tree3)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + return false; + } + transport3 = tree3->session->transport; + + /* Set lease handlers */ + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + transport2->lease.handler = torture_lease_handler; + transport2->lease.private_data = tree2; + transport3->lease.handler = torture_lease_handler; + transport3->lease.private_data = tree3; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access(""), + LEASE1, + smb2_util_lease_state("RH")); + io1.in.durable_open = true; + smb2_generic_create(&io2, NULL, false, fname, + NTCREATEX_DISP_OPEN_IF, + SMB2_OPLOCK_LEVEL_NONE, 0, 0); + + torture_comment(tctx, "tree1: create file[%s] with durable RH lease (SHARE NONE)\n", fname); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "tree1: skip lease acks\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + torture_comment(tctx, "tree2: open file[%s] without lease (SHARE RWD)\n", fname); + req2 = smb2_create_send(tree2, &io2); + torture_assert(tctx, req2 != NULL, "req2 started"); + + torture_comment(tctx, "tree1: wait for lease break\n"); + torture_wait_for_lease_break(tctx); + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RH", "R", LEASE1); + + torture_comment(tctx, "tree1: reset lease handler\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n"); + torture_assert_int_equal(tctx, req2->state, + SMB2_REQUEST_RECV, + "SMB2_REQUEST_RECV"); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "transport2: keepalive\n"); + status = smb2_keepalive(transport2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "transport3: keepalive\n"); + status = smb2_keepalive(transport3); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n"); + torture_assert_int_equal(tctx, req2->state, + SMB2_REQUEST_RECV, + "SMB2_REQUEST_RECV"); + torture_comment(tctx, "tree2: check for STATUS_PENDING\n"); + torture_assert(tctx, req2->cancel.can_cancel, "STATUS_PENDING"); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport2: disconnect\n"); + TALLOC_FREE(tree2); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport1: disconnect\n"); + TALLOC_FREE(tree1); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport3: keepalive\n"); + status = smb2_keepalive(transport3); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport3: disconnect\n"); + TALLOC_FREE(tree3); + +done: + + return ret; +} + +static bool test_lease_duplicate_create(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname1 = "duplicate_create1.dat"; + const char *fname2 = "duplicate_create2.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Ensure files don't exist. */ + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + + /* Create file1 - LEASE1 key. */ + smb2_lease_create(&io, &ls, false, fname1, LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + /* + * Create file2 with the same LEASE1 key - this should fail with. + * INVALID_PARAMETER. + */ + smb2_lease_create(&io, &ls, false, fname2, LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + smb2_util_close(tree, h1); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_duplicate_open(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname1 = "duplicate_open1.dat"; + const char *fname2 = "duplicate_open2.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Ensure files don't exist. */ + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + + /* Create file1 - LEASE1 key. */ + smb2_lease_create(&io, &ls, false, fname1, LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + /* Leave file1 open and leased. */ + + /* Create file2 - no lease. */ + smb2_lease_create(&io, NULL, false, fname2, 0, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + /* Close it. */ + smb2_util_close(tree, h2); + + /* + * Try and open file2 with the same LEASE1 key - this should fail with. + * INVALID_PARAMETER. + */ + smb2_lease_create(&io, &ls, false, fname2, LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + /* + * If we did open this is an error, but save off + * the handle so we close below. + */ + h2 = io.out.file.handle; + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v1_bug_15148(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_v1_bug_15148.dat"; + bool ret = true; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "R", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + + /* Contend with LEASE2. */ + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "R", true, LEASE2, 0); + + CHECK_NO_BREAK(tctx); + + ZERO_STRUCT(w); + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO("R", "", LEASE2); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'O', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_NO_BREAK(tctx); + + ZERO_STRUCT(w); + w.in.file.handle = h2; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO("R", "", LEASE1); + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_bug_15148(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_v2_bug_15148.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL, + smb2_util_lease_state("R"), 0x4711); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "R", true, LEASE1, + 0, 0, ls1.lease_epoch); + + CHECK_NO_BREAK(tctx); + + /* Contend with LEASE2. */ + smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, + smb2_util_lease_state("R"), 0x11); + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls2.lease_epoch += 1; + CHECK_LEASE_V2(&io2, "R", true, LEASE2, + 0, 0, ls2.lease_epoch); + + CHECK_NO_BREAK(tctx); + + ZERO_STRUCT(w); + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree->session->transport, + "R", "", LEASE2, ls2.lease_epoch); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'O', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_NO_BREAK(tctx); + + ZERO_STRUCT(w); + w.in.file.handle = h2; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree->session->transport, + "R", "", LEASE1, ls1.lease_epoch); + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "lease"); + + torture_suite_add_1smb2_test(suite, "request", test_lease_request); + torture_suite_add_1smb2_test(suite, "break_twice", + test_lease_break_twice); + torture_suite_add_1smb2_test(suite, "nobreakself", + test_lease_nobreakself); + torture_suite_add_1smb2_test(suite, "statopen", test_lease_statopen); + torture_suite_add_1smb2_test(suite, "statopen2", test_lease_statopen2); + torture_suite_add_1smb2_test(suite, "statopen3", test_lease_statopen3); + torture_suite_add_1smb2_test(suite, "statopen4", test_lease_statopen4); + torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade); + torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2); + torture_suite_add_1smb2_test(suite, "upgrade3", test_lease_upgrade3); + torture_suite_add_1smb2_test(suite, "break", test_lease_break); + torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock); + torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak); + torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1); + torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2); + torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3); + torture_suite_add_1smb2_test(suite, "v2_breaking3", test_lease_v2_breaking3); + torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4); + torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5); + torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6); + torture_suite_add_2smb2_test(suite, "lock1", test_lease_lock1); + torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); + torture_suite_add_1smb2_test(suite, "v2_request_parent", + test_lease_v2_request_parent); + torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request); + torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1); + torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2); + torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3); + torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); + torture_suite_add_1smb2_test(suite, "v2_complex2", test_lease_v2_complex2); + torture_suite_add_1smb2_test(suite, "v2_rename", test_lease_v2_rename); + torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share); + torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); + torture_suite_add_1smb2_test(suite, "unlink", test_lease_unlink); + torture_suite_add_1smb2_test(suite, "timeout-disconnect", test_lease_timeout_disconnect); + torture_suite_add_1smb2_test(suite, "rename_wait", + test_lease_rename_wait); + torture_suite_add_1smb2_test(suite, "duplicate_create", + test_lease_duplicate_create); + torture_suite_add_1smb2_test(suite, "duplicate_open", + test_lease_duplicate_open); + torture_suite_add_1smb2_test(suite, "v1_bug15148", + test_lease_v1_bug_15148); + torture_suite_add_1smb2_test(suite, "v2_bug15148", + test_lease_v2_bug_15148); + + suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); + + return suite; +} diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c new file mode 100644 index 0000000..6c865dc --- /dev/null +++ b/source4/torture/smb2/lease_break_handler.c @@ -0,0 +1,161 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 leases + + Copyright (C) Zachary Loafman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" +#include "libcli/smb/smbXcli_base.h" +#include "lease_break_handler.h" + +struct lease_break_info lease_break_info; + +void torture_lease_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + + status = smb2_lease_break_ack_recv(req, &lease_break_info.lease_break_ack); + if (!NT_STATUS_IS_OK(status)) + lease_break_info.failures++; + + return; +} + +/* a lease break request handler */ +bool torture_lease_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_lease_break_ack io; + struct smb2_request *req; + const char *action = NULL; + char *ls = smb2_util_lease_state_string(lease_break_info.tctx, + lb->new_lease_state); + + if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { + action = "acking"; + } else { + action = "received"; + } + + lease_break_info.lease_transport = transport; + lease_break_info.lease_break = *lb; + lease_break_info.count++; + + if (lease_break_info.lease_skip_ack) { + torture_comment(lease_break_info.tctx, + "transport[%p] Skip %s to %s in lease handler\n", + transport, action, ls); + return true; + } + + torture_comment(lease_break_info.tctx, + "transport[%p] %s to %s in lease handler\n", + transport, action, ls); + + if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { + ZERO_STRUCT(io); + io.in.lease.lease_key = lb->current_lease.lease_key; + io.in.lease.lease_state = lb->new_lease_state; + + req = smb2_lease_break_ack_send(tree, &io); + req->async.fn = torture_lease_break_callback; + req->async.private_data = NULL; + } + + return true; +} + +/* + * A lease break handler which ignores incoming lease break requests + * To be used in cases where the client is expected to ignore incoming + * lease break requests + */ +bool torture_lease_ignore_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data) +{ + return true; +} + +/* + Timer handler function notifies the registering function that time is up +*/ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; + return; +} + +/* + Wait a short period of time to receive a single oplock break request +*/ +void torture_wait_for_lease_break(struct torture_context *tctx) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + int old_count = lease_break_info.count; + + /* Wait 1 second for an lease break */ + ne = tevent_timeval_current_ofs(0, 1000000); + + te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up); + if (te == NULL) { + torture_comment(tctx, "Failed to wait for an lease break. " + "test results may not be accurate.\n"); + goto done; + } + + torture_comment(tctx, "Waiting for a potential lease break...\n"); + while (!timesup && lease_break_info.count < old_count + 1) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for a lease " + "break. test results may not be " + "accurate.\n"); + goto done; + } + } + if (timesup) { + torture_comment(tctx, "... waiting for a lease break timed out\n"); + } else { + torture_comment(tctx, "Got %u lease breaks\n", + lease_break_info.count - old_count); + } + +done: + /* We don't know if the timed event fired and was freed, we received + * our oplock break, or some other event triggered the loop. Thus, + * we create a tmp_ctx to be able to safely free/remove the timed + * event in all 3 cases. */ + talloc_free(tmp_ctx); + + return; +} diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h new file mode 100644 index 0000000..90fde1a --- /dev/null +++ b/source4/torture/smb2/lease_break_handler.h @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 leases + + Copyright (C) Zachary Loafman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "torture/util.h" + +struct lease_break_info { + struct torture_context *tctx; + + struct smb2_lease_break lease_break; + struct smb2_transport *lease_transport; + bool lease_skip_ack; + struct smb2_lease_break_ack lease_break_ack; + int count; + int failures; + + struct smb2_handle oplock_handle; + uint8_t held_oplock_level; + uint8_t oplock_level; + int oplock_count; + int oplock_failures; +}; + +#define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \ + do { \ + uint16_t __new = smb2_util_lease_state(__state); \ + uint16_t __old = smb2_util_lease_state(__oldstate); \ + CHECK_VAL((__lb)->new_lease_state, __new); \ + CHECK_VAL((__lb)->current_lease.lease_state, __old); \ + CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \ + CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \ + if (__old & (SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE)) { \ + CHECK_VAL((__lb)->break_flags, \ + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); \ + } else { \ + CHECK_VAL((__lb)->break_flags, 0); \ + } \ + } while(0) + +#define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \ + do { \ + CHECK_VAL((__lba)->out.reserved, 0); \ + CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \ + CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \ + CHECK_VAL((__lba)->out.lease.lease_state, smb2_util_lease_state(__state)); \ + CHECK_VAL((__lba)->out.lease.lease_flags, 0); \ + CHECK_VAL((__lba)->out.lease.lease_duration, 0); \ + } while(0) + +#define CHECK_NO_BREAK(tctx) \ + do { \ + torture_wait_for_lease_break(tctx); \ + CHECK_VAL(lease_break_info.failures, 0); \ + CHECK_VAL(lease_break_info.count, 0); \ + CHECK_VAL(lease_break_info.oplock_failures, 0); \ + CHECK_VAL(lease_break_info.oplock_count, 0); \ + } while(0) + +#define CHECK_OPLOCK_BREAK(__brokento) \ + do { \ + torture_wait_for_lease_break(tctx); \ + CHECK_VAL(lease_break_info.oplock_count, 1); \ + CHECK_VAL(lease_break_info.oplock_failures, 0); \ + CHECK_VAL(lease_break_info.oplock_level, \ + smb2_util_oplock_level(__brokento)); \ + lease_break_info.held_oplock_level = lease_break_info.oplock_level; \ + } while(0) + +#define _CHECK_BREAK_INFO(__oldstate, __state, __key) \ + do { \ + torture_wait_for_lease_break(tctx); \ + CHECK_VAL(lease_break_info.failures, 0); \ + CHECK_VAL(lease_break_info.count, 1); \ + CHECK_LEASE_BREAK(&lease_break_info.lease_break, (__oldstate), \ + (__state), (__key)); \ + if (!lease_break_info.lease_skip_ack && \ + (lease_break_info.lease_break.break_flags & \ + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)) \ + { \ + torture_wait_for_lease_break(tctx); \ + CHECK_LEASE_BREAK_ACK(&lease_break_info.lease_break_ack, \ + (__state), (__key)); \ + } \ + } while(0) + +#define CHECK_BREAK_INFO(__oldstate, __state, __key) \ + do { \ + _CHECK_BREAK_INFO(__oldstate, __state, __key); \ + CHECK_VAL(lease_break_info.lease_break.new_epoch, 0); \ + } while(0) + +#define CHECK_BREAK_INFO_V2(__transport, __oldstate, __state, __key, __epoch) \ + do { \ + _CHECK_BREAK_INFO(__oldstate, __state, __key); \ + CHECK_VAL(lease_break_info.lease_break.new_epoch, __epoch); \ + if (!TARGET_IS_SAMBA3(tctx)) { \ + CHECK_VAL((uintptr_t)lease_break_info.lease_transport, \ + (uintptr_t)__transport); \ + } \ + } while(0) + +extern struct lease_break_info lease_break_info; + +bool torture_lease_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data); +bool torture_lease_ignore_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data); +void torture_wait_for_lease_break(struct torture_context *tctx); + +static inline void torture_reset_lease_break_info(struct torture_context *tctx, + struct lease_break_info *r) +{ + ZERO_STRUCTP(r); + r->tctx = tctx; +} diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c new file mode 100644 index 0000000..eac0d55 --- /dev/null +++ b/source4/torture/smb2/lock.c @@ -0,0 +1,3513 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 lock test suite + + Copyright (C) Stefan Metzmacher 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" + +#include "lib/events/events.h" +#include "param/param.h" + +#define CHECK_STATUS(status, correct) do { \ + const char *_cmt = "(" __location__ ")"; \ + torture_assert_ntstatus_equal_goto(torture,status,correct, \ + ret,done,_cmt); \ + } while (0) + +#define CHECK_STATUS_CMT(status, correct, cmt) do { \ + torture_assert_ntstatus_equal_goto(torture,status,correct, \ + ret,done,cmt); \ + } while (0) + +#define CHECK_STATUS_CONT(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + const char *_cmt = "(" __location__ ")"; \ + torture_assert_int_equal_goto(torture,v,correct,ret,done,_cmt); \ + } while (0) + +#define BASEDIR "testlock" + +#define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \ + (torture_setting_bool(_tctx, "invalid_lock_range_support", true)) +#define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false)) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(torture->ev) != 0) { \ + break; \ + } \ + } + +static bool test_valid_request(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + ZERO_STRUCT(buf); + + status = torture_smb2_testfile(tree, "lock1.txt", &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + torture_comment(torture, "Test request with 0 locks.\n"); + + lck.in.lock_count = 0x0000; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0x0000000000000000; + el[0].length = 0x0000000000000000; + el[0].reserved = 0x0000000000000000; + el[0].flags = 0x00000000; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + lck.in.lock_count = 0x0000; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 0; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 0; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_NONE; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has bug validating lock flags " + "parameter.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } + + torture_comment(torture, "Test >63-bit lock requests.\n"); + + lck.in.file.handle.data[0] +=1; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + lck.in.file.handle.data[0] -=1; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x123ab1; + lck.in.file.handle = h; + el[0].offset = UINT64_MAX; + el[0].length = UINT64_MAX; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(torture)) { + CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + } + + lck.in.lock_sequence = 0x123ab2; + status = smb2_lock(tree, &lck); + if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(torture)) { + CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } + + torture_comment(torture, "Test basic lock stacking.\n"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x12345678; + lck.in.file.handle = h; + el[0].offset = UINT32_MAX; + el[0].length = UINT32_MAX; + el[0].reserved = 0x87654321; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + el[0].flags = SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x87654321; + lck.in.file.handle = h; + el[0].offset = 0x00000000FFFFFFFF; + el[0].length = 0x00000000FFFFFFFF; + el[0].reserved = 0x1234567; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x1234567; + lck.in.file.handle = h; + el[0].offset = 0x00000000FFFFFFFF; + el[0].length = 0x00000000FFFFFFFF; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Test flags field permutations.\n"); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0; + lck.in.file.handle = h; + el[0].offset = 1; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[0].flags = ~SMB2_LOCK_FLAG_ALL_MASK; + + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has bug validating lock flags " + "parameter.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } + + if (TARGET_IS_W2K8(torture)) { + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK | + SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK | + SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + torture_warning(torture, "Target has bug validating lock flags " + "parameter.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } + + torture_comment(torture, "Test return error when 2 locks are " + "requested\n"); + + lck.in.lock_count = 2; + lck.in.lock_sequence = 0; + lck.in.file.handle = h; + el[0].offset = 9999; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[1].offset = 9999; + el[1].length = 1; + el[1].reserved = 0x00000000; + + lck.in.lock_count = 2; + el[0].flags = 0; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + lck.in.lock_count = 2; + el[0].flags = SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + lck.in.lock_count = 2; + el[0].flags = 0; + el[1].flags = 0; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has bug validating lock flags " + "parameter.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + } + + lck.in.lock_count = 2; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = 0; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has bug validating lock flags " + "parameter.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + } + + lck.in.lock_count = 1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + lck.in.lock_count = 1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + lck.in.lock_count = 1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + lck.in.lock_count = 1; + el[0].flags = SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.lock_count = 2; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.lock_count = 1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + +done: + return ret; +} + +struct test_lock_read_write_state { + const char *fname; + uint32_t lock_flags; + NTSTATUS write_h1_status; + NTSTATUS read_h1_status; + NTSTATUS write_h2_status; + NTSTATUS read_h2_status; +}; + +static bool test_lock_read_write(struct torture_context *torture, + struct smb2_tree *tree, + struct test_lock_read_write_state *s) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h1, h2; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_create cr; + struct smb2_write wr; + struct smb2_read rd; + struct smb2_lock_element el[1]; + + lck.in.locks = el; + + ZERO_STRUCT(buf); + + status = torture_smb2_testfile(tree, s->fname, &h1); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h1, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h1; + el[0].offset = 0; + el[0].length = ARRAY_SIZE(buf)/2; + el[0].reserved = 0x00000000; + el[0].flags = s->lock_flags; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h1; + el[0].offset = ARRAY_SIZE(buf)/2; + el[0].length = ARRAY_SIZE(buf)/2; + el[0].reserved = 0x00000000; + el[0].flags = s->lock_flags; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + ZERO_STRUCT(cr); + cr.in.oplock_level = 0; + cr.in.desired_access = SEC_RIGHTS_FILE_ALL; + cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + cr.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + cr.in.create_options = 0; + cr.in.fname = s->fname; + + status = smb2_create(tree, tree, &cr); + CHECK_STATUS(status, NT_STATUS_OK); + + h2 = cr.out.file.handle; + + ZERO_STRUCT(wr); + wr.in.file.handle = h1; + wr.in.offset = ARRAY_SIZE(buf)/2; + wr.in.data = data_blob_const(buf, ARRAY_SIZE(buf)/2); + + status = smb2_write(tree, &wr); + CHECK_STATUS(status, s->write_h1_status); + + ZERO_STRUCT(rd); + rd.in.file.handle = h1; + rd.in.offset = ARRAY_SIZE(buf)/2; + rd.in.length = ARRAY_SIZE(buf)/2; + + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, s->read_h1_status); + + ZERO_STRUCT(wr); + wr.in.file.handle = h2; + wr.in.offset = ARRAY_SIZE(buf)/2; + wr.in.data = data_blob_const(buf, ARRAY_SIZE(buf)/2); + + status = smb2_write(tree, &wr); + CHECK_STATUS(status, s->write_h2_status); + + ZERO_STRUCT(rd); + rd.in.file.handle = h2; + rd.in.offset = ARRAY_SIZE(buf)/2; + rd.in.length = ARRAY_SIZE(buf)/2; + + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, s->read_h2_status); + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h1; + el[0].offset = ARRAY_SIZE(buf)/2; + el[0].length = ARRAY_SIZE(buf)/2; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + ZERO_STRUCT(wr); + wr.in.file.handle = h2; + wr.in.offset = ARRAY_SIZE(buf)/2; + wr.in.data = data_blob_const(buf, ARRAY_SIZE(buf)/2); + + status = smb2_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h2; + rd.in.offset = ARRAY_SIZE(buf)/2; + rd.in.length = ARRAY_SIZE(buf)/2; + + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + return ret; +} + +static bool test_lock_rw_none(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct test_lock_read_write_state s = { + .fname = "lock_rw_none.dat", + .lock_flags = SMB2_LOCK_FLAG_NONE, + .write_h1_status = NT_STATUS_FILE_LOCK_CONFLICT, + .read_h1_status = NT_STATUS_OK, + .write_h2_status = NT_STATUS_FILE_LOCK_CONFLICT, + .read_h2_status = NT_STATUS_OK, + }; + + if (!TARGET_IS_W2K8(torture)) { + torture_skip(torture, "RW-NONE tests the behavior of a " + "NONE-type lock, which is the same as a SHARED " + "lock but is granted due to a bug in W2K8. If " + "target is not W2K8 we skip this test.\n"); + } + + return test_lock_read_write(torture, tree, &s); +} + +static bool test_lock_rw_shared(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct test_lock_read_write_state s = { + .fname = "lock_rw_shared.dat", + .lock_flags = SMB2_LOCK_FLAG_SHARED, + .write_h1_status = NT_STATUS_FILE_LOCK_CONFLICT, + .read_h1_status = NT_STATUS_OK, + .write_h2_status = NT_STATUS_FILE_LOCK_CONFLICT, + .read_h2_status = NT_STATUS_OK, + }; + + return test_lock_read_write(torture, tree, &s); +} + +static bool test_lock_rw_exclusive(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct test_lock_read_write_state s = { + .fname = "lock_rw_exclusive.dat", + .lock_flags = SMB2_LOCK_FLAG_EXCLUSIVE, + .write_h1_status = NT_STATUS_OK, + .read_h1_status = NT_STATUS_OK, + .write_h2_status = NT_STATUS_FILE_LOCK_CONFLICT, + .read_h2_status = NT_STATUS_FILE_LOCK_CONFLICT, + }; + + return test_lock_read_write(torture, tree, &s); +} + +static bool test_lock_auto_unlock(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + ZERO_STRUCT(buf); + + status = torture_smb2_testfile(tree, "autounlock.txt", &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(lck); + ZERO_STRUCT(el[0]); + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + +done: + return ret; +} + +/* + test different lock ranges and see if different handles conflict +*/ +static bool test_lock(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + const char *fname = BASEDIR "\\async.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + torture_comment(torture, "Trying 0/0 lock\n"); + el[0].offset = 0x0000000000000000; + el[0].length = 0x0000000000000000; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Trying 0/1 lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 0x0000000000000000; + el[0].length = 0x0000000000000001; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying 0xEEFFFFF lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 0xEEFFFFFF; + el[0].length = 4000; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying 0xEF00000 lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 0xEF000000; + el[0].length = 4000; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying (2^63 - 1)/1\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 1; + el[0].offset <<= 63; + el[0].offset--; + el[0].length = 1; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying 2^63/1\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 1; + el[0].offset <<= 63; + el[0].length = 1; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying max/0 lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = ~0; + el[0].length = 0; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying max/1 lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = ~0; + el[0].length = 1; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Trying max/2 lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = ~0; + el[0].length = 2; + status = smb2_lock(tree, &lck); + if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(torture)) { + CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + torture_comment(torture, "Trying wrong handle unlock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[0].offset = 10001; + el[0].length = 40002; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + test SMB2 LOCK async operation +*/ +static bool test_async(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + struct smb2_request *req = NULL; + + const char *fname = BASEDIR "\\async.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 100; + el[0].length = 50; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + torture_comment(torture, " Acquire first lock\n"); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should now succeed\n"); + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock_recv(req, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + test SMB2 LOCK Cancel operation +*/ +static bool test_cancel(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + struct smb2_request *req = NULL; + + const char *fname = BASEDIR "\\cancel.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 100; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + torture_comment(torture, "Testing basic cancel\n"); + + torture_comment(torture, " Acquire first lock\n"); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Cancel the second lock\n"); + smb2_cancel(req); + lck.in.file.handle = h2; + status = smb2_lock_recv(req, &lck); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + torture_comment(torture, " Unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Testing cancel by unlock\n"); + + torture_comment(torture, " Acquire first lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Attempt to unlock pending second lock\n"); + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, " Now cancel the second lock\n"); + smb2_cancel(req); + lck.in.file.handle = h2; + status = smb2_lock_recv(req, &lck); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + torture_comment(torture, " Unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Testing cancel by close\n"); + + torture_comment(torture, " Acquire first lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Close the second lock handle\n"); + smb2_util_close(tree, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Check pending lock reply\n"); + status = smb2_lock_recv(req, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, " Unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + test SMB2 LOCK Cancel by tree disconnect +*/ +static bool test_cancel_tdis(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + struct smb2_request *req = NULL; + + const char *fname = BASEDIR "\\cancel_tdis.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 100; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + torture_comment(torture, "Testing cancel by tree disconnect\n"); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Acquire first lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Disconnect the tree\n"); + smb2_tdis(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Check pending lock reply\n"); + status = smb2_lock_recv(req, &lck); + if (!NT_STATUS_EQUAL(status, NT_STATUS_RANGE_NOT_LOCKED)) { + /* + * The status depends on the server internals + * the order in which the files are closed + * by smb2_tdis(). + */ + CHECK_STATUS(status, NT_STATUS_OK); + } + + torture_comment(torture, " Attempt to unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + /* + * Most Windows versions have a strange order to + * verify the session id, tree id and file id. + * (They should be checked in that order, but windows + * seems to check the file id before the others). + */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) { + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + } + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + test SMB2 LOCK Cancel by user logoff +*/ +static bool test_cancel_logoff(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + struct smb2_request *req = NULL; + + const char *fname = BASEDIR "\\cancel_logoff.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 100; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + + torture_comment(torture, "Testing cancel by ulogoff\n"); + + torture_comment(torture, " Acquire first lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " Second lock should pend on first\n"); + lck.in.file.handle = h2; + req = smb2_lock_send(tree, &lck); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(torture, " Logoff user\n"); + smb2_logoff(tree->session); + + torture_comment(torture, " Check pending lock reply\n"); + status = smb2_lock_recv(req, &lck); + if (!NT_STATUS_EQUAL(status, NT_STATUS_RANGE_NOT_LOCKED)) { + /* + * The status depends on the server internals + * the order in which the files are closed + * by smb2_logoff(). + */ + CHECK_STATUS(status, NT_STATUS_OK); + } + + torture_comment(torture, " Attempt to unlock first lock\n"); + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + /* + * Most Windows versions have a strange order to + * verify the session id, tree id and file id. + * (They should be checked in that order, but windows + * seems to check the file id before the others). + */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) { + CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); + } + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + * Test NT_STATUS_LOCK_NOT_GRANTED vs. NT_STATUS_FILE_LOCK_CONFLICT + * + * The SMBv1 protocol returns a different error code on lock acquisition + * failure depending on a number of parameters, including what error code + * was returned to the previous failure. + * + * SMBv2 has cleaned up these semantics and should always return + * NT_STATUS_LOCK_NOT_GRANTED to failed lock requests, and + * NT_STATUS_FILE_LOCK_CONFLICT to failed read/write requests due to a lock + * being held on that range. +*/ +static bool test_errorcode(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + const char *fname = BASEDIR "\\errorcode.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = el; + + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 100; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + torture_comment(torture, "Testing LOCK_NOT_GRANTED vs. " + "FILE_LOCK_CONFLICT\n"); + + if (TARGET_IS_W2K8(torture)) { + torture_result(torture, TORTURE_SKIP, + "Target has \"pretty please\" bug. A contending lock " + "request on the same handle unlocks the lock."); + goto done; + } + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Demonstrate that the first conflicting lock on each handle gives + * LOCK_NOT_GRANTED. */ + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* Demonstrate that each following conflict also gives + * LOCK_NOT_GRANTED */ + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* Demonstrate that the smbpid doesn't matter */ + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* Demonstrate that a 0-byte lock inside the locked range still + * gives the same error. */ + + el[0].offset = 102; + el[0].length = 0; + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* Demonstrate that a lock inside the locked range still gives the + * same error. */ + + el[0].offset = 102; + el[0].length = 5; + lck.in.file.handle = h; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.file.handle = h2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Tests zero byte locks. + */ + +struct double_lock_test { + struct smb2_lock_element lock1; + struct smb2_lock_element lock2; + NTSTATUS status; +}; + +static struct double_lock_test zero_byte_tests[] = { + /* {offset, count, reserved, flags}, + * {offset, count, reserved, flags}, + * status */ + + /** First, takes a zero byte lock at offset 10. Then: + * - Taking 0 byte lock at 10 should succeed. + * - Taking 1 byte locks at 9,10,11 should succeed. + * - Taking 2 byte lock at 9 should fail. + * - Taking 2 byte lock at 10 should succeed. + * - Taking 3 byte lock at 9 should fail. + */ + {{10, 0, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{10, 0, 0, 0}, {9, 1, 0, 0}, NT_STATUS_OK}, + {{10, 0, 0, 0}, {10, 1, 0, 0}, NT_STATUS_OK}, + {{10, 0, 0, 0}, {11, 1, 0, 0}, NT_STATUS_OK}, + {{10, 0, 0, 0}, {9, 2, 0, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + {{10, 0, 0, 0}, {10, 2, 0, 0}, NT_STATUS_OK}, + {{10, 0, 0, 0}, {9, 3, 0, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + + /** Same, but opposite order. */ + {{10, 0, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{9, 1, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{10, 1, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{11, 1, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{9, 2, 0, 0}, {10, 0, 0, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + {{10, 2, 0, 0}, {10, 0, 0, 0}, NT_STATUS_OK}, + {{9, 3, 0, 0}, {10, 0, 0, 0}, NT_STATUS_LOCK_NOT_GRANTED}, + + /** Zero zero case. */ + {{0, 0, 0, 0}, {0, 0, 0, 0}, NT_STATUS_OK}, +}; + +static bool test_zerobytelength(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + int i; + + const char *fname = BASEDIR "\\zero.txt"; + + torture_comment(torture, "Testing zero length byte range locks:\n"); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Setup initial parameters */ + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + + /* Try every combination of locks in zero_byte_tests, using the same + * handle for both locks. The first lock is assumed to succeed. The + * second lock may contend, depending on the expected status. */ + for (i = 0; i < ARRAY_SIZE(zero_byte_tests); i++) { + torture_comment(torture, + " ... {%llu, %llu} + {%llu, %llu} = %s\n", + (unsigned long long) zero_byte_tests[i].lock1.offset, + (unsigned long long) zero_byte_tests[i].lock1.length, + (unsigned long long) zero_byte_tests[i].lock2.offset, + (unsigned long long) zero_byte_tests[i].lock2.length, + nt_errstr(zero_byte_tests[i].status)); + + /* Lock both locks. */ + lck.in.locks = &zero_byte_tests[i].lock1; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.locks = &zero_byte_tests[i].lock2; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS_CONT(status, zero_byte_tests[i].status); + + /* Unlock both locks in reverse order. */ + lck.in.locks[0].flags = SMB2_LOCK_FLAG_UNLOCK; + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + lck.in.locks = &zero_byte_tests[i].lock1; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + /* Try every combination of locks in zero_byte_tests, using two + * different handles. */ + for (i = 0; i < ARRAY_SIZE(zero_byte_tests); i++) { + torture_comment(torture, + " ... {%llu, %llu} + {%llu, %llu} = %s\n", + (unsigned long long) zero_byte_tests[i].lock1.offset, + (unsigned long long) zero_byte_tests[i].lock1.length, + (unsigned long long) zero_byte_tests[i].lock2.offset, + (unsigned long long) zero_byte_tests[i].lock2.length, + nt_errstr(zero_byte_tests[i].status)); + + /* Lock both locks. */ + lck.in.file.handle = h; + lck.in.locks = &zero_byte_tests[i].lock1; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.file.handle = h2; + lck.in.locks = &zero_byte_tests[i].lock2; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS_CONT(status, zero_byte_tests[i].status); + + /* Unlock both locks in reverse order. */ + lck.in.file.handle = h2; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_UNLOCK; + if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + lck.in.file.handle = h; + lck.in.locks = &zero_byte_tests[i].lock1; + lck.in.locks[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_zerobyteread(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + struct smb2_read rd; + + const char *fname = BASEDIR "\\zerobyteread.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Setup initial parameters */ + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + + ZERO_STRUCT(rd); + rd.in.file.handle = h2; + + torture_comment(torture, "Testing zero byte read on lock range:\n"); + + /* Take an exclusive lock */ + torture_comment(torture, " taking exclusive lock.\n"); + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + /* Try a zero byte read */ + torture_comment(torture, " reading 0 bytes.\n"); + rd.in.offset = 5; + rd.in.length = 0; + status = smb2_read(tree, tree, &rd); + torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Unlock lock */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + torture_comment(torture, "Testing zero byte read on zero byte lock " + "range:\n"); + + /* Take an exclusive lock */ + torture_comment(torture, " taking exclusive 0-byte lock.\n"); + el[0].offset = 5; + el[0].length = 0; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + + /* Try a zero byte read before the lock */ + torture_comment(torture, " reading 0 bytes before the lock.\n"); + rd.in.offset = 4; + rd.in.length = 0; + status = smb2_read(tree, tree, &rd); + torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read on the lock */ + torture_comment(torture, " reading 0 bytes on the lock.\n"); + rd.in.offset = 5; + rd.in.length = 0; + status = smb2_read(tree, tree, &rd); + torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try a zero byte read after the lock */ + torture_comment(torture, " reading 0 bytes after the lock.\n"); + rd.in.offset = 6; + rd.in.length = 0; + status = smb2_read(tree, tree, &rd); + torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done, + "zero byte read did not return 0 bytes"); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Unlock lock */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(lck.out.reserved, 0); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_unlock(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el1[1]; + struct smb2_lock_element el2[1]; + + const char *fname = BASEDIR "\\unlock.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Setup initial parameters */ + lck.in.locks = el1; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + el1[0].offset = 0; + el1[0].length = 10; + el1[0].reserved = 0x00000000; + + /* Take exclusive lock, then unlock it with a shared-unlock call. */ + + torture_comment(torture, "Testing unlock exclusive with shared\n"); + + torture_comment(torture, " taking exclusive lock.\n"); + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " try to unlock the exclusive with a shared " + "unlock call.\n"); + el1[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + torture_comment(torture, " try shared lock on h2, to test the " + "unlock.\n"); + lck.in.file.handle = h2; + el1[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(torture, " unlock the exclusive lock\n"); + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Unlock a shared lock with an exclusive-unlock call. */ + + torture_comment(torture, "Testing unlock shared with exclusive\n"); + + torture_comment(torture, " taking shared lock.\n"); + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_SHARED; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, " try to unlock the shared with an exclusive " + "unlock call.\n"); + el1[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + torture_comment(torture, " try exclusive lock on h2, to test the " + "unlock.\n"); + lck.in.file.handle = h2; + el1[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(torture, " unlock the exclusive lock\n"); + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test unlocking of stacked 0-byte locks. SMB2 0-byte lock behavior + * should be the same as >0-byte behavior. Exclusive locks should be + * unlocked before shared. */ + + torture_comment(torture, "Test unlocking stacked 0-byte locks\n"); + + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].offset = 10; + el1[0].length = 0; + el1[0].reserved = 0x00000000; + el2[0].offset = 5; + el2[0].length = 10; + el2[0].reserved = 0x00000000; + + /* lock 0-byte exclusive */ + el1[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* lock 0-byte shared */ + el1[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test contention */ + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* unlock */ + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test - can we take a shared lock? */ + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el2[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test unlocking of stacked exclusive, shared locks. Exclusive + * should be unlocked before any shared. */ + + torture_comment(torture, "Test unlocking stacked exclusive/shared " + "locks\n"); + + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].offset = 10; + el1[0].length = 10; + el1[0].reserved = 0x00000000; + el2[0].offset = 5; + el2[0].length = 10; + el2[0].reserved = 0x00000000; + + /* lock exclusive */ + el1[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* lock shared */ + el1[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test contention */ + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* unlock */ + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* test - can we take a shared lock? */ + lck.in.locks = el2; + lck.in.file.handle = h2; + el2[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el2[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.locks = el1; + lck.in.file.handle = h; + el1[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_multiple_unlock(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + const char *fname = BASEDIR "\\unlock_multiple.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing multiple unlocks:\n"); + + /* Setup initial parameters */ + lck.in.lock_count = 0x0002; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[1].offset = 10; + el[1].length = 10; + el[1].reserved = 0x00000000; + + /* Test1: Acquire second lock, but not first. */ + torture_comment(torture, " unlock 2 locks, first one not locked. " + "Expect no locks unlocked. \n"); + + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to unlock both locks */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* Second lock should not be unlocked. */ + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } + + /* cleanup */ + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test2: Acquire first lock, but not second. */ + torture_comment(torture, " unlock 2 locks, second one not locked. " + "Expect first lock unlocked.\n"); + + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to unlock both locks */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* First lock should be unlocked. */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test3: Request 2 locks, second will contend. What happens to the + * first? */ + torture_comment(torture, " request 2 locks, second one will contend. " + "Expect both to fail.\n"); + + /* Lock the second range */ + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Request both locks */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* First lock should be unlocked. */ + lck.in.lock_count = 0x0001; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + if (TARGET_IS_W2K8(torture)) { + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + /* Test4: Request unlock and lock. The lock contends, is the unlock + * then relocked? SMB2 doesn't like the lock and unlock requests in the + * same packet. The unlock will succeed, but the lock will return + * INVALID_PARAMETER. This behavior is described in MS-SMB2 + * 3.3.5.14.1 */ + torture_comment(torture, " request unlock and lock, second one will " + "error. Expect the unlock to succeed.\n"); + + /* Lock both ranges */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Attempt to unlock the first range and lock the second. The lock + * request will error. */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* The first lock should've been unlocked */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test10: SMB2 only test. Request unlock and lock in same packet. + * Neither contend. SMB2 doesn't like lock and unlock requests in the + * same packet. The unlock will succeed, but the lock will return + * INVALID_PARAMETER. */ + torture_comment(torture, " request unlock and lock. Unlock will " + "succeed, but lock will fail.\n"); + + /* Lock first range */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Attempt to unlock the first range and lock the second */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* Neither lock should still be locked */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test11: SMB2 only test. Request lock and unlock in same packet. + * Neither contend. SMB2 doesn't like lock and unlock requests in the + * same packet. The lock will succeed, the unlock will fail with + * INVALID_PARAMETER, and the lock will be unlocked before return. */ + torture_comment(torture, " request lock and unlock. Both will " + "fail.\n"); + + /* Lock second range */ + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Attempt to lock the first range and unlock the second */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* First range should be unlocked, second locked. */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + if (TARGET_IS_W2K8(torture)) { + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Test lock stacking + * - some tests ported from BASE-LOCK-LOCK5 + */ +static bool test_stacking(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + const char *fname = BASEDIR "\\stacking.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing lock stacking:\n"); + + /* Setup initial parameters */ + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + + /* Try to take a shared lock, then a shared lock on same handle */ + torture_comment(torture, " stacking a shared on top of a shared" + "lock succeeds.\n"); + + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + + /* Try to take an exclusive lock, then a shared lock on same handle */ + torture_comment(torture, " stacking a shared on top of an exclusive " + "lock succeeds.\n"); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* stacking a shared from a different handle should fail */ + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* ensure the 4th unlock fails */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* ensure a second handle can now take an exclusive lock */ + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to take an exclusive lock, then a shared lock on a + * different handle */ + torture_comment(torture, " stacking a shared on top of an exclusive " + "lock with different handles fails.\n"); + + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Try to take a shared lock, then stack an exclusive with same + * handle. */ + torture_comment(torture, " stacking an exclusive on top of a shared " + "lock fails.\n"); + + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + } + + /* Prove that two exclusive locks do not stack on the same handle. */ + torture_comment(torture, " two exclusive locks do not stack.\n"); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + if (TARGET_IS_W2K8(torture)) { + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + CHECK_STATUS(status, NT_STATUS_OK); + } + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Test lock contention + * - shared lock should contend with exclusive lock on different handle + */ +static bool test_contend(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + const char *fname = BASEDIR "\\contend.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing lock contention:\n"); + + /* Setup initial parameters */ + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + + /* Take an exclusive lock, then a shared lock on different handle */ + torture_comment(torture, " shared should contend on exclusive on " + "different handle.\n"); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.file.handle = h2; + el[0].flags = SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* cleanup */ + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Test locker context + * - test that pid does not affect the locker context + */ +static bool test_context(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + + const char *fname = BASEDIR "\\context.txt"; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing locker context:\n"); + + /* Setup initial parameters */ + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + + /* Take an exclusive lock, then try to unlock with a different pid, + * same handle. This shows that the pid doesn't affect the locker + * context in SMB2. */ + torture_comment(torture, " pid shouldn't affect locker context\n"); + + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + +done: + smb2_util_close(tree, h2); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Test as much of the potential lock range as possible + * - test ported from BASE-LOCK-LOCK3 + */ +static bool test_range(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h2 = {{0}}; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + uint64_t offset, i; + extern int torture_numops; + + const char *fname = BASEDIR "\\range.txt"; + +#define NEXT_OFFSET offset += (~(uint64_t)0) / torture_numops + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = torture_smb2_testfile(tree, fname, &h2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing locks spread across the 64-bit " + "offset range\n"); + + if (TARGET_IS_W2K8(torture)) { + torture_result(torture, TORTURE_SKIP, + "Target has \"pretty please\" bug. A contending lock " + "request on the same handle unlocks the lock."); + goto done; + } + + /* Setup initial parameters */ + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + torture_comment(torture, " establishing %d locks\n", torture_numops); + + for (offset=i=0; i + * perform lock sequence verification ... + * + * ... + * + * <314> Section 3.3.5.14: Windows 7 and Windows Server 2008 R2 perform + * lock sequence verification only when Open.IsResilient is TRUE. + * Windows 8 through Windows 10 v1909 and Windows Server 2012 through + * Windows Server v1909 perform lock sequence verification only when + * Open.IsResilient or Open.IsPersistent is TRUE. + * + * Note <314> also applies to all versions (at least) up to Windows Server v2004. + * + * Hopefully this will be fixed in future Windows versions and they + * will avoid Note <314>. + */ +static bool test_replay_broken_windows(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h; + struct smb2_ioctl ioctl; + struct smb2_lock lck; + struct smb2_lock_element el; + uint8_t res_req[8]; + const char *fname = BASEDIR "\\replay_broken_windows.txt"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB2_10) { + torture_skip(torture, "SMB 2.1.0 Dialect family or above \ + required for Lock Replay tests\n"); + } + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + torture_comment(torture, "Testing Open File:\n"); + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Setup initial parameters + */ + el = (struct smb2_lock_element) { + .length = 100, + .offset = 100, + }; + lck = (struct smb2_lock) { + .in.locks = &el, + .in.lock_count = 0x0001, + .in.file.handle = h + }; + + torture_comment(torture, "Testing Lock Replay detection [ignored]:\n"); + lck.in.lock_sequence = 0x010 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + if (NT_STATUS_IS_OK(status)) { + lck.in.lock_sequence = 0x020 + 0x2; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + lck.in.lock_sequence = 0x010 + 0x1; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + if (smbXcli_conn_protocol(transport->conn) > PROTOCOL_SMB2_10) { + torture_skip_goto(torture, done, + "SMB3 Server implements LockSequence " + "for all handles\n"); + } + } + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + if (smbXcli_conn_protocol(transport->conn) > PROTOCOL_SMB2_10) { + torture_comment(torture, + "\nSMB3 Server implements LockSequence as SMB 2.1.0" + " LEGACY BROKEN Windows!!!\n\n"); + } + torture_comment(torture, + "Testing SMB 2.1.0 LockSequence for ResilientHandles\n"); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Testing Set Resiliency:\n"); + SIVAL(res_req, 0, 1000); /* timeout */ + SIVAL(res_req, 4, 0); /* reserved */ + ioctl = (struct smb2_ioctl) { + .level = RAW_IOCTL_SMB2, + .in.file.handle = h, + .in.function = FSCTL_LMR_REQ_RESILIENCY, + .in.max_output_response = 0, + .in.flags = SMB2_IOCTL_FLAG_IS_FSCTL, + .in.out.data = res_req, + .in.out.length = sizeof(res_req) + }; + status = smb2_ioctl(tree, torture, &ioctl); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Test with an invalid bucket number (only 1..64 are valid). + * With an invalid number, lock replay detection is not performed. + */ + torture_comment(torture, "Testing Lock (ignored) Replay detection " + "(Bucket No: 0 (invalid)) [ignored]:\n"); + lck.in.lock_sequence = 0x000 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 1):\n"); + + /* + * Obtain Exclusive Lock of length 100 bytes using Bucket Num 1 + * and Bucket Seq 1. + */ + lck.in.lock_sequence = 0x010 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Server detects Replay of Byte Range locks using the Lock Sequence + * Numbers. And ignores the requests completely. + */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* status: still locked */ + + /* + * Server will not grant same Byte Range using a different Bucket Seq + */ + lck.in.lock_sequence = 0x010 + 0x2; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 2):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x020 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* status: still locked */ + + /* test with invalid bucket when file is locked */ + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 65 (invalid)) [ignored]:\n"); + + lck.in.lock_sequence = 0x410 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: unlocked */ + + /* + * Lock again for the unlock replay test + */ + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 64):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x400 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * Test Unlock replay detection + */ + lck.in.lock_sequence = 0x400 + 0x2; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* new seq num ==> unlocked */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* replay detected ==> ignored */ + + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); /* same seq num ==> ignored */ + CHECK_STATUS(status, NT_STATUS_OK); + + /* verify it's unlocked: */ + lck.in.lock_sequence = 0x400 + 0x3; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: not locked */ + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/** + * Test lock replay detection + * + * This test check the SMB 3 behaviour of lock sequence checking, + * which should be implemented for all handles. + * + * Make it clear that this test is supposed to pass a + * server implementing the specification: + * + * [MS-SMB2] 3.3.5.14 Receiving an SMB2 LOCK Request + * + * ... + * + * ... if Open.IsResilient or Open.IsDurable or Open.IsPersistent is + * TRUE or if Connection.Dialect belongs to the SMB 3.x dialect family + * and Connection.ServerCapabilities includes + * SMB2_GLOBAL_CAP_MULTI_CHANNEL bit, the server SHOULD<314> + * perform lock sequence verification ... + * + * ... + * + * <314> Section 3.3.5.14: Windows 7 and Windows Server 2008 R2 perform + * lock sequence verification only when Open.IsResilient is TRUE. + * Windows 8 through Windows 10 v1909 and Windows Server 2012 through + * Windows Server v1909 perform lock sequence verification only when + * Open.IsResilient or Open.IsPersistent is TRUE. + * + * Note <314> also applies to all versions (at least) up to Windows Server v2004. + * + * Hopefully this will be fixed in future Windows versions and they + * will avoid Note <314>. + */ +static bool _test_replay_smb3_specification(struct torture_context *torture, + struct smb2_tree *tree, + const char *testname, + bool use_durable) +{ + NTSTATUS status; + bool ret = true; + struct smb2_create io; + struct smb2_handle h; + struct smb2_lock lck; + struct smb2_lock_element el; + char fname[256]; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(torture, "SMB 3.0.0 Dialect family or above \ + required for Lock Replay tests\n"); + } + + snprintf(fname, sizeof(fname), "%s\\%s.dat", BASEDIR, testname); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + torture_comment(torture, "%s: Testing Open File:\n", testname); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = use_durable; + + status = smb2_create(tree, torture, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.out.file.handle; + CHECK_VALUE(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VALUE(io.out.durable_open, use_durable); + CHECK_VALUE(io.out.durable_open_v2, false); + CHECK_VALUE(io.out.persistent_open, false); + + /* + * Setup initial parameters + */ + el = (struct smb2_lock_element) { + .length = 100, + .offset = 100, + }; + lck = (struct smb2_lock) { + .in.locks = &el, + .in.lock_count = 0x0001, + .in.file.handle = h + }; + + torture_comment(torture, + "Testing SMB 3 LockSequence for all Handles\n"); + + /* + * Test with an invalid bucket number (only 1..64 are valid). + * With an invalid number, lock replay detection is not performed. + */ + torture_comment(torture, "Testing Lock (ignored) Replay detection " + "(Bucket No: 0 (invalid)) [ignored]:\n"); + lck.in.lock_sequence = 0x000 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 1):\n"); + + /* + * Obtain Exclusive Lock of length 100 bytes using Bucket Num 1 + * and Bucket Seq 1. + */ + lck.in.lock_sequence = 0x010 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Server detects Replay of Byte Range locks using the Lock Sequence + * Numbers. And ignores the requests completely. + */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* status: still locked */ + + /* + * Server will not grant same Byte Range using a different Bucket Seq + */ + lck.in.lock_sequence = 0x010 + 0x2; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 2):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x020 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* status: still locked */ + + /* test with invalid bucket when file is locked */ + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 65 (invalid)) [ignored]:\n"); + + lck.in.lock_sequence = 0x410 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: unlocked */ + + /* + * Lock again for the unlock replay test + */ + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 64):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x400 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * Test Unlock replay detection + */ + lck.in.lock_sequence = 0x400 + 0x2; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* new seq num ==> unlocked */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* replay detected ==> ignored */ + + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); /* same seq num ==> ignored */ + CHECK_STATUS(status, NT_STATUS_OK); + + /* verify it's unlocked: */ + lck.in.lock_sequence = 0x400 + 0x3; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: not locked */ + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_replay_smb3_specification_durable(struct torture_context *torture, + struct smb2_tree *tree) +{ + const char *testname = "replay_smb3_specification_durable"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB2_10) { + torture_skip(torture, "SMB 2.1.0 Dialect family or above \ + required for Lock Replay tests on durable handles\n"); + } + + return _test_replay_smb3_specification(torture, tree, testname, true); +} + +static bool test_replay_smb3_specification_multi(struct torture_context *torture, + struct smb2_tree *tree) +{ + const char *testname = "replay_smb3_specification_multi"; + struct smb2_transport *transport = tree->session->transport; + uint32_t server_capabilities; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(torture, "SMB 3.0.0 Dialect family or above \ + required for Lock Replay tests on without durable handles\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(torture, "MULTI_CHANNEL is \ + required for Lock Replay tests on without durable handles\n"); + } + + return _test_replay_smb3_specification(torture, tree, testname, false); +} + +/** + * Test lock interaction between smbd and ctdb with tombstone records. + * + * Re-locking an unlocked record could lead to a deadlock between + * smbd and ctdb. Make sure we don't regress. + * + * https://bugzilla.samba.org/show_bug.cgi?id=12005 + * https://bugzilla.samba.org/show_bug.cgi?id=10008 + */ +static bool test_deadlock(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + uint8_t buf[200]; + const char *fname = BASEDIR "\\deadlock.txt"; + + if (!lpcfg_clustering(torture->lp_ctx)) { + torture_skip(torture, "Test must be run on a ctdb cluster\n"); + return true; + } + + status = torture_smb2_testdir(tree, BASEDIR, &_h); + torture_assert_ntstatus_ok(torture, status, + "torture_smb2_testdir failed"); + smb2_util_close(tree, _h); + + status = torture_smb2_testfile(tree, fname, &_h); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "torture_smb2_testfile failed"); + h = &_h; + + ZERO_STRUCT(buf); + status = smb2_util_write(tree, *h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_util_write failed"); + + status = test_smb2_lock(tree, *h, 0, 1, true); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "test_smb2_lock failed"); + + status = test_smb2_unlock(tree, *h, 0, 1); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "test_smb2_unlock failed"); + + status = test_smb2_lock(tree, *h, 0, 1, true); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "test_smb2_lock failed"); + + status = test_smb2_unlock(tree, *h, 0, 1); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "test_smb2_unlock failed"); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* basic testing of SMB2 locking +*/ +struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "lock"); + torture_suite_add_1smb2_test(suite, "valid-request", + test_valid_request); + torture_suite_add_1smb2_test(suite, "rw-none", test_lock_rw_none); + torture_suite_add_1smb2_test(suite, "rw-shared", test_lock_rw_shared); + torture_suite_add_1smb2_test(suite, "rw-exclusive", + test_lock_rw_exclusive); + torture_suite_add_1smb2_test(suite, "auto-unlock", + test_lock_auto_unlock); + torture_suite_add_1smb2_test(suite, "lock", test_lock); + torture_suite_add_1smb2_test(suite, "async", test_async); + torture_suite_add_1smb2_test(suite, "cancel", test_cancel); + torture_suite_add_1smb2_test(suite, "cancel-tdis", test_cancel_tdis); + torture_suite_add_1smb2_test(suite, "cancel-logoff", + test_cancel_logoff); + torture_suite_add_1smb2_test(suite, "errorcode", test_errorcode); + torture_suite_add_1smb2_test(suite, "zerobytelength", + test_zerobytelength); + torture_suite_add_1smb2_test(suite, "zerobyteread", + test_zerobyteread); + torture_suite_add_1smb2_test(suite, "unlock", test_unlock); + torture_suite_add_1smb2_test(suite, "multiple-unlock", + test_multiple_unlock); + torture_suite_add_1smb2_test(suite, "stacking", test_stacking); + torture_suite_add_1smb2_test(suite, "contend", test_contend); + torture_suite_add_1smb2_test(suite, "context", test_context); + torture_suite_add_1smb2_test(suite, "range", test_range); + torture_suite_add_2smb2_test(suite, "overlap", test_overlap); + torture_suite_add_1smb2_test(suite, "truncate", test_truncate); + torture_suite_add_1smb2_test(suite, "replay_broken_windows", + test_replay_broken_windows); + torture_suite_add_1smb2_test(suite, "replay_smb3_specification_durable", + test_replay_smb3_specification_durable); + torture_suite_add_1smb2_test(suite, "replay_smb3_specification_multi", + test_replay_smb3_specification_multi); + torture_suite_add_1smb2_test(suite, "ctdb-delrec-deadlock", test_deadlock); + + suite->description = talloc_strdup(suite, "SMB2-LOCK tests"); + + return suite; +} diff --git a/source4/torture/smb2/mangle.c b/source4/torture/smb2/mangle.c new file mode 100644 index 0000000..09097ee --- /dev/null +++ b/source4/torture/smb2/mangle.c @@ -0,0 +1,341 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - mangling test + Copyright (C) Andrew Tridgell 2002 + Copyright (C) David Mulder 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/dir.h" +#include +#include "../lib/util/util_tdb.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +#undef strcasecmp + +static TDB_CONTEXT *tdb; + +#define NAME_LENGTH 20 + +static unsigned int total, collisions, failures; + +static bool test_one(struct torture_context *tctx, struct smb2_tree *tree, + const char *name) +{ + struct smb2_handle fnum; + const char *shortname; + const char *name2; + NTSTATUS status; + TDB_DATA data; + struct smb2_create io = {0}; + + total++; + + io.in.fname = name; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | + SEC_FILE_EXECUTE; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + status = smb2_create(tree, tree, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "open of %s failed (%s)\n", name, + nt_errstr(status)); + return false; + } + fnum = io.out.file.handle; + + status = smb2_util_close(tree, fnum); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, "close of %s failed (%s)\n", name, + nt_errstr(status)); + return false; + } + + /* get the short name */ + status = smb2_qpathinfo_alt_name(tctx, tree, name, &shortname); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "query altname of %s failed (%s)\n", + name, nt_errstr(status)); + return false; + } + + name2 = talloc_asprintf(tctx, "mangle_test\\%s", shortname); + status = smb2_util_unlink(tree, name2); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, "unlink of %s (%s) failed (%s)\n", + name2, name, nt_errstr(status)); + return false; + } + + /* recreate by short name */ + io = (struct smb2_create){0}; + io.in.fname = name2; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | + SEC_FILE_EXECUTE; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + status = smb2_create(tree, tree, &io); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "open2 of %s failed (%s)\n", name2, + nt_errstr(status)); + return false; + } + fnum = io.out.file.handle; + + status = smb2_util_close(tree, fnum); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, "close of %s failed (%s)\n", name, + nt_errstr(status)); + return false; + } + + /* and unlink by long name */ + status = smb2_util_unlink(tree, name); + if (NT_STATUS_IS_ERR(status)) { + torture_comment(tctx, "unlink2 of %s (%s) failed (%s)\n", + name, name2, nt_errstr(status)); + failures++; + smb2_util_unlink(tree, name2); + return true; + } + + /* see if the short name is already in the tdb */ + data = tdb_fetch_bystring(tdb, shortname); + if (data.dptr) { + /* maybe its a duplicate long name? */ + if (strcasecmp(name, (const char *)data.dptr) != 0) { + /* we have a collision */ + collisions++; + torture_comment(tctx, "Collision between %s and %s" + " -> %s (coll/tot: %u/%u)\n", + name, data.dptr, shortname, collisions, + total); + } + free(data.dptr); + } else { + TDB_DATA namedata; + /* store it for later */ + namedata.dptr = discard_const_p(uint8_t, name); + namedata.dsize = strlen(name)+1; + tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE); + } + + return true; +} + + +static char *gen_name(struct torture_context *tctx) +{ + const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~..."; + unsigned int max_idx = strlen(chars); + unsigned int len; + int i; + char *p = NULL; + char *name = NULL; + + name = talloc_strdup(tctx, "mangle_test\\"); + if (!name) { + return NULL; + } + + len = 1 + random() % NAME_LENGTH; + + name = talloc_realloc(tctx, name, char, strlen(name) + len + 6); + if (!name) { + return NULL; + } + p = name + strlen(name); + + for (i=0;i 5) && (random() % 10 == 0)) { + strlcpy(p, "ABCDE", 6); + } + + /* and a high probability of a good extension length */ + if (random() % 2 == 0) { + char *s = strrchr(p, '.'); + if (s) { + s[4] = 0; + } + } + + return name; +} + + +static bool torture_smb2_mangle(struct torture_context *torture, + struct smb2_tree *tree) +{ + extern int torture_numops; + int i; + bool ok; + NTSTATUS status; + + /* we will use an internal tdb to store the names we have used */ + tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0); + torture_assert(torture, tdb, "ERROR: Failed to open tdb\n"); + + ok = smb2_util_setup_dir(torture, tree, "mangle_test"); + torture_assert(torture, ok, "smb2_util_setup_dir failed\n"); + + for (i=0;idescription = talloc_strdup(suite, "SMB2 name mangling tests"); + + torture_suite_add_1smb2_test(suite, "mangle", torture_smb2_mangle); + torture_suite_add_1smb2_test(suite, "mangled-mask", test_mangled_mask); + return suite; +} diff --git a/source4/torture/smb2/max_allowed.c b/source4/torture/smb2/max_allowed.c new file mode 100644 index 0000000..af8b08a --- /dev/null +++ b/source4/torture/smb2/max_allowed.c @@ -0,0 +1,248 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - deny mode scanning functions + Copyright (C) Andrew Tridgell 2001 + Copyright (C) David Mulder 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/security/security.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +#define MAXIMUM_ALLOWED_FILE "torture_maximum_allowed" +static bool torture_smb2_maximum_allowed(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct security_descriptor *sd = NULL, *sd_orig = NULL; + struct smb2_create io = {0}; + TALLOC_CTX *mem_ctx = NULL; + struct smb2_handle fnum = {{0}}; + int i; + bool ret = true; + NTSTATUS status; + union smb_fileinfo q; + const char *owner_sid = NULL; + bool has_restore_privilege, has_backup_privilege, has_system_security_privilege; + + mem_ctx = talloc_init("torture_maximum_allowed"); + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, + "talloc allocation failed\n"); + + if (!torture_setting_bool(tctx, "sacl_support", true)) + torture_warning(tctx, "Skipping SACL related tests!\n"); + + sd = security_descriptor_dacl_create(mem_ctx, + 0, NULL, NULL, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_READ, + 0, NULL); + torture_assert_goto(tctx, sd != NULL, ret, done, + "security descriptor creation failed\n"); + + /* Blank slate */ + smb2_util_unlink(tree, MAXIMUM_ALLOWED_FILE); + + /* create initial file with restrictive SD */ + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.fname = MAXIMUM_ALLOWED_FILE; + io.in.sec_desc = sd; + + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + talloc_asprintf(tctx, "Incorrect status %s - should be %s\n", + nt_errstr(status), nt_errstr(NT_STATUS_OK))); + fnum = io.out.file.handle; + + /* the correct answers for this test depends on whether the + user has restore privileges. To find that out we first need + to know our SID - get it from the owner_sid of the file we + just created */ + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = fnum; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + talloc_asprintf(tctx, "Incorrect status %s - should be %s\n", + nt_errstr(status), nt_errstr(NT_STATUS_OK))); + sd_orig = q.query_secdesc.out.sd; + + owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); + + status = torture_smb2_check_privilege(tree, + owner_sid, + sec_privilege_name(SEC_PRIV_RESTORE)); + has_restore_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_RESTORE for %s - %s\n", + owner_sid, + has_restore_privilege?"Yes":"No"); + + status = torture_smb2_check_privilege(tree, + owner_sid, + sec_privilege_name(SEC_PRIV_BACKUP)); + has_backup_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_BACKUP for %s - %s\n", + owner_sid, + has_backup_privilege?"Yes":"No"); + + status = torture_smb2_check_privilege(tree, + owner_sid, + sec_privilege_name(SEC_PRIV_SECURITY)); + has_system_security_privilege = NT_STATUS_IS_OK(status); + torture_comment(tctx, "Checked SEC_PRIV_SECURITY for %s - %s\n", + owner_sid, + has_system_security_privilege?"Yes":"No"); + + smb2_util_close(tree, fnum); + + for (i = 0; i < 32; i++) { + uint32_t mask = SEC_FLAG_MAXIMUM_ALLOWED | (1u << i); + /* + * SEC_GENERIC_EXECUTE is a complete subset of + * SEC_GENERIC_READ when mapped to specific bits, + * so we need to include it in the basic OK mask. + */ + uint32_t ok_mask = SEC_RIGHTS_FILE_READ | SEC_GENERIC_READ | SEC_GENERIC_EXECUTE | + SEC_STD_DELETE | SEC_STD_WRITE_DAC; + + /* + * Now SEC_RIGHTS_PRIV_RESTORE and SEC_RIGHTS_PRIV_BACKUP + * don't include any generic bits (they're used directly + * in the fileserver where the generic bits have already + * been mapped into file specific bits) we need to add the + * generic bits to the ok_mask when we have these privileges. + */ + if (has_restore_privilege) { + ok_mask |= SEC_RIGHTS_PRIV_RESTORE|SEC_GENERIC_WRITE; + } + if (has_backup_privilege) { + ok_mask |= SEC_RIGHTS_PRIV_BACKUP|SEC_GENERIC_READ; + } + if (has_system_security_privilege) { + ok_mask |= SEC_FLAG_SYSTEM_SECURITY; + } + + /* Skip all SACL related tests. */ + if ((!torture_setting_bool(tctx, "sacl_support", true)) && + (mask & SEC_FLAG_SYSTEM_SECURITY)) + continue; + + io = (struct smb2_create){0}; + io.in.desired_access = mask; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.impersonation_level = + NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.fname = MAXIMUM_ALLOWED_FILE; + + status = smb2_create(tree, mem_ctx, &io); + if (mask & ok_mask || + mask == SEC_FLAG_MAXIMUM_ALLOWED) { + torture_assert_ntstatus_ok_goto(tctx, status, ret, + done, talloc_asprintf(tctx, + "Incorrect status %s - should be %s\n", + nt_errstr(status), nt_errstr(NT_STATUS_OK))); + } else { + if (mask & SEC_FLAG_SYSTEM_SECURITY) { + torture_assert_ntstatus_equal_goto(tctx, + status, NT_STATUS_PRIVILEGE_NOT_HELD, + ret, done, talloc_asprintf(tctx, + "Incorrect status %s - should be %s\n", + nt_errstr(status), + nt_errstr(NT_STATUS_PRIVILEGE_NOT_HELD))); + } else { + torture_assert_ntstatus_equal_goto(tctx, + status, NT_STATUS_ACCESS_DENIED, + ret, done, talloc_asprintf(tctx, + "Incorrect status %s - should be %s\n", + nt_errstr(status), + nt_errstr(NT_STATUS_ACCESS_DENIED))); + } + } + + fnum = io.out.file.handle; + + smb2_util_close(tree, fnum); + } + + done: + smb2_util_unlink(tree, MAXIMUM_ALLOWED_FILE); + talloc_free(mem_ctx); + return ret; +} + +static bool torture_smb2_read_only_file(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create c; + struct smb2_handle h = {{0}}; + bool ret = true; + NTSTATUS status; + + smb2_deltree(tree, MAXIMUM_ALLOWED_FILE); + + c = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_ALL, + .in.file_attributes = FILE_ATTRIBUTE_READONLY, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.fname = MAXIMUM_ALLOWED_FILE, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h = c.out.file.handle; + smb2_util_close(tree, h); + ZERO_STRUCT(h); + + c = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_READONLY, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.fname = MAXIMUM_ALLOWED_FILE, + }; + + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto( + tctx, status, ret, done, + "Failed to open READ-ONLY file with SEC_FLAG_MAXIMUM_ALLOWED\n"); + h = c.out.file.handle; + smb2_util_close(tree, h); + ZERO_STRUCT(h); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_deltree(tree, MAXIMUM_ALLOWED_FILE); + return ret; +} + +struct torture_suite *torture_smb2_max_allowed(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "maximum_allowed"); + + torture_suite_add_1smb2_test(suite, "maximum_allowed", torture_smb2_maximum_allowed); + torture_suite_add_1smb2_test(suite, "read_only", torture_smb2_read_only_file); + return suite; +} diff --git a/source4/torture/smb2/maxfid.c b/source4/torture/smb2/maxfid.c new file mode 100644 index 0000000..5f0b9fe --- /dev/null +++ b/source4/torture/smb2/maxfid.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 maxfid test + + Copyright (C) Christof Schmitt 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +bool torture_smb2_maxfid(struct torture_context *tctx) +{ + bool ret = true; + NTSTATUS status; + struct smb2_tree *tree = NULL; + const char *dname = "smb2_maxfid"; + size_t i, maxfid; + struct smb2_handle *handles, dir_handle = { }; + size_t max_handles; + + /* + * We limited this to 65520 as socket_wrapper has a limit of + * 65535 (0xfff0) open sockets. + * + * It could be increased by setting the following env variable: + * + * SOCKET_WRAPPER_MAX_SOCKETS=100000 + */ + max_handles = torture_setting_int(tctx, "maxopenfiles", 65520); + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + handles = talloc_array(tctx, struct smb2_handle, max_handles); + if (handles == 0) { + torture_fail(tctx, "Could not allocate handles array.\n"); + return false; + } + + smb2_deltree(tree, dname); + + status = torture_smb2_testdir(tree, dname, &dir_handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed"); + smb2_util_close(tree, dir_handle); + + torture_comment(tctx, "Creating subdirectories\n"); + + for (i = 0; i < max_handles; i += 1000) { + char *name; + struct smb2_create create = { }; + struct smb2_close close = { }; + + name = talloc_asprintf(tctx, "%s\\%zu", dname, i / 1000); + torture_assert_goto(tctx, (name != NULL), ret, done, + "no memory for directory name\n"); + + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = name; + + status = smb2_create(tree, tctx, &create); + talloc_free(name); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE directory failed\n"); + + close.in.file.handle = create.out.file.handle; + status = smb2_close(tree, &close); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE directory failed\n"); + } + + torture_comment(tctx, "Testing maximum number of open files\n"); + + for (i = 0; i < max_handles; i++) { + char *name; + struct smb2_create create = { }; + + name = talloc_asprintf(tctx, "%s\\%zu\\%zu", dname, i / 1000, i); + torture_assert_goto(tctx, (name != NULL), ret, done, + "no memory for file name\n"); + + create.in.desired_access = SEC_RIGHTS_DIR_ALL; + create.in.create_options = 0; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.fname = name; + + status = smb2_create(tree, tctx, &create); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "create of %s failed: %s\n", + name, nt_errstr(status)); + talloc_free(name); + break; + } + talloc_free(name); + + handles[i] = create.out.file.handle; + } + + maxfid = i; + if (maxfid == max_handles) { + torture_comment(tctx, "Reached test limit of %zu open files. " + "Adjust to higher test with " + "--option=torture:maxopenfiles=NNN\n", maxfid); + } + + torture_comment(tctx, "Cleanup open files\n"); + + for (i = 0; i < maxfid; i++) { + status = smb2_util_close(tree, handles[i]); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE failed\n"); + } + +done: + smb2_deltree(tree, dname); + talloc_free(handles); + + return ret; +} diff --git a/source4/torture/smb2/maxwrite.c b/source4/torture/smb2/maxwrite.c new file mode 100644 index 0000000..bc52ebc --- /dev/null +++ b/source4/torture/smb2/maxwrite.c @@ -0,0 +1,137 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 write operations + + Copyright (C) Andrew Tridgell 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "librpc/gen_ndr/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#define FNAME "testmaxwrite.dat" + +/* + test writing +*/ +static NTSTATUS torture_smb2_write(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_handle handle) +{ + struct smb2_write w; + struct smb2_read r; + NTSTATUS status; + int i, len; + int max = 80000000; + int min = 1; + + while (max > min) { + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + + len = 1+(min+max)/2; + + ZERO_STRUCT(w); + w.in.file.handle = handle; + w.in.offset = 0; + w.in.data = data_blob_talloc(tmp_ctx, NULL, len); + + for (i=0;i. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "libcli/smb_composite/smb_composite.h" + +#define BASEDIR "mkdirtest" + +/* + test mkdir ops +*/ +bool torture_smb2_mkdir(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_handle h = {{0}}; + const char *path = BASEDIR "\\mkdir.dir"; + NTSTATUS status; + bool ret = true; + + ret = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert(tctx, ret, "Failed to setup up test directory: " BASEDIR); + + /* + basic mkdir + */ + status = smb2_util_mkdir(tree, path); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status"); + + torture_comment(tctx, "Testing mkdir collision\n"); + + /* 2nd create */ + status = smb2_util_mkdir(tree, path); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_COLLISION, + ret, done, "Incorrect status"); + + /* basic rmdir */ + status = smb2_util_rmdir(tree, path); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status"); + + status = smb2_util_rmdir(tree, path); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Incorrect status"); + + torture_comment(tctx, "Testing mkdir collision with file\n"); + + /* name collision with a file */ + smb2_create_complex_file(tctx, tree, path, &h); + smb2_util_close(tree, h); + status = smb2_util_mkdir(tree, path); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_COLLISION, + ret, done, "Incorrect status"); + + torture_comment(tctx, "Testing rmdir with file\n"); + + /* delete a file with rmdir */ + status = smb2_util_rmdir(tree, path); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NOT_A_DIRECTORY, + ret, done, "Incorrect status"); + + smb2_util_unlink(tree, path); + + torture_comment(tctx, "Testing invalid dir\n"); + + /* create an invalid dir */ + status = smb2_util_mkdir(tree, "..\\..\\.."); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_PATH_SYNTAX_BAD, + ret, done, "Incorrect status"); + + torture_comment(tctx, "Testing t2mkdir bad path\n"); + status = smb2_util_mkdir(tree, BASEDIR "\\bad_path\\bad_path"); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_PATH_NOT_FOUND, + ret, done, "Incorrect status"); + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c new file mode 100644 index 0000000..6b6e00e --- /dev/null +++ b/source4/torture/smb2/multichannel.c @@ -0,0 +1,2743 @@ +/* + * Unix SMB/CIFS implementation. + * + * test SMB2 multichannel operations + * + * Copyright (C) Guenther Deschner, 2016 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "librpc/gen_ndr/ndr_ioctl.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/security/security.h" +#include "libcli/resolve/resolve.h" +#include "lib/socket/socket.h" +#include "lib/param/param.h" +#include "lib/events/events.h" +#include "oplock_break_handler.h" +#include "lease_break_handler.h" +#include "torture/smb2/block.h" + +#define BASEDIR "multichanneltestdir" + +#define CHECK_STATUS(status, correct) \ + torture_assert_ntstatus_equal_goto(tctx, status, correct,\ + ret, done, "") + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \ + " got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + goto done; \ + } } while (0) + +#define CHECK_VAL_GREATER_THAN(v, gt_val) do { \ + if ((v) <= (gt_val)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s): wrong value for %s got 0x%x - " \ + "should be greater than 0x%x\n", \ + __location__, #v, (int)v, (int)gt_val); \ + ret = false; \ + goto done; \ + } } while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, \ + NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while (0) + +#define CHECK_PTR(ptr, correct) do { \ + if ((ptr) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got 0x%p - should be 0x%p\n", \ + __location__, #ptr, ptr, correct); \ + ret = false; \ + goto done; \ + } } while (0) + +#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \ + do { \ + CHECK_VAL((__io)->out.lease_response.lease_version, 1); \ + if (__oplevel) { \ + CHECK_VAL((__io)->out.oplock_level, \ + SMB2_OPLOCK_LEVEL_LEASE); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\ + (__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\ + ~(__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_state,\ + smb2_util_lease_state(__state)); \ + } else { \ + CHECK_VAL((__io)->out.oplock_level,\ + SMB2_OPLOCK_LEVEL_NONE); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\ + 0); \ + CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\ + 0); \ + CHECK_VAL((__io)->out.lease_response.lease_state, 0); \ + } \ + \ + CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \ + CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \ + } while (0) + +#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \ + do { \ + CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \ + if (__oplevel) { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \ + } else { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \ + } \ + \ + CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \ + if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \ + CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \ + CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \ + } \ + CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \ + } while(0) + +#define CHECK_LEASE_BREAK_V2(__lb, __key, __from, __to, __break_flags, __new_epoch) \ + do { \ + CHECK_VAL((__lb).current_lease.lease_key.data[0], (__key)); \ + CHECK_VAL((__lb).current_lease.lease_key.data[1], ~(__key)); \ + CHECK_VAL((__lb).current_lease.lease_state, smb2_util_lease_state(__from)); \ + CHECK_VAL((__lb).new_epoch, (__new_epoch)); \ + CHECK_VAL((__lb).break_flags, (__break_flags)); \ + CHECK_VAL((__lb).new_lease_state, smb2_util_lease_state(__to)); \ + } while(0) + +static bool test_ioctl_network_interface_info(struct torture_context *tctx, + struct smb2_tree *tree, + struct fsctl_net_iface_info *info) +{ + union smb_ioctl ioctl; + struct smb2_handle fh; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, + "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + ZERO_STRUCT(ioctl); + + ioctl.smb2.level = RAW_IOCTL_SMB2; + + fh.data[0] = UINT64_MAX; + fh.data[1] = UINT64_MAX; + + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO; + /* Windows client sets this to 64KiB */ + ioctl.smb2.in.max_output_response = 0x10000; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + torture_assert_ntstatus_ok(tctx, + smb2_ioctl(tree, tctx, &ioctl.smb2), + "FSCTL_QUERY_NETWORK_INTERFACE_INFO failed"); + + torture_assert(tctx, + (ioctl.smb2.out.out.length != 0), + "no interface info returned???"); + + torture_assert_ndr_success(tctx, + ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info, + (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info), + "failed to ndr pull"); + + if (DEBUGLVL(1)) { + NDR_PRINT_DEBUG(fsctl_net_iface_info, info); + } + + return true; +} + +static bool test_multichannel_interface_info(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct fsctl_net_iface_info info; + + return test_ioctl_network_interface_info(tctx, tree, &info); +} + +static struct smb2_tree *test_multichannel_create_channel( + struct torture_context *tctx, + const char *host, + const char *share, + struct cli_credentials *credentials, + const struct smbcli_options *_transport_options, + struct smb2_tree *parent_tree + ) +{ + struct smbcli_options transport_options = *_transport_options; + NTSTATUS status; + struct smb2_transport *transport; + struct smb2_session *session; + bool ret = true; + struct smb2_tree *tree; + + if (parent_tree) { + transport_options.only_negprot = true; + } + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &transport_options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport = tree->session->transport; + transport->oplock.handler = torture_oplock_ack_handler; + transport->oplock.private_data = tree; + transport->lease.handler = torture_lease_handler; + transport->lease.private_data = tree; + torture_comment(tctx, "established transport [%p]\n", transport); + + /* + * If parent tree is set, bind the session to the parent transport + */ + if (parent_tree) { + session = smb2_session_channel(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + parent_tree, parent_tree->session); + torture_assert_goto(tctx, session != NULL, ret, done, + "smb2_session_channel failed"); + + tree->smbXcli = parent_tree->smbXcli; + tree->session = session; + status = smb2_session_setup_spnego(session, + credentials, + 0 /* previous_session_id */); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "bound new session to parent\n"); + } + /* + * We absolutely need to make sure to send something over this + * connection to register the oplock break handler with the smb client + * connection. If we do not send something (at least a keepalive), we + * will *NEVER* receive anything over this transport. + */ + smb2_keepalive(transport); + +done: + if (ret) { + return tree; + } else { + return NULL; + } +} + +bool test_multichannel_create_channel_array( + struct torture_context *tctx, + const char *host, + const char *share, + struct cli_credentials *credentials, + struct smbcli_options *transport_options, + uint8_t num_trees, + struct smb2_tree **trees) +{ + uint8_t i; + + transport_options->client_guid = GUID_random(); + + for (i = 0; i < num_trees; i++) { + struct smb2_tree *parent_tree = NULL; + struct smb2_tree *tree = NULL; + struct smb2_transport *transport = NULL; + uint16_t local_port = 0; + + if (i > 0) { + parent_tree = trees[0]; + } + + torture_comment(tctx, "Setting up connection %d\n", i); + tree = test_multichannel_create_channel(tctx, host, share, + credentials, transport_options, + parent_tree); + torture_assert(tctx, tree, "failed to created new channel"); + + trees[i] = tree; + transport = tree->session->transport; + local_port = torture_get_local_port_from_transport(transport); + torture_comment(tctx, "transport[%d] uses tcp port: %d\n", + i, local_port); + } + + return true; +} + +bool test_multichannel_create_channels( + struct torture_context *tctx, + const char *host, + const char *share, + struct cli_credentials *credentials, + struct smbcli_options *transport_options, + struct smb2_tree **tree2A, + struct smb2_tree **tree2B, + struct smb2_tree **tree2C + ) +{ + struct smb2_tree **trees = NULL; + size_t num_trees = 0; + bool ret; + + torture_assert(tctx, tree2A, "tree2A required!"); + num_trees += 1; + torture_assert(tctx, tree2B, "tree2B required!"); + num_trees += 1; + if (tree2C != NULL) { + num_trees += 1; + } + trees = talloc_zero_array(tctx, struct smb2_tree *, num_trees); + torture_assert(tctx, trees, "out of memory"); + + ret = test_multichannel_create_channel_array(tctx, host, share, credentials, + transport_options, + num_trees, trees); + if (!ret) { + return false; + } + + *tree2A = trees[0]; + *tree2B = trees[1]; + if (tree2C != NULL) { + *tree2C = trees[2]; + } + + return true; +} + +static void test_multichannel_free_channels(struct smb2_tree *tree2A, + struct smb2_tree *tree2B, + struct smb2_tree *tree2C) +{ + TALLOC_FREE(tree2A); + TALLOC_FREE(tree2B); + TALLOC_FREE(tree2C); +} + +static bool test_multichannel_initial_checks(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + struct smb2_transport *transport1 = tree1->session->transport; + uint32_t server_capabilities; + struct fsctl_net_iface_info info; + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip_goto(tctx, fail, + "SMB 3.X Dialect family required for " + "Multichannel tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip_goto(tctx, fail, + "Server does not support multichannel."); + } + + torture_assert(tctx, + test_ioctl_network_interface_info(tctx, tree1, &info), + "failed to retrieve network interface info"); + + return true; +fail: + return false; +} + +static void test_multichannel_init_smb_create(struct smb2_create *io) +{ + io->in.durable_open = false; + io->in.durable_open_v2 = true; + io->in.persistent_open = false; + io->in.create_guid = GUID_random(); + io->in.timeout = 0x493E0; /* 300000 */ + /* windows 2016 returns 300000 0x493E0 */ +} + +/* Timer handler function notifies the registering function that time is up */ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; +} + +/* + * Oplock break - Test 1 + * Test to confirm that server sends oplock breaks as expected. + * open file1 in session 2A + * open file2 in session 2B + * open file1 in session 1 + * oplock break received + * open file1 in session 1 + * oplock break received + * Cleanup + */ +static bool test_multichannel_oplock_break_test1(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client1_file2 = {{0}}; + struct smb2_handle h_client1_file3 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_handle h_client2_file2 = {{0}}; + struct smb2_handle h_client2_file3 = {{0}}; + struct smb2_create io1, io2, io3; + bool ret = true; + const char *fname1 = BASEDIR "\\oplock_break_test1.dat"; + const char *fname2 = BASEDIR "\\oplock_break_test2.dat"; + const char *fname3 = BASEDIR "\\oplock_break_test3.dat"; + struct smb2_tree *tree2A = NULL; + struct smb2_tree *tree2B = NULL; + struct smb2_tree *tree2C = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Oplock break retry: Test1\n"); + + torture_reset_break_info(tctx, &break_info); + + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io1, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io1); + + smb2_oplock_create_share(&io2, fname2, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io2); + + smb2_oplock_create_share(&io3, fname3, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io3); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channels(tctx, host, share, + credentials, + &transport2_options, + &tree2A, &tree2B, NULL); + torture_assert(tctx, ret, "Could not create channels.\n"); + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via session 2A\n"); + status = smb2_create(tree2A, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + /* 2b opens file2 */ + torture_comment(tctx, "client2 opens fname2 via session 2B\n"); + status = smb2_create(tree2B, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + + /* 1 opens file1 - batchoplock break? */ + torture_comment(tctx, "client1 opens fname1 via session 1\n"); + io1.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + + torture_reset_break_info(tctx, &break_info); + + /* 1 opens file2 - batchoplock break? */ + torture_comment(tctx, "client1 opens fname2 via session 1\n"); + io2.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree1, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + + /* cleanup everything */ + torture_reset_break_info(tctx, &break_info); + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + smb2_util_close(tree2A, h_client2_file1); + smb2_util_close(tree2A, h_client2_file2); + smb2_util_close(tree2A, h_client2_file3); + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(break_info.count, 0); + test_multichannel_free_channels(tree2A, tree2B, tree2C); + tree2A = tree2B = tree2C = NULL; +done: + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + if (tree2A != NULL) { + smb2_util_close(tree2A, h_client2_file1); + smb2_util_close(tree2A, h_client2_file2); + smb2_util_close(tree2A, h_client2_file3); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + smb2_deltree(tree1, BASEDIR); + + test_multichannel_free_channels(tree2A, tree2B, tree2C); + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Oplock Break Test 2 + * Test to see if oplock break retries are sent by the server. + * Also checks to see if new channels can be created and used + * after an oplock break retry. + * open file1 in 2A + * open file2 in 2B + * open file1 in session 1 + * oplock break received + * block channel on which oplock break received + * open file2 in session 1 + * oplock break not received. Retry received. + * file opened + * write to file2 on 2B + * Break sent to session 1(which has file2 open) + * Break sent to session 2A(which has read oplock) + * close file1 in session 1 + * open file1 with session 1 + * unblock blocked channel + * disconnect blocked channel + * connect channel 2D + * open file3 in 2D + * open file3 in session 1 + * receive break + */ +static bool test_multichannel_oplock_break_test2(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client1_file2 = {{0}}; + struct smb2_handle h_client1_file3 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_handle h_client2_file2 = {{0}}; + struct smb2_handle h_client2_file3 = {{0}}; + struct smb2_create io1, io2, io3; + bool ret = true; + const char *fname1 = BASEDIR "\\oplock_break_test1.dat"; + const char *fname2 = BASEDIR "\\oplock_break_test2.dat"; + const char *fname3 = BASEDIR "\\oplock_break_test3.dat"; + struct smb2_tree *tree2A = NULL; + struct smb2_tree *tree2B = NULL; + struct smb2_tree *tree2C = NULL; + struct smb2_tree *tree2D = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2 = NULL; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + DATA_BLOB blob; + bool block_setup = false; + bool block_ok = false; + bool unblock_ok = false; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Oplock break retry: Test2\n"); + + torture_reset_break_info(tctx, &break_info); + + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io1, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io1); + + smb2_oplock_create_share(&io2, fname2, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io2); + + smb2_oplock_create_share(&io3, fname3, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + test_multichannel_init_smb_create(&io3); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channels(tctx, host, share, + credentials, + &transport2_options, + &tree2A, &tree2B, &tree2C); + torture_assert(tctx, ret, "Could not create channels.\n"); + + torture_comment(tctx, "client2 opens fname1 via session 2A\n"); + io1.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree2A, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + + torture_comment(tctx, "client2 opens fname2 via session 2B\n"); + io2.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree2B, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + + torture_comment(tctx, "client1 opens fname1 via session 1\n"); + io1.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + + /* We use the transport over which this oplock break was received */ + transport2 = break_info.received_transport; + torture_reset_break_info(tctx, &break_info); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + /* block channel */ + block_ok = test_block_smb2_transport(tctx, transport2); + + torture_comment(tctx, "client1 opens fname2 via session 1\n"); + io2.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree1, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s")); + + /* + * Samba downgrades oplock to a level 2 oplock. + * Windows 2016 revokes oplock + */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + torture_reset_break_info(tctx, &break_info); + + torture_comment(tctx, "Trying write to file2 on tree2B\n"); + + blob = data_blob_string_const("Here I am"); + status = smb2_util_write(tree2B, + h_client2_file2, + blob.data, + 0, + blob.length); + torture_assert_ntstatus_ok(tctx, status, + "failed to write file2 via channel 2B"); + + /* + * Samba: Write triggers 2 oplock breaks + * for session 1 which has file2 open + * for session 2 which has type 2 oplock + * Windows 2016: Only one oplock break for session 1 + */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL_GREATER_THAN(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + torture_comment(tctx, "client1 closes fname2 via session 1\n"); + smb2_util_close(tree1, h_client1_file2); + + torture_comment(tctx, "client1 opens fname2 via session 1 again\n"); + io2.in.oplock_level = smb2_util_oplock_level("b"); + status = smb2_create(tree1, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file2 = io2.out.file.handle; + io2.out.alloc_size = 0; + io2.out.size = 0; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s")); + + /* + * now add a fourth channel and repeat the test, we need to reestablish + * transport2 because the remote end has invalidated our connection + */ + torture_comment(tctx, "Connecting session 2D\n"); + tree2D = test_multichannel_create_channel(tctx, host, share, + credentials, &transport2_options, tree2B); + if (!tree2D) { + goto done; + } + + torture_reset_break_info(tctx, &break_info); + torture_comment(tctx, "client 2 opening fname3 over transport2D\n"); + status = smb2_create(tree2D, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("b")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "client1 opens fname3 via session 1\n"); + status = smb2_create(tree1, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s")); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + +done: + if (block_ok && !unblock_ok) { + test_unblock_smb2_transport(tctx, transport2); + } + test_cleanup_blocked_transports(tctx); + + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + if (tree2B != NULL) { + smb2_util_close(tree2B, h_client2_file1); + smb2_util_close(tree2B, h_client2_file2); + smb2_util_close(tree2B, h_client2_file3); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + smb2_deltree(tree1, BASEDIR); + + test_multichannel_free_channels(tree2A, tree2B, tree2C); + if (tree2D != NULL) { + TALLOC_FREE(tree2D); + } + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +struct test_multichannel_oplock_break_state; + +struct test_multichannel_oplock_break_channel { + struct test_multichannel_oplock_break_state *state; + size_t idx; + char name[64]; + struct smb2_tree *tree; + bool blocked; + struct timeval break_time; + double full_duration; + double relative_duration; + uint8_t level; + size_t break_num; +}; + +struct test_multichannel_oplock_break_state { + struct torture_context *tctx; + struct timeval open_req_time; + struct timeval open_rep_time; + size_t num_breaks; + struct timeval last_break_time; + struct test_multichannel_oplock_break_channel channels[32]; +}; + +static bool test_multichannel_oplock_break_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct test_multichannel_oplock_break_channel *c = + (struct test_multichannel_oplock_break_channel *)private_data; + struct test_multichannel_oplock_break_state *state = c->state; + + c->break_time = timeval_current(); + c->full_duration = timeval_elapsed2(&state->open_req_time, + &c->break_time); + c->relative_duration = timeval_elapsed2(&state->last_break_time, + &c->break_time); + state->last_break_time = c->break_time; + c->level = level; + c->break_num = ++state->num_breaks; + + torture_comment(state->tctx, "Got OPLOCK break %zu on %s after %f ( %f)\n", + c->break_num, c->name, + c->relative_duration, + c->full_duration); + + return torture_oplock_ack_handler(transport, handle, level, c->tree); +} + +static bool test_multichannel_oplock_break_test3_windows(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct test_multichannel_oplock_break_state state = { + .tctx = tctx, + }; + struct test_multichannel_oplock_break_channel *open2_channel = NULL; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_create io1; + struct smb2_create io2; + bool ret = true; + const char *fname1 = BASEDIR "\\oplock_break_test3w.dat"; + struct smb2_tree *trees2[32] = { NULL, }; + size_t i; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + bool block_setup = false; + bool block_ok = false; + double open_duration; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Oplock break retry: Test3 (Windows behavior)\n"); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io2, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channel_array(tctx, host, share, credentials, + &transport2_options, + ARRAY_SIZE(trees2), trees2); + torture_assert(tctx, ret, "Could not create channels.\n"); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = trees2[i]->session->transport; + + c->state = &state; + c->idx = i+1; + c->tree = trees2[i]; + snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx); + + t->oplock.handler = test_multichannel_oplock_break_handler; + t->oplock.private_data = c; + } + + open2_channel = &state.channels[0]; + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via %s\n", + open2_channel->name); + status = smb2_create(open2_channel->tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io2.out.durable_open_v2, false); + CHECK_VAL(io2.out.timeout, io2.in.timeout); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(break_info.count, 0); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = c->tree->session->transport; + + torture_comment(tctx, "Blocking %s\n", c->name); + block_ok = _test_block_smb2_transport(tctx, t, c->name); + torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport"); + c->blocked = true; + } + + /* 1 opens file2 */ + torture_comment(tctx, + "Client opens fname1 with session 1 with all %zu blocked\n", + ARRAY_SIZE(trees2)); + smb2_oplock_create_share(&io1, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + CHECK_VAL(lease_break_info.count, 0); + state.open_req_time = timeval_current(); + state.last_break_time = state.open_req_time; + status = smb2_create(tree1, mem_ctx, &io1); + state.open_rep_time = timeval_current(); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s")); + + CHECK_VAL(break_info.count, 1); + + open_duration = timeval_elapsed2(&state.open_req_time, + &state.open_rep_time); + torture_comment(tctx, "open_duration: %f\n", open_duration); + CHECK_VAL_GREATER_THAN(open_duration, 35); + + if (break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected oplock break!!\n"); + } else { + torture_comment(tctx, "Received %d oplock break(s)!!\n", + break_info.count); + } + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + size_t expected_break_num = 0; + + /* + * Only the latest channel gets a break notification + */ + if (i == (ARRAY_SIZE(state.channels) - 1)) { + expected_break_num = 1; + } + + torture_comment(tctx, "Verify %s\n", c->name); + torture_assert_int_equal(tctx, c->break_num, expected_break_num, + "Got oplock break on wrong channel"); + if (expected_break_num != 0) { + CHECK_VAL(c->level, smb2_util_oplock_level("s")); + } + } + +done: + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = NULL; + + if (!c->blocked) { + continue; + } + + t = c->tree->session->transport; + + torture_comment(tctx, "Unblocking %s\n", c->name); + _test_unblock_smb2_transport(tctx, t, c->name); + c->blocked = false; + } + if (block_setup) { + test_cleanup_blocked_transports(tctx); + } + + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + if (trees2[0] != NULL) { + smb2_util_close(trees2[0], h_client2_file1); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_deltree(tree1, BASEDIR); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + if (trees2[i] == NULL) { + continue; + } + TALLOC_FREE(trees2[i]); + } + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_multichannel_oplock_break_test3_specification(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct test_multichannel_oplock_break_state state = { + .tctx = tctx, + }; + struct test_multichannel_oplock_break_channel *open2_channel = NULL; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_create io1; + struct smb2_create io2; + bool ret = true; + const char *fname1 = BASEDIR "\\oplock_break_test3s.dat"; + struct smb2_tree *trees2[32] = { NULL, }; + size_t i; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + bool block_setup = false; + bool block_ok = false; + double open_duration; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Oplock break retry: Test3 (Specification behavior)\n"); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io2, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channel_array(tctx, host, share, credentials, + &transport2_options, + ARRAY_SIZE(trees2), trees2); + torture_assert(tctx, ret, "Could not create channels.\n"); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = trees2[i]->session->transport; + + c->state = &state; + c->idx = i+1; + c->tree = trees2[i]; + snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx); + + t->oplock.handler = test_multichannel_oplock_break_handler; + t->oplock.private_data = c; + } + + open2_channel = &state.channels[0]; + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via %s\n", + open2_channel->name); + status = smb2_create(open2_channel->tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io2.out.durable_open_v2, false); + CHECK_VAL(io2.out.timeout, io2.in.timeout); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(break_info.count, 0); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = c->tree->session->transport; + + torture_comment(tctx, "Blocking %s\n", c->name); + block_ok = _test_block_smb2_transport(tctx, t, c->name); + torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport"); + c->blocked = true; + } + + /* 1 opens file2 */ + torture_comment(tctx, + "Client opens fname1 with session 1 with all %zu blocked\n", + ARRAY_SIZE(trees2)); + smb2_oplock_create_share(&io1, fname1, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + CHECK_VAL(lease_break_info.count, 0); + state.open_req_time = timeval_current(); + state.last_break_time = state.open_req_time; + status = smb2_create(tree1, mem_ctx, &io1); + state.open_rep_time = timeval_current(); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + + CHECK_VAL_GREATER_THAN(break_info.count, 1); + + open_duration = timeval_elapsed2(&state.open_req_time, + &state.open_rep_time); + torture_comment(tctx, "open_duration: %f\n", open_duration); + if (break_info.count < ARRAY_SIZE(state.channels)) { + CHECK_VAL_GREATER_THAN(open_duration, 35); + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s")); + } else { + CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); + } + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + if (break_info.count >= ARRAY_SIZE(state.channels)) { + break; + } + torture_comment(tctx, "Received %d oplock break(s) wait for more!!\n", + break_info.count); + torture_wait_for_oplock_break(tctx); + } + + if (break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected oplock break!!\n"); + } else { + torture_comment(tctx, "Received %d oplock break(s)!!\n", + break_info.count); + } + + if (break_info.count < ARRAY_SIZE(state.channels)) { + CHECK_VAL_GREATER_THAN(break_info.count, 3); + } else { + CHECK_VAL(break_info.count, ARRAY_SIZE(state.channels)); + } + + for (i = 0; i < break_info.count; i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + + torture_comment(tctx, "Verify %s\n", c->name); + torture_assert_int_equal(tctx, c->break_num, c->idx, + "Got oplock break on wrong channel"); + CHECK_VAL(c->level, smb2_util_oplock_level("s")); + } + +done: + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_oplock_break_channel *c = &state.channels[i]; + struct smb2_transport *t = NULL; + + if (!c->blocked) { + continue; + } + + t = c->tree->session->transport; + + torture_comment(tctx, "Unblocking %s\n", c->name); + _test_unblock_smb2_transport(tctx, t, c->name); + c->blocked = false; + } + if (block_setup) { + test_cleanup_blocked_transports(tctx); + } + + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + if (trees2[0] != NULL) { + smb2_util_close(trees2[0], h_client2_file1); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_deltree(tree1, BASEDIR); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + if (trees2[i] == NULL) { + continue; + } + TALLOC_FREE(trees2[i]); + } + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +static const uint64_t LEASE1F1 = 0xBADC0FFEE0DDF00Dull; +static const uint64_t LEASE1F2 = 0xBADC0FFEE0DDD00Dull; +static const uint64_t LEASE1F3 = 0xDADC0FFEE0DDD00Dull; +static const uint64_t LEASE2F1 = 0xDEADBEEFFEEDBEADull; +static const uint64_t LEASE2F2 = 0xDAD0FFEDD00DF00Dull; +static const uint64_t LEASE2F3 = 0xBAD0FFEDD00DF00Dull; + +/* + * Lease Break Test 1: + * Test to check if lease breaks are sent by the server as expected. + * open file1 in session 2A + * open file2 in session 2B + * open file3 in session 2C + * open file1 in session 1 + * lease break sent + * open file2 in session 1 + * lease break sent + * open file3 in session 1 + * lease break sent + */ +static bool test_multichannel_lease_break_test1(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client1_file2 = {{0}}; + struct smb2_handle h_client1_file3 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_handle h_client2_file2 = {{0}}; + struct smb2_handle h_client2_file3 = {{0}}; + struct smb2_create io1, io2, io3; + bool ret = true; + const char *fname1 = BASEDIR "\\lease_break_test1.dat"; + const char *fname2 = BASEDIR "\\lease_break_test2.dat"; + const char *fname3 = BASEDIR "\\lease_break_test3.dat"; + struct smb2_tree *tree2A = NULL; + struct smb2_tree *tree2B = NULL; + struct smb2_tree *tree2C = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_lease ls3; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Lease break retry: Test1\n"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(lease_break_info.count, 0); + + smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io1); + + smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io2); + + smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io3); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channels(tctx, host, share, + credentials, + &transport2_options, + &tree2A, &tree2B, &tree2C); + torture_assert(tctx, ret, "Could not create channels.\n"); + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via session 2A\n"); + status = smb2_create(tree2A, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0); + CHECK_VAL(lease_break_info.count, 0); + + /* 2b opens file2 */ + torture_comment(tctx, "client2 opens fname2 via session 2B\n"); + status = smb2_create(tree2B, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0); + CHECK_VAL(lease_break_info.count, 0); + + /* 2c opens file3 */ + torture_comment(tctx, "client2 opens fname3 via session 2C\n"); + smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3, + smb2_util_lease_state("RHW")); + status = smb2_create(tree2C, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0); + CHECK_VAL(lease_break_info.count, 0); + + /* 1 opens file1 - lease break? */ + torture_comment(tctx, "client1 opens fname1 via session 1\n"); + smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F1); + CHECK_VAL(lease_break_info.count, 1); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* 1 opens file2 - lease break? */ + torture_comment(tctx, "client1 opens fname2 via session 1\n"); + smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F2); + CHECK_VAL(lease_break_info.count, 1); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* 1 opens file3 - lease break? */ + torture_comment(tctx, "client1 opens fname3 via session 1\n"); + smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F3); + CHECK_VAL(lease_break_info.count, 1); + + /* cleanup everything */ + torture_reset_lease_break_info(tctx, &lease_break_info); + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + smb2_util_close(tree2A, h_client2_file1); + smb2_util_close(tree2A, h_client2_file2); + smb2_util_close(tree2A, h_client2_file3); + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(lease_break_info.count, 0); + test_multichannel_free_channels(tree2A, tree2B, tree2C); + tree2A = tree2B = tree2C = NULL; +done: + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + if (tree2A != NULL) { + smb2_util_close(tree2A, h_client2_file1); + smb2_util_close(tree2A, h_client2_file2); + smb2_util_close(tree2A, h_client2_file3); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + smb2_deltree(tree1, BASEDIR); + + test_multichannel_free_channels(tree2A, tree2B, tree2C); + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Lease Break Test 2: + * Test for lease break retries being sent by the server. + * Connect 2A, 2B + * open file1 in session 2A + * open file2 in session 2B + * block 2A + * open file2 in session 1 + * lease break retry reaches the client? + * Connect 2C + * open file3 in session 2C + * unblock 2A + * open file1 in session 1 + * lease break reaches the client? + * open file3 in session 1 + * lease break reached the client? + * Cleanup + * On deletion by 1, lease breaks sent for file1, file2 and file3 + * on 2B + * This changes RH lease to R for Session 2. + * (This has been disabled while we add support for sending lease + * break for handle leases.) + */ +static bool test_multichannel_lease_break_test2(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client1_file2 = {{0}}; + struct smb2_handle h_client1_file3 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_handle h_client2_file2 = {{0}}; + struct smb2_handle h_client2_file3 = {{0}}; + struct smb2_create io1, io2, io3; + bool ret = true; + const char *fname1 = BASEDIR "\\lease_break_test1.dat"; + const char *fname2 = BASEDIR "\\lease_break_test2.dat"; + const char *fname3 = BASEDIR "\\lease_break_test3.dat"; + struct smb2_tree *tree2A = NULL; + struct smb2_tree *tree2B = NULL; + struct smb2_tree *tree2C = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2A = NULL; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_lease ls3; + bool block_setup = false; + bool block_ok = false; + bool unblock_ok = false; + + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Lease break retry: Test2\n"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + CHECK_VAL(lease_break_info.count, 0); + + smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io1); + + smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io2); + + smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io3); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channels(tctx, host, share, + credentials, + &transport2_options, + &tree2A, &tree2B, NULL); + torture_assert(tctx, ret, "Could not create channels.\n"); + transport2A = tree2A->session->transport; + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via session 2A\n"); + smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree2A, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0); + CHECK_VAL(io1.out.durable_open_v2, false); //true); + CHECK_VAL(io1.out.timeout, io1.in.timeout); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(lease_break_info.count, 0); + + /* 2b opens file2 */ + torture_comment(tctx, "client2 opens fname2 via session 2B\n"); + smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2, + smb2_util_lease_state("RHW")); + status = smb2_create(tree2B, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0); + CHECK_VAL(io2.out.durable_open_v2, false); //true); + CHECK_VAL(io2.out.timeout, io2.in.timeout); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(lease_break_info.count, 0); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + torture_comment(tctx, "Blocking 2A\n"); + /* Block 2A */ + block_ok = test_block_smb2_transport(tctx, transport2A); + torture_assert(tctx, block_ok, "we could not block tcp transport"); + + torture_wait_for_lease_break(tctx); + CHECK_VAL(lease_break_info.count, 0); + + /* 1 opens file2 */ + torture_comment(tctx, + "Client opens fname2 with session1 with 2A blocked\n"); + smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0); + CHECK_VAL(io2.out.durable_open_v2, false); + CHECK_VAL(io2.out.timeout, 0); + CHECK_VAL(io2.out.durable_open, false); + + if (lease_break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected lease break!!\n"); + } else { + torture_comment(tctx, "Received %d lease break(s)!!\n", + lease_break_info.count); + } + + /* + * We got breaks on both channels + * (one failed on the blocked connection) + */ + CHECK_VAL(lease_break_info.count, 2); + lease_break_info.count -= 1; + CHECK_VAL(lease_break_info.failures, 1); + lease_break_info.failures -= 1; + CHECK_BREAK_INFO("RHW", "RH", LEASE2F2); + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* Connect 2C */ + torture_comment(tctx, "Connecting session 2C\n"); + talloc_free(tree2C); + tree2C = test_multichannel_create_channel(tctx, host, share, + credentials, &transport2_options, tree2A); + if (!tree2C) { + goto done; + } + + /* 2c opens file3 */ + torture_comment(tctx, "client2 opens fname3 via session 2C\n"); + smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3, + smb2_util_lease_state("RHW")); + status = smb2_create(tree2C, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0); + CHECK_VAL(io3.out.durable_open_v2, false); + CHECK_VAL(io3.out.timeout, io2.in.timeout); + CHECK_VAL(io3.out.durable_open, false); + CHECK_VAL(lease_break_info.count, 0); + + /* Unblock 2A */ + torture_comment(tctx, "Unblocking 2A\n"); + unblock_ok = test_unblock_smb2_transport(tctx, transport2A); + torture_assert(tctx, unblock_ok, "we could not unblock tcp transport"); + + /* 1 opens file1 */ + torture_comment(tctx, "Client opens fname1 with session 1\n"); + smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0); + + if (lease_break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected lease break!!\n"); + } else { + torture_comment(tctx, + "Received %d lease break(s)!!\n", + lease_break_info.count); + } + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F1); + torture_reset_lease_break_info(tctx, &lease_break_info); + + /*1 opens file3 */ + torture_comment(tctx, "client opens fname3 via session 1\n"); + + smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0); + + if (lease_break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected lease break!!\n"); + } else { + torture_comment(tctx, + "Received %d lease break(s)!!\n", + lease_break_info.count); + } + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F3); + torture_reset_lease_break_info(tctx, &lease_break_info); + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + + /* + * Session 2 still has RW lease on file 1. Deletion of this file by 1 + * leads to a lease break call to session 2 file1 + */ + smb2_util_unlink(tree1, fname1); + /* + * Bug - Samba does not revoke Handle lease on unlink + * CHECK_BREAK_INFO("RH", "R", LEASE2F1); + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * Session 2 still has RW lease on file 2. Deletion of this file by 1 + * leads to a lease break call to session 2 file2 + */ + smb2_util_unlink(tree1, fname2); + /* + * Bug - Samba does not revoke Handle lease on unlink + * CHECK_BREAK_INFO("RH", "R", LEASE2F2); + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + + /* + * Session 2 still has RW lease on file 3. Deletion of this file by 1 + * leads to a lease break call to session 2 file3 + */ + smb2_util_unlink(tree1, fname3); + /* + * Bug - Samba does not revoke Handle lease on unlink + * CHECK_BREAK_INFO("RH", "R", LEASE2F3); + */ + torture_reset_lease_break_info(tctx, &lease_break_info); + + smb2_util_close(tree2C, h_client2_file1); + smb2_util_close(tree2C, h_client2_file2); + smb2_util_close(tree2C, h_client2_file3); + + test_multichannel_free_channels(tree2A, tree2B, tree2C); + tree2A = tree2B = tree2C = NULL; + +done: + if (block_ok && !unblock_ok) { + test_unblock_smb2_transport(tctx, transport2A); + } + if (block_setup) { + test_cleanup_blocked_transports(tctx); + } + + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + smb2_util_close(tree1, h_client1_file2); + smb2_util_close(tree1, h_client1_file3); + if (tree2A != NULL) { + smb2_util_close(tree2A, h_client2_file1); + smb2_util_close(tree2A, h_client2_file2); + smb2_util_close(tree2A, h_client2_file3); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + smb2_util_unlink(tree1, fname3); + smb2_deltree(tree1, BASEDIR); + + test_multichannel_free_channels(tree2A, tree2B, tree2C); + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Test 3: Check to see how the server behaves if lease break + * response is sent over a different channel to one over which + * the break is received. + * Connect 2A, 2B + * open file1 in session 2A + * open file1 in session 1 + * Lease break sent to 2A + * 2B sends back lease break reply. + * session 1 allowed to open file + */ +static bool test_multichannel_lease_break_test3(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_create io1; + bool ret = true; + const char *fname1 = BASEDIR "\\lease_break_test1.dat"; + struct smb2_tree *tree2A = NULL; + struct smb2_tree *tree2B = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2A = NULL; + struct smbcli_options transport2_options; + uint16_t local_port = 0; + struct smb2_lease ls1; + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Lease break retry: Test3\n"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + CHECK_VAL(lease_break_info.count, 0); + + smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1, + smb2_util_lease_state("RHW")); + test_multichannel_init_smb_create(&io1); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channels(tctx, host, share, + credentials, + &transport2_options, + &tree2A, &tree2B, NULL); + torture_assert(tctx, ret, "Could not create channels.\n"); + transport2A = tree2A->session->transport; + transport2A->lease.private_data = tree2B; + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via session 2A\n"); + smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree2A, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0); + CHECK_VAL(io1.out.durable_open_v2, false); //true); + CHECK_VAL(io1.out.timeout, io1.in.timeout); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(lease_break_info.count, 0); + + /* Set a timeout for 5 seconds for session 1 to open file1 */ + ne = tevent_timeval_current_ofs(0, 5000000); + te = tevent_add_timer(tctx->ev, mem_ctx, ne, timeout_cb, ×up); + if (te == NULL) { + torture_comment(tctx, "Failed to add timer."); + goto done; + } + + /* 1 opens file2 */ + torture_comment(tctx, "Client opens fname1 with session 1\n"); + smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0); + CHECK_VAL(io1.out.durable_open_v2, false); + CHECK_VAL(io1.out.timeout, 0); + CHECK_VAL(io1.out.durable_open, false); + + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RHW", "RH", LEASE2F1); + + /* + * Check if timeout handler was fired. This would indicate + * that the server didn't receive a reply for the oplock break + * from the client and the server let session 1 open the file + * only after the oplock break timeout. + */ + CHECK_VAL(timesup, false); + +done: + smb2_util_close(tree1, h_client1_file1); + if (tree2A != NULL) { + smb2_util_close(tree2A, h_client2_file1); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_deltree(tree1, BASEDIR); + + test_multichannel_free_channels(tree2A, tree2B, NULL); + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Test limits of channels + */ +static bool test_multichannel_num_channels(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + TALLOC_CTX *mem_ctx = talloc_new(tctx); + bool ret = true; + struct smb2_tree **tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport **transport2 = NULL; + struct smbcli_options transport2_options; + struct smb2_session **session2 = NULL; + uint32_t server_capabilities; + int i; + int max_channels = 33; /* 32 is the W2K12R2 and W2K16 limit */ + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_fail(tctx, + "SMB 3.X Dialect family required for Multichannel" + " tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_fail(tctx, + "Server does not support multichannel."); + } + + torture_comment(tctx, "Testing max. number of channels\n"); + + transport2_options = transport1->options; + transport2_options.client_guid = GUID_random(); + + tree2 = talloc_zero_array(mem_ctx, struct smb2_tree *, + max_channels); + transport2 = talloc_zero_array(mem_ctx, struct smb2_transport *, + max_channels); + session2 = talloc_zero_array(mem_ctx, struct smb2_session *, + max_channels); + if (tree2 == NULL || transport2 == NULL || session2 == NULL) { + torture_fail(tctx, "out of memory"); + } + + for (i = 0; i < max_channels; i++) { + + NTSTATUS expected_status; + + torture_assert_ntstatus_ok_goto(tctx, + smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2[i], + tctx->ev, + &transport2_options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ), + ret, done, "smb2_connect failed"); + + transport2[i] = tree2[i]->session->transport; + + if (i == 0) { + /* + * done for the 1st channel + * + * For all remaining channels we do the + * session setup on our own. + */ + transport2_options.only_negprot = true; + continue; + } + + /* + * Now bind the session2[i] to the transport2 + */ + session2[i] = smb2_session_channel(transport2[i], + lpcfg_gensec_settings(tctx, + tctx->lp_ctx), + tree2[0], + tree2[0]->session); + + torture_assert(tctx, session2[i] != NULL, + "smb2_session_channel failed"); + + torture_comment(tctx, "established transport2 [#%d]\n", i); + + if (i >= 32) { + expected_status = NT_STATUS_INSUFFICIENT_RESOURCES; + } else { + expected_status = NT_STATUS_OK; + } + + torture_assert_ntstatus_equal_goto(tctx, + smb2_session_setup_spnego(session2[i], + samba_cmdline_get_creds(), + 0 /* previous_session_id */), + expected_status, + ret, done, + talloc_asprintf(tctx, "failed to establish session " + "setup for channel #%d", i)); + + torture_comment(tctx, "bound session2 [#%d] to session2 [0]\n", + i); + } + + done: + talloc_free(mem_ctx); + + return ret; +} + +struct test_multichannel_lease_break_state; + +struct test_multichannel_lease_break_channel { + struct test_multichannel_lease_break_state *state; + size_t idx; + char name[64]; + struct smb2_tree *tree; + bool blocked; + struct timeval break_time; + double full_duration; + double relative_duration; + struct smb2_lease_break lb; + size_t break_num; +}; + +struct test_multichannel_lease_break_state { + struct torture_context *tctx; + struct timeval open_req_time; + struct timeval open_rep_time; + size_t num_breaks; + struct timeval last_break_time; + struct test_multichannel_lease_break_channel channels[32]; +}; + +static bool test_multichannel_lease_break_handler(struct smb2_transport *transport, + const struct smb2_lease_break *lb, + void *private_data) +{ + struct test_multichannel_lease_break_channel *c = + (struct test_multichannel_lease_break_channel *)private_data; + struct test_multichannel_lease_break_state *state = c->state; + + c->break_time = timeval_current(); + c->full_duration = timeval_elapsed2(&state->open_req_time, + &c->break_time); + c->relative_duration = timeval_elapsed2(&state->last_break_time, + &c->break_time); + state->last_break_time = c->break_time; + c->lb = *lb; + c->break_num = ++state->num_breaks; + + torture_comment(state->tctx, "Got LEASE break epoch[0x%x] %zu on %s after %f ( %f)\n", + c->lb.new_epoch, c->break_num, c->name, + c->relative_duration, + c->full_duration); + + return torture_lease_handler(transport, lb, c->tree); +} + +static bool test_multichannel_lease_break_test4(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct test_multichannel_lease_break_state state = { + .tctx = tctx, + }; + struct test_multichannel_lease_break_channel *open2_channel = NULL; + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_handle h_client1_file1 = {{0}}; + struct smb2_handle h_client2_file1 = {{0}}; + struct smb2_create io1; + struct smb2_create io2; + bool ret = true; + const char *fname1 = BASEDIR "\\lease_break_test4.dat"; + struct smb2_tree *trees2[32] = { NULL, }; + size_t i; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options transport2_options; + struct smb2_session *session1 = tree1->session; + uint16_t local_port = 0; + struct smb2_lease ls1; + struct smb2_lease ls2; + bool block_setup = false; + bool block_ok = false; + double open_duration; + + if (!test_multichannel_initial_checks(tctx, tree1)) { + return true; + } + + torture_comment(tctx, "Lease break retry: Test4\n"); + + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + torture_comment(tctx, "transport1 [%p]\n", transport1); + local_port = torture_get_local_port_from_transport(transport1); + torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port); + + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname1); + CHECK_VAL(lease_break_info.count, 0); + + smb2_lease_v2_create(&io2, &ls2, false, fname1, + LEASE2F1, NULL, + smb2_util_lease_state("RHW"), + 0x20); + + transport2_options = transport1->options; + + ret = test_multichannel_create_channel_array(tctx, host, share, credentials, + &transport2_options, + ARRAY_SIZE(trees2), trees2); + torture_assert(tctx, ret, "Could not create channels.\n"); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + struct test_multichannel_lease_break_channel *c = &state.channels[i]; + struct smb2_transport *t = trees2[i]->session->transport; + + c->state = &state; + c->idx = i+1; + c->tree = trees2[i]; + snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx); + + t->lease.handler = test_multichannel_lease_break_handler; + t->lease.private_data = c; + } + + open2_channel = &state.channels[0]; + + /* 2a opens file1 */ + torture_comment(tctx, "client2 opens fname1 via %s\n", + open2_channel->name); + status = smb2_create(open2_channel->tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h_client2_file1 = io2.out.file.handle; + CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io2, "RHW", true, LEASE2F1, 0, 0, 0x21); + CHECK_VAL(io2.out.durable_open_v2, false); + CHECK_VAL(io2.out.timeout, io2.in.timeout); + CHECK_VAL(io2.out.durable_open, false); + CHECK_VAL(lease_break_info.count, 0); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_lease_break_channel *c = &state.channels[i]; + struct smb2_transport *t = c->tree->session->transport; + + torture_comment(tctx, "Blocking %s\n", c->name); + block_ok = _test_block_smb2_transport(tctx, t, c->name); + torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport"); + c->blocked = true; + } + + /* 1 opens file2 */ + torture_comment(tctx, + "Client opens fname1 with session 1 with all %zu blocked\n", + ARRAY_SIZE(trees2)); + smb2_lease_v2_create(&io1, &ls1, false, fname1, + LEASE1F1, NULL, + smb2_util_lease_state("RHW"), + 0x10); + CHECK_VAL(lease_break_info.count, 0); + state.open_req_time = timeval_current(); + state.last_break_time = state.open_req_time; + status = smb2_create(tree1, mem_ctx, &io1); + state.open_rep_time = timeval_current(); + CHECK_STATUS(status, NT_STATUS_OK); + h_client1_file1 = io1.out.file.handle; + + CHECK_VAL_GREATER_THAN(lease_break_info.count, 1); + + open_duration = timeval_elapsed2(&state.open_req_time, + &state.open_rep_time); + torture_comment(tctx, "open_duration: %f\n", open_duration); + if (lease_break_info.count < ARRAY_SIZE(state.channels)) { + CHECK_VAL_GREATER_THAN(open_duration, 35); + CHECK_LEASE_V2(&io1, "RH", true, LEASE1F1, 0, 0, 0x11); + } else { + CHECK_LEASE_V2(&io1, "RWH", true, LEASE1F1, 0, 0, 0x11); + } + + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + if (lease_break_info.count >= ARRAY_SIZE(state.channels)) { + break; + } + torture_comment(tctx, "Received %d lease break(s) wait for more!!\n", + lease_break_info.count); + torture_wait_for_lease_break(tctx); + } + + if (lease_break_info.count == 0) { + torture_comment(tctx, + "Did not receive expected lease break!!\n"); + } else { + torture_comment(tctx, "Received %d lease break(s)!!\n", + lease_break_info.count); + } + + if (lease_break_info.count < ARRAY_SIZE(state.channels)) { + CHECK_VAL_GREATER_THAN(lease_break_info.count, 3); + } else { + CHECK_VAL(lease_break_info.count, ARRAY_SIZE(state.channels)); + } + + for (i = 0; i < lease_break_info.count; i++) { + struct test_multichannel_lease_break_channel *c = &state.channels[i]; + + torture_comment(tctx, "Verify %s\n", c->name); + torture_assert_int_equal(tctx, c->break_num, c->idx, + "Got lease break in wrong order"); + CHECK_LEASE_BREAK_V2(c->lb, LEASE2F1, "RWH", "RH", + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED, + 0x22); + } + +done: + for (i = 0; i < ARRAY_SIZE(state.channels); i++) { + struct test_multichannel_lease_break_channel *c = &state.channels[i]; + struct smb2_transport *t = NULL; + + if (!c->blocked) { + continue; + } + + t = c->tree->session->transport; + + torture_comment(tctx, "Unblocking %s\n", c->name); + _test_unblock_smb2_transport(tctx, t, c->name); + c->blocked = false; + } + if (block_setup) { + test_cleanup_blocked_transports(tctx); + } + + tree1->session = session1; + + smb2_util_close(tree1, h_client1_file1); + if (trees2[0] != NULL) { + smb2_util_close(trees2[0], h_client2_file1); + } + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname1); + smb2_deltree(tree1, BASEDIR); + + for (i = 0; i < ARRAY_SIZE(trees2); i++) { + if (trees2[i] == NULL) { + continue; + } + TALLOC_FREE(trees2[i]); + } + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * Test channel merging race + * This is a regression test for + * https://bugzilla.samba.org/show_bug.cgi?id=15346 + */ +struct test_multichannel_bug_15346_conn; + +struct test_multichannel_bug_15346_state { + struct torture_context *tctx; + struct test_multichannel_bug_15346_conn *conns; + size_t num_conns; + size_t num_ready; + bool asserted; + bool looping; +}; + +struct test_multichannel_bug_15346_conn { + struct test_multichannel_bug_15346_state *state; + size_t idx; + struct smbXcli_conn *smbXcli; + struct tevent_req *nreq; + struct tevent_req *ereq; +}; + +static void test_multichannel_bug_15346_ndone(struct tevent_req *subreq); +static void test_multichannel_bug_15346_edone(struct tevent_req *subreq); + +static void test_multichannel_bug_15346_ndone(struct tevent_req *subreq) +{ + struct test_multichannel_bug_15346_conn *conn = + (struct test_multichannel_bug_15346_conn *) + tevent_req_callback_data_void(subreq); + struct test_multichannel_bug_15346_state *state = conn->state; + struct torture_context *tctx = state->tctx; + struct timeval current_time; + struct tm tm_buf; + struct tm *current_tm = NULL; + char time_str[sizeof "10000-01-01T00:00:00"]; + size_t time_str_len; + NTSTATUS status; + bool ok = false; + + SMB_ASSERT(conn->nreq == subreq); + conn->nreq = NULL; + + status = smbXcli_negprot_recv(subreq, NULL, NULL); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok_goto(tctx, status, ok, asserted, + "smbXcli_negprot_recv failed"); + + current_time = tevent_timeval_current(); + current_tm = gmtime_r(¤t_time.tv_sec, &tm_buf); + torture_assert_not_null_goto(tctx, current_tm, ok, asserted, + "gmtime_r failed"); + + time_str_len = strftime(time_str, sizeof time_str, "%FT%T", current_tm); + torture_assert_size_not_equal_goto(tctx, time_str_len, 0, ok, asserted, + "strftime failed"); + + torture_comment(tctx, + "%s.%ldZ: conn[%zu]: negprot done\n", + time_str, + (long)current_time.tv_usec, + conn->idx); + + conn->ereq = smb2cli_echo_send(conn->smbXcli, + tctx->ev, + conn->smbXcli, + state->num_conns * 2 * 1000); + torture_assert_goto(tctx, conn->ereq != NULL, ok, asserted, + "smb2cli_echo_send"); + tevent_req_set_callback(conn->ereq, + test_multichannel_bug_15346_edone, + conn); + + return; + +asserted: + SMB_ASSERT(!ok); + state->asserted = true; + state->looping = false; + return; +} + +static void test_multichannel_bug_15346_edone(struct tevent_req *subreq) +{ + struct test_multichannel_bug_15346_conn *conn = + (struct test_multichannel_bug_15346_conn *) + tevent_req_callback_data_void(subreq); + struct test_multichannel_bug_15346_state *state = conn->state; + struct torture_context *tctx = state->tctx; + struct timeval current_time; + struct tm tm_buf; + struct tm *current_tm = NULL; + char time_str[sizeof "10000-01-01T00:00:00"]; + size_t time_str_len; + const char *outcome = NULL; + NTSTATUS status; + bool ok = false; + + SMB_ASSERT(conn->ereq == subreq); + conn->ereq = NULL; + + current_time = tevent_timeval_current(); + current_tm = gmtime_r(¤t_time.tv_sec, &tm_buf); + torture_assert_not_null_goto(tctx, current_tm, ok, asserted, + "gmtime_r failed"); + + time_str_len = strftime(time_str, sizeof time_str, "%FT%T", current_tm); + torture_assert_size_not_equal_goto(tctx, time_str_len, 0, ok, asserted, + "strftime failed"); + + status = smb2cli_echo_recv(subreq); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + outcome = "timed out"; + } else if (!NT_STATUS_IS_OK(status)) { + outcome = "failed"; + } else { + outcome = "done"; + } + torture_comment(tctx, + "%s.%ldZ: conn[%zu]: echo %s\n", + time_str, + (long)current_time.tv_usec, + conn->idx, + outcome); + torture_assert_ntstatus_ok_goto(tctx, status, ok, asserted, + "smb2cli_echo_recv failed"); + + state->num_ready += 1; + if (state->num_ready < state->num_conns) { + return; + } + + state->looping = false; + return; + +asserted: + SMB_ASSERT(!ok); + state->asserted = true; + state->looping = false; + return; +} + +static bool test_multichannel_bug_15346(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct resolve_context *resolve_ctx = lpcfg_resolve_context(tctx->lp_ctx); + const char *socket_options = lpcfg_socket_options(tctx->lp_ctx); + struct gensec_settings *gsettings = NULL; + bool ret = true; + NTSTATUS status; + struct smb2_transport *transport1 = tree1->session->transport; + struct test_multichannel_bug_15346_state *state = NULL; + uint32_t server_capabilities; + struct smb2_handle root_handle = {{0}}; + size_t i; + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_fail(tctx, + "SMB 3.X Dialect family required for Multichannel" + " tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_fail(tctx, + "Server does not support multichannel."); + } + + torture_comment(tctx, "Testing for BUG 15346\n"); + + state = talloc_zero(tctx, struct test_multichannel_bug_15346_state); + torture_assert_goto(tctx, state != NULL, ret, done, + "talloc_zero"); + state->tctx = tctx; + + gsettings = lpcfg_gensec_settings(state, tctx->lp_ctx); + torture_assert_goto(tctx, gsettings != NULL, ret, done, + "lpcfg_gensec_settings"); + + /* + * 32 is the W2K12R2 and W2K16 limit + * add 31 additional connections + */ + state->num_conns = 31; + state->conns = talloc_zero_array(state, + struct test_multichannel_bug_15346_conn, + state->num_conns); + torture_assert_goto(tctx, state->conns != NULL, ret, done, + "talloc_zero_array"); + + /* + * First we open the additional tcp connections + */ + + for (i = 0; i < state->num_conns; i++) { + struct test_multichannel_bug_15346_conn *conn = &state->conns[i]; + struct socket_context *sock = NULL; + uint16_t port = 445; + struct smbcli_options options = transport1->options; + + conn->state = state; + conn->idx = i; + + status = socket_connect_multi(state->conns, + host, + 1, &port, + resolve_ctx, + tctx->ev, + &sock, + &port); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "socket_connect_multi failed"); + + conn->smbXcli = smbXcli_conn_create(state->conns, + sock->fd, + host, + SMB_SIGNING_OFF, + 0, + &options.client_guid, + options.smb2_capabilities, + &options.smb3_capabilities); + torture_assert_goto(tctx, conn->smbXcli != NULL, ret, done, + "smbXcli_conn_create failed"); + sock->fd = -1; + TALLOC_FREE(sock); + } + + /* + * Now prepare the async SMB2 Negotiate requests + */ + for (i = 0; i < state->num_conns; i++) { + struct test_multichannel_bug_15346_conn *conn = &state->conns[i]; + + conn->nreq = smbXcli_negprot_send(conn->smbXcli, + tctx->ev, + conn->smbXcli, + state->num_conns * 2 * 1000, + smbXcli_conn_protocol(transport1->conn), + smbXcli_conn_protocol(transport1->conn), + 33, /* max_credits */ + NULL); + torture_assert_goto(tctx, conn->nreq != NULL, ret, done, "smbXcli_negprot_send"); + tevent_req_set_callback(conn->nreq, + test_multichannel_bug_15346_ndone, + conn); + } + + /* + * now we loop until all negprot and the first round + * of echos are done. + */ + state->looping = true; + while (state->looping) { + torture_assert_goto(tctx, tevent_loop_once(tctx->ev) == 0, + ret, done, "tevent_loop_once"); + } + + if (state->asserted) { + ret = false; + goto done; + } + + /* + * Now we check that the connections are still usable + */ + for (i = 0; i < state->num_conns; i++) { + struct test_multichannel_bug_15346_conn *conn = &state->conns[i]; + + torture_comment(tctx, "conn[%zu]: checking echo again1\n", conn->idx); + + status = smb2cli_echo(conn->smbXcli, 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2cli_echo failed"); + } + + status = smb2_util_roothandle(tree1, &root_handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_roothandle failed"); + + /* + * Now we check that the connections are still usable + */ + for (i = 0; i < state->num_conns; i++) { + struct test_multichannel_bug_15346_conn *conn = &state->conns[i]; + struct smbcli_options options = transport1->options; + struct smb2_session *session = NULL; + struct smb2_tree *tree = NULL; + union smb_fileinfo io; + + torture_comment(tctx, "conn[%zu]: checking session bind\n", conn->idx); + + /* + * Prepare smb2_{tree,session,transport} structures + * for the existing connection. + */ + options.only_negprot = true; + status = smb2_connect_ext(state->conns, + host, + NULL, /* ports */ + share, + resolve_ctx, + samba_cmdline_get_creds(), + &conn->smbXcli, + 0, /* previous_session_id */ + &tree, + tctx->ev, + &options, + socket_options, + gsettings); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect_ext failed"); + conn->smbXcli = tree->session->transport->conn; + + session = smb2_session_channel(tree->session->transport, + lpcfg_gensec_settings(tree, tctx->lp_ctx), + tree, + tree1->session); + torture_assert_goto(tctx, session != NULL, ret, done, + "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* + * Fix up the bound smb2_tree + */ + tree->session = session; + tree->smbXcli = tree1->smbXcli; + + ZERO_STRUCT(io); + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = root_handle; + + status = smb2_getinfo_file(tree, tree, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + } + + done: + talloc_free(state); + + return ret; +} + +struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "multichannel"); + struct torture_suite *suite_generic = torture_suite_create(ctx, + "generic"); + struct torture_suite *suite_oplocks = torture_suite_create(ctx, + "oplocks"); + struct torture_suite *suite_leases = torture_suite_create(ctx, + "leases"); + struct torture_suite *suite_bugs = torture_suite_create(ctx, + "bugs"); + + torture_suite_add_suite(suite, suite_generic); + torture_suite_add_suite(suite, suite_oplocks); + torture_suite_add_suite(suite, suite_leases); + torture_suite_add_suite(suite, suite_bugs); + + torture_suite_add_1smb2_test(suite_generic, "interface_info", + test_multichannel_interface_info); + torture_suite_add_1smb2_test(suite_generic, "num_channels", + test_multichannel_num_channels); + torture_suite_add_1smb2_test(suite_oplocks, "test1", + test_multichannel_oplock_break_test1); + torture_suite_add_1smb2_test(suite_oplocks, "test2", + test_multichannel_oplock_break_test2); + torture_suite_add_1smb2_test(suite_oplocks, "test3_windows", + test_multichannel_oplock_break_test3_windows); + torture_suite_add_1smb2_test(suite_oplocks, "test3_specification", + test_multichannel_oplock_break_test3_specification); + torture_suite_add_1smb2_test(suite_leases, "test1", + test_multichannel_lease_break_test1); + torture_suite_add_1smb2_test(suite_leases, "test2", + test_multichannel_lease_break_test2); + torture_suite_add_1smb2_test(suite_leases, "test3", + test_multichannel_lease_break_test3); + torture_suite_add_1smb2_test(suite_leases, "test4", + test_multichannel_lease_break_test4); + torture_suite_add_1smb2_test(suite_bugs, "bug_15346", + test_multichannel_bug_15346); + + suite->description = talloc_strdup(suite, "SMB2 Multichannel tests"); + + return suite; +} diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c new file mode 100644 index 0000000..0aadc50 --- /dev/null +++ b/source4/torture/smb2/notify.c @@ -0,0 +1,2786 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 notify test suite + + Copyright (C) Stefan Metzmacher 2006 + Copyright (C) Andrew Tridgell 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/util.h" + +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/security.h" + +#include "lib/events/events.h" + +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_WIRE_STR(field, value) do { \ + if (!field.s || strcmp(field.s, value)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) %s [%s] != %s\n", __location__, #field, \ + field.s, value); \ + ret = false; \ + goto done; \ + }} while (0) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(torture->ev) != 0) { \ + break; \ + } \ + } + +#define BASEDIR "test_notify" +#define FNAME "smb2-notify01.dat" + +static bool test_valid_request(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle dh; + struct smb2_notify n; + struct smb2_request *req; + uint32_t max_buffer_size; + + torture_comment(torture, "TESTING VALIDITY OF CHANGE NOTIFY REQUEST\n"); + + smb2_transport_credits_ask_num(tree->session->transport, 256); + + smb2_util_unlink(tree, FNAME); + + status = smb2_util_roothandle(tree, &dh); + CHECK_STATUS(status, NT_STATUS_OK); + + max_buffer_size = + smb2cli_conn_max_trans_size(tree->session->transport->conn); + + n.in.recursive = 0x0000; + n.in.buffer_size = max_buffer_size; + n.in.file.handle = dh; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.unknown = 0x00000000; + req = smb2_notify_send(tree, &n); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(torture->ev) != 0) { + break; + } + } + + status = torture_setup_simple_file(torture, tree, FNAME); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(n.out.num_changes, 1); + CHECK_VAL(n.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(n.out.changes[0].name, FNAME); + + /* + * if the change response doesn't fit in the buffer + * NOTIFY_ENUM_DIR is returned. + */ + n.in.buffer_size = 0x00000000; + req = smb2_notify_send(tree, &n); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(torture->ev) != 0) { + break; + } + } + + status = torture_setup_simple_file(torture, tree, FNAME); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR); + + /* + * if the change response fits in the buffer we get + * NT_STATUS_OK again + */ + n.in.buffer_size = max_buffer_size; + req = smb2_notify_send(tree, &n); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(torture->ev) != 0) { + break; + } + } + + status = torture_setup_simple_file(torture, tree, FNAME); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(n.out.num_changes, 3); + CHECK_VAL(n.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(n.out.changes[0].name, FNAME); + CHECK_VAL(n.out.changes[1].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(n.out.changes[1].name, FNAME); + CHECK_VAL(n.out.changes[2].action, NOTIFY_ACTION_MODIFIED); + CHECK_WIRE_STR(n.out.changes[2].name, FNAME); + + /* if the first notify returns NOTIFY_ENUM_DIR, all do */ + status = smb2_util_close(tree, dh); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_roothandle(tree, &dh); + CHECK_STATUS(status, NT_STATUS_OK); + + n.in.recursive = 0x0000; + n.in.buffer_size = 0x00000001; + n.in.file.handle = dh; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.unknown = 0x00000000; + req = smb2_notify_send(tree, &n); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(torture->ev) != 0) { + break; + } + } + + status = torture_setup_simple_file(torture, tree, FNAME); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR); + + n.in.buffer_size = max_buffer_size; + req = smb2_notify_send(tree, &n); + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(torture->ev) != 0) { + break; + } + } + + status = torture_setup_simple_file(torture, tree, FNAME); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR); + + /* if the buffer size is too large, we get invalid parameter */ + n.in.recursive = 0x0000; + n.in.buffer_size = max_buffer_size + 1; + n.in.file.handle = dh; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.unknown = 0x00000000; + req = smb2_notify_send(tree, &n); + status = smb2_notify_recv(req, torture, &n); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + +done: + return ret; +} + +/* + basic testing of change notify on directories +*/ + +#define BASEDIR_DIR BASEDIR "_DIR" + +static bool torture_smb2_notify_dir(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + union smb_close cl; + int i, count; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_request *req, *req2; + const char *fname = BASEDIR_DIR "\\subdir-name"; + extern int torture_numops; + + torture_comment(torture, "TESTING CHANGE NOTIFY ON DIRECTORIES\n"); + + smb2_deltree(tree1, BASEDIR_DIR); + smb2_util_rmdir(tree1, BASEDIR_DIR); + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_DIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ; + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + torture_comment(torture, "Testing notify cancel\n"); + + req = smb2_notify_send(tree1, &(notify.smb2)); + smb2_cancel(req); + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + torture_comment(torture, "Testing notify mkdir\n"); + + req = smb2_notify_send(tree1, &(notify.smb2)); + smb2_util_mkdir(tree2, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, "Testing notify rmdir\n"); + + req = smb2_notify_send(tree1, &(notify.smb2)); + smb2_util_rmdir(tree2, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, + "Testing notify mkdir - rmdir - mkdir - rmdir\n"); + + smb2_util_mkdir(tree2, fname); + smb2_util_rmdir(tree2, fname); + smb2_util_mkdir(tree2, fname); + smb2_util_rmdir(tree2, fname); + smb_msleep(200); + req = smb2_notify_send(tree1, &(notify.smb2)); + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 4); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + CHECK_VAL(notify.smb2.out.changes[1].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[1].name, "subdir-name"); + CHECK_VAL(notify.smb2.out.changes[2].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[2].name, "subdir-name"); + CHECK_VAL(notify.smb2.out.changes[3].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[3].name, "subdir-name"); + + count = torture_numops; + torture_comment(torture, + "Testing buffered notify on create of %d files\n", count); + for (i=0;iin.fname); + status = smb2_create(tree, torture, smb2); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = smb2->out.file.handle; +done: + if (!ret) { + h1 = (struct smb2_handle) { + .data = { 0 , 0}, + }; + } + return h1; +} + +/* + testing of recursive change notify +*/ + +#define BASEDIR_REC BASEDIR "_REC" + +static bool torture_smb2_notify_recursive(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io, io1; + union smb_setfileinfo sinfo; + struct smb2_handle h1; + struct smb2_request *req1, *req2; + + smb2_deltree(tree1, BASEDIR_REC); + smb2_util_rmdir(tree1, BASEDIR_REC); + + torture_comment(torture, "TESTING CHANGE NOTIFY WITH RECURSION\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_REC; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, on file or directory name + changes. Setup both with and without recursion */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_CREATION; + notify.smb2.in.file.handle = h1; + + notify.smb2.in.recursive = true; + req1 = smb2_notify_send(tree1, &(notify.smb2)); + smb2_cancel(req1); + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + notify.smb2.in.recursive = false; + req2 = smb2_notify_send(tree1, &(notify.smb2)); + smb2_cancel(req2); + status = smb2_notify_recv(req2, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + ZERO_STRUCT(io1.smb2); + io1.generic.level = RAW_OPEN_SMB2; + io1.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io1.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_ALL; + io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io1.smb2.in.alloc_size = 0; + io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io1.smb2.in.security_flags = 0; + io1.smb2.in.fname = BASEDIR_REC "\\subdir-name"; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree2, io1.smb2.out.file.handle); + + io1.smb2.in.fname = BASEDIR_REC "\\subdir-name\\subname1"; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR_REC "\\subdir-name\\subname1-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io1.smb2.in.fname = BASEDIR_REC "\\subdir-name\\subname2"; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = BASEDIR_REC "\\subname2-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + io1.smb2.in.fname = BASEDIR_REC "\\subname2-r"; + io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = BASEDIR_REC "\\subname3-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + notify.smb2.in.completion_filter = 0; + notify.smb2.in.recursive = true; + smb_msleep(200); + req1 = smb2_notify_send(tree1, &(notify.smb2)); + + status = smb2_util_rmdir(tree2, + BASEDIR_REC "\\subdir-name\\subname1-r"); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_rmdir(tree2, + BASEDIR_REC "\\subdir-name"); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_unlink(tree2, BASEDIR_REC "\\subname3-r"); + CHECK_STATUS(status, NT_STATUS_OK); + + notify.smb2.in.recursive = false; + req2 = smb2_notify_send(tree1, &(notify.smb2)); + + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 9); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + CHECK_VAL(notify.smb2.out.changes[1].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[1].name, "subdir-name\\subname1"); + CHECK_VAL(notify.smb2.out.changes[2].action, NOTIFY_ACTION_OLD_NAME); + CHECK_WIRE_STR(notify.smb2.out.changes[2].name, "subdir-name\\subname1"); + CHECK_VAL(notify.smb2.out.changes[3].action, NOTIFY_ACTION_NEW_NAME); + CHECK_WIRE_STR(notify.smb2.out.changes[3].name, "subdir-name\\subname1-r"); + CHECK_VAL(notify.smb2.out.changes[4].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[4].name, "subdir-name\\subname2"); + CHECK_VAL(notify.smb2.out.changes[5].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[5].name, "subdir-name\\subname2"); + CHECK_VAL(notify.smb2.out.changes[6].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[6].name, "subname2-r"); + CHECK_VAL(notify.smb2.out.changes[7].action, NOTIFY_ACTION_OLD_NAME); + CHECK_WIRE_STR(notify.smb2.out.changes[7].name, "subname2-r"); + CHECK_VAL(notify.smb2.out.changes[8].action, NOTIFY_ACTION_NEW_NAME); + CHECK_WIRE_STR(notify.smb2.out.changes[8].name, "subname3-r"); + +done: + smb2_deltree(tree1, BASEDIR_REC); + return ret; +} + +/* + testing of change notify mask change +*/ + +#define BASEDIR_MC BASEDIR "_MC" + +static bool torture_smb2_notify_mask_change(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io, io1; + struct smb2_handle h1; + struct smb2_request *req1, *req2; + union smb_setfileinfo sinfo; + + smb2_deltree(tree1, BASEDIR_MC); + smb2_util_rmdir(tree1, BASEDIR_MC); + + torture_comment(torture, "TESTING CHANGE NOTIFY WITH MASK CHANGE\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_MC; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, on file or directory name + changes. Setup both with and without recursion */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES; + notify.smb2.in.file.handle = h1; + + notify.smb2.in.recursive = true; + req1 = smb2_notify_send(tree1, &(notify.smb2)); + + smb2_cancel(req1); + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + + notify.smb2.in.recursive = false; + req2 = smb2_notify_send(tree1, &(notify.smb2)); + + smb2_cancel(req2); + status = smb2_notify_recv(req2, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + notify.smb2.in.recursive = true; + req1 = smb2_notify_send(tree1, &(notify.smb2)); + + /* Set to hidden then back again. */ + ZERO_STRUCT(io1.smb2); + io1.generic.level = RAW_OPEN_SMB2; + io1.smb2.in.create_flags = 0; + io1.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_ALL; + io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io1.smb2.in.security_flags = 0; + io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io1.smb2.in.fname = BASEDIR_MC "\\tname1"; + + smb2_util_close(tree1, + custom_smb2_create(tree1, torture, &(io1.smb2))); + status = smb2_util_setatr(tree1, BASEDIR_MC "\\tname1", + FILE_ATTRIBUTE_HIDDEN); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_unlink(tree1, BASEDIR_MC "\\tname1"); + + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "tname1"); + + /* Now try and change the mask to include other events. + * This should not work - once the mask is set on a directory + * h1 it seems to be fixed until the fnum is closed. */ + + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_CREATION; + notify.smb2.in.recursive = true; + req1 = smb2_notify_send(tree1, &(notify.smb2)); + + notify.smb2.in.recursive = false; + req2 = smb2_notify_send(tree1, &(notify.smb2)); + + io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io1.smb2.in.fname = BASEDIR_MC "\\subdir-name"; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree2, io1.smb2.out.file.handle); + + ZERO_STRUCT(sinfo); + io1.smb2.in.fname = BASEDIR_MC "\\subdir-name\\subname1"; + io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR_MC "\\subdir-name\\subname1-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + io1.smb2.in.fname = BASEDIR_MC "\\subdir-name\\subname2"; + io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.new_name = BASEDIR_MC "\\subname2-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree2, io1.smb2.out.file.handle); + + io1.smb2.in.fname = BASEDIR_MC "\\subname2-r"; + io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, torture, &(io1.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle; + sinfo.rename_information.in.new_name = BASEDIR_MC "\\subname3-r"; + status = smb2_setinfo_file(tree2, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree2, io1.smb2.out.file.handle); + + status = smb2_util_rmdir(tree2, BASEDIR_MC "\\subdir-name\\subname1-r"); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_rmdir(tree2, BASEDIR_MC "\\subdir-name"); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_unlink(tree2, BASEDIR_MC "\\subname3-r"); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subname2-r"); + + status = smb2_notify_recv(req2, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subname3-r"); + + if (!ret) { + goto done; + } + +done: + smb2_deltree(tree1, BASEDIR_MC); + return ret; +} + +/* + testing of mask bits for change notify +*/ + +#define BASEDIR_MSK BASEDIR "_MSK" + +static bool torture_smb2_notify_mask(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io, io1; + struct smb2_handle h1, h2; + int i; + char c = 1; + union smb_setfileinfo sinfo; + + smb2_deltree(tree1, BASEDIR_MSK); + smb2_util_rmdir(tree1, BASEDIR_MSK); + + torture_comment(torture, "TESTING CHANGE NOTIFY COMPLETION FILTERS\n"); + + + ZERO_STRUCT(h1); + ZERO_STRUCT(h2); + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_MSK; + + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.recursive = true; + +#define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, \ + expected, nchanges) \ + do { \ + do { for (i=0;i<32;i++) { \ + struct smb2_request *req; \ + status = smb2_create(tree1, torture, &(io.smb2)); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + h1 = io.smb2.out.file.handle; \ + setup \ + notify.smb2.in.file.handle = h1; \ + notify.smb2.in.completion_filter = ((uint32_t)1<session); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + +done: + smb2_deltree(tree1, BASEDIR_NUL); + return ret; +} + +/* + basic testing of change notifies followed by a session reconnect +*/ + +#define BASEDIR_NSR BASEDIR "_NSR" + +static bool torture_smb2_notify_session_reconnect(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + uint64_t previous_session_id = 0; + struct smb2_session *session2 = NULL; + + smb2_deltree(tree1, BASEDIR_NSR); + smb2_util_rmdir(tree1, BASEDIR_NSR); + + torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY SESSION RECONNECT\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_NSR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree1, &(notify.smb2)); + + WAIT_FOR_ASYNC_RESPONSE(req); + + previous_session_id = smb2cli_session_current_id(tree1->session->smbXcli); + torture_assert(torture, torture_smb2_session_setup(torture, + tree1->session->transport, + previous_session_id, + torture, &session2), + "session setup with previous_session_id failed"); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + + status = smb2_logoff(tree1->session); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + + status = smb2_logoff(session2); + CHECK_STATUS(status, NT_STATUS_OK); +done: + smb2_deltree(tree1, BASEDIR_NSR); + return ret; +} + +/* + basic testing of change notifies followed by an invalid reauth +*/ + +#define BASEDIR_IR BASEDIR "_IR" + +static bool torture_smb2_notify_invalid_reauth(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + struct cli_credentials *invalid_creds; + + smb2_deltree(tree2, BASEDIR_IR); + smb2_util_rmdir(tree2, BASEDIR_IR); + + torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY invalid REAUTH\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_IR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree1, &(notify.smb2)); + + WAIT_FOR_ASYNC_RESPONSE(req); + + invalid_creds = cli_credentials_init(torture); + torture_assert(torture, (invalid_creds != NULL), "talloc error"); + cli_credentials_set_username(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_domain(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_password(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_realm(invalid_creds, NULL, CRED_SPECIFIED); + cli_credentials_set_workstation(invalid_creds, "", CRED_UNINITIALISED); + + status = smb2_session_setup_spnego(tree1->session, + invalid_creds, + 0 /* previous_session_id */); + CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + + /* + * Demonstrate that the session is no longer valid. + */ + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); +done: + smb2_deltree(tree2, BASEDIR_IR); + return ret; +} + +static void tcp_dis_handler(struct smb2_transport *t, void *p) +{ + struct smb2_tree *tree = (struct smb2_tree *)p; + smb2_transport_dead(tree->session->transport, + NT_STATUS_LOCAL_DISCONNECT); + t = NULL; + tree = NULL; +} + +/* + basic testing of change notifies followed by tcp disconnect +*/ + +#define BASEDIR_NTCPD BASEDIR "_NTCPD" + +static bool torture_smb2_notify_tcp_disconnect( + struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + + smb2_deltree(tree, BASEDIR_NTCPD); + smb2_util_rmdir(tree, BASEDIR_NTCPD); + + torture_comment(torture, + "TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_NTCPD; + + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_cancel(req); + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + notify.smb2.in.recursive = true; + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_transport_idle_handler(tree->session->transport, + tcp_dis_handler, 250000, tree); + tree = NULL; + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT); + +done: + return ret; +} + +/* + test setting up two change notify requests on one handle +*/ + +#define BASEDIR_NDOH BASEDIR "_NDOH" + +static bool torture_smb2_notify_double(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req1, *req2; + + smb2_deltree(tree1, BASEDIR_NDOH); + smb2_util_rmdir(tree1, BASEDIR_NDOH); + + torture_comment(torture, + "TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ| + SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_NDOH; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req1 = smb2_notify_send(tree1, &(notify.smb2)); + smb2_cancel(req1); + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + req2 = smb2_notify_send(tree1, &(notify.smb2)); + smb2_cancel(req2); + status = smb2_notify_recv(req2, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + smb2_util_mkdir(tree2, BASEDIR_NDOH "\\subdir-name"); + req1 = smb2_notify_send(tree1, &(notify.smb2)); + req2 = smb2_notify_send(tree1, &(notify.smb2)); + + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + smb2_util_mkdir(tree2, BASEDIR_NDOH "\\subdir-name2"); + + status = smb2_notify_recv(req2, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name2"); + +done: + smb2_deltree(tree1, BASEDIR_NDOH); + return ret; +} + + +/* + test multiple change notifies at different depths and with/without recursion +*/ + +#define BASEDIR_TREE BASEDIR "_TREE" + +static bool torture_smb2_notify_tree(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + union smb_notify notify; + union smb_open io; + struct smb2_request *req; + struct timeval tv; + struct { + const char *path; + bool recursive; + uint32_t filter; + int expected; + struct smb2_handle h1; + int counted; + } dirs[] = { + { + .path = BASEDIR_TREE "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 30, + }, + { + .path = BASEDIR_TREE "\\zqy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 8, + }, + { + .path = BASEDIR_TREE "\\atsy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 4, + }, + { + .path = BASEDIR_TREE "\\abc\\foo", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\abc\\blah", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 13, + }, + { + .path = BASEDIR_TREE "\\abc\\blah", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 7, + }, + { + .path = BASEDIR_TREE "\\abc\\blah\\a", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\abc\\blah\\b", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\abc\\blah\\c", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\abc\\fooblah", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\zqy\\xx", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\zqy\\yyy", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 2, + }, + { + .path = BASEDIR_TREE "\\zqy\\..", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 40, + }, + { + .path = BASEDIR_TREE, + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 40, + }, + { + .path = BASEDIR_TREE, + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 6, + }, + { + .path = BASEDIR_TREE "\\atsy", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 4, + }, + { + .path = BASEDIR_TREE "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 24, + }, + { + .path = BASEDIR_TREE "\\abc", + .recursive = false, + .filter = FILE_NOTIFY_CHANGE_FILE_NAME, + .expected = 0, + }, + { + .path = BASEDIR_TREE "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_FILE_NAME, + .expected = 0, + }, + { + .path = BASEDIR_TREE "\\abc", + .recursive = true, + .filter = FILE_NOTIFY_CHANGE_NAME, + .expected = 24, + }, + }; + int i; + NTSTATUS status; + bool all_done = false; + + smb2_deltree(tree, BASEDIR_TREE); + smb2_util_rmdir(tree, BASEDIR_TREE); + + torture_comment(torture, "TESTING NOTIFY FOR DIFFERENT DEPTHS\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_TREE; + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 20000; + + /* + setup the directory tree, and the notify buffer on each directory + */ + for (i=0;i=0;i--) { + smb2_util_close(tree, dirs[i].h1); + smb2_util_rmdir(tree, dirs[i].path); + } + +done: + smb2_deltree(tree, BASEDIR_TREE); + smb2_util_rmdir(tree, BASEDIR_TREE); + return ret; +} + +/* + Test response when cached server events exceed single NT NOTFIY response + packet size. +*/ + +#define BASEDIR_OVF BASEDIR "_OVF" + +static bool torture_smb2_notify_overflow(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1, h2; + int count = 100; + struct smb2_request *req1; + int i; + + smb2_deltree(tree, BASEDIR_OVF); + smb2_util_rmdir(tree, BASEDIR_OVF); + + torture_comment(torture, "TESTING CHANGE NOTIFY EVENT OVERFLOW\n"); + + /* get a handle on the directory */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_OVF; + + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, on name changes. */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_NTTRANS; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + + notify.smb2.in.recursive = true; + req1 = smb2_notify_send(tree, &(notify.smb2)); + + /* cancel initial requests so the buffer is setup */ + smb2_cancel(req1); + status = smb2_notify_recv(req1, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + /* open a lot of files, filling up the server side notify buffer */ + torture_comment(torture, + "Testing overflowed buffer notify on create of %d files\n", + count); + + for (i=0;isession, tree, &tree1)) { + torture_warning(torture, "couldn't reconnect to share, bailing\n"); + ret = false; + goto done; + } + + torture_comment(torture, "tid1=%d tid2=%d\n", + smb2cli_tcon_current_id(tree->smbXcli), + smb2cli_tcon_current_id(tree1->smbXcli)); + + torture_comment(torture, "Testing notify mkdir\n"); + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_util_mkdir(tree1, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, "Testing notify rmdir\n"); + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_util_rmdir(tree, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, "CHANGE NOTIFY WITH TCON OK\n"); + + torture_comment(torture, "Disconnecting secondary tree\n"); + status = smb2_tdis(tree1); + CHECK_STATUS(status, NT_STATUS_OK); + talloc_free(tree1); + + torture_comment(torture, "Testing notify mkdir\n"); + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_util_mkdir(tree, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, "Testing notify rmdir\n"); + req = smb2_notify_send(tree, &(notify.smb2)); + smb2_util_rmdir(tree, fname); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.smb2.out.num_changes, 1); + CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name"); + + torture_comment(torture, "CHANGE NOTIFY WITH TDIS OK\n"); +done: + smb2_util_close(tree, h1); + smb2_deltree(tree, BASEDIR_TCON); + + return ret; +} + +#define BASEDIR_RMD BASEDIR "_RMD" + +static bool torture_smb2_notify_rmdir(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2, + bool initial_delete_on_close) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify = {}; + union smb_setfileinfo sfinfo = {}; + union smb_open io = {}; + struct smb2_handle h = {}; + struct smb2_request *req; + + torture_comment(torture, "TESTING NOTIFY CANCEL FOR DELETED DIR\n"); + + smb2_deltree(tree1, BASEDIR_RMD); + smb2_util_rmdir(tree1, BASEDIR_RMD); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE ; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_RMD; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h = io.smb2.out.file.handle; + + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h; + notify.smb2.in.recursive = false; + + io.smb2.in.desired_access |= SEC_STD_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + req = smb2_notify_send(tree1, &(notify.smb2)); + + if (initial_delete_on_close) { + status = smb2_util_rmdir(tree2, BASEDIR_RMD); + CHECK_STATUS(status, NT_STATUS_OK); + } else { + status = smb2_create(tree2, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = io.smb2.out.file.handle; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb2_setinfo_file(tree2, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree2, io.smb2.out.file.handle); + } + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, NT_STATUS_DELETE_PENDING); + +done: + + smb2_util_close(tree1, h); + smb2_deltree(tree1, BASEDIR_RMD); + + return ret; +} + +static bool torture_smb2_notify_rmdir1(struct torture_context *torture, + struct smb2_tree *tree) +{ + return torture_smb2_notify_rmdir(torture, tree, tree, false); +} + +static bool torture_smb2_notify_rmdir2(struct torture_context *torture, + struct smb2_tree *tree) +{ + return torture_smb2_notify_rmdir(torture, tree, tree, true); +} + +static bool torture_smb2_notify_rmdir3(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return torture_smb2_notify_rmdir(torture, tree1, tree2, false); +} + +static bool torture_smb2_notify_rmdir4(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return torture_smb2_notify_rmdir(torture, tree1, tree2, true); +} + +static void notify_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct smb2_request *req = talloc_get_type_abort( + private_data, struct smb2_request); + + smb2_cancel(req); +} + +#define BASEDIR_INR BASEDIR "_INR" + +static bool torture_smb2_inotify_rename(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + struct smb2_notify notify; + struct notify_changes change1 = {0}; + struct notify_changes change2 = {0}; + struct smb2_create create; + union smb_setfileinfo sinfo; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_request *req; + struct tevent_timer *te = NULL; + bool ok = false; + + smb2_deltree(tree1, BASEDIR_INR); + + torture_comment(torture, "Testing change notify of a rename with inotify\n"); + + status = torture_smb2_testdir(tree1, BASEDIR_INR, &h1); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "torture_smb2_testdir failed"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_ALL; + create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = BASEDIR_INR "\\subdir-name"; + + status = smb2_create(tree2, torture, &create); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_create failed\n"); + h2 = create.out.file.handle; + + ZERO_STRUCT(notify); + notify.level = RAW_NOTIFY_SMB2; + notify.in.buffer_size = 4096; + notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.in.file.handle = h1; + notify.in.recursive = true; + req = smb2_notify_send(tree1, ¬ify); + torture_assert_not_null_goto(torture, req, ok, done, "smb2_notify_send failed\n"); + + while (!NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) { + if (tevent_loop_once(torture->ev) != 0) { + goto done; + } + } + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h2; + sinfo.rename_information.in.new_name = BASEDIR_INR "\\subdir-name-r"; + + status = smb2_setinfo_file(tree2, &sinfo); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_setinfo_file failed\n"); + + smb2_util_close(tree2, h2); + + te = tevent_add_timer(torture->ev, + tree1, + tevent_timeval_current_ofs(1, 0), + notify_timeout, + req); + torture_assert_not_null_goto(torture, te, ok, done, "tevent_add_timer failed\n"); + + status = smb2_notify_recv(req, torture, ¬ify); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_notify_recv failed\n"); + + torture_assert_goto(torture, notify.out.num_changes == 1 || notify.out.num_changes == 2, + ok, done, "bad notify\n"); + + change1 = notify.out.changes[0]; + if (notify.out.num_changes == 2) { + change2 = notify.out.changes[1]; + } else { + /* + * We may only get one event at a time, so check for the + * matching second event for the oldname/newname or + * removed/added pair. + */ + ZERO_STRUCT(notify); + notify.level = RAW_NOTIFY_SMB2; + notify.in.buffer_size = 4096; + notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.in.file.handle = h1; + notify.in.recursive = true; + req = smb2_notify_send(tree1, ¬ify); + torture_assert_not_null_goto(torture, req, ok, done, "smb2_notify_send failed\n"); + + status = smb2_notify_recv(req, torture, ¬ify); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_notify_recv failed\n"); + + torture_assert_goto(torture, notify.out.num_changes == 1, ok, done, + "bad notify\n"); + + change2 = notify.out.changes[0]; + } + + if ((change1.action != NOTIFY_ACTION_OLD_NAME) && + (change1.action != NOTIFY_ACTION_REMOVED)) + { + torture_fail_goto(torture, done, "bad change notification\n"); + } + torture_assert_str_equal_goto(torture, change1.name.s, "subdir-name", + ok, done, "bad change notification\n"); + + if ((change2.action != NOTIFY_ACTION_NEW_NAME) && + (change2.action != NOTIFY_ACTION_ADDED)) + { + torture_fail_goto(torture, done, "bad change notification\n"); + } + torture_assert_str_equal_goto(torture, change2.name.s, "subdir-name-r", + ok, done, "bad change notification\n"); + + ok = true; +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree1, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree2, h2); + } + + smb2_deltree(tree1, BASEDIR_INR); + return ok; +} + +/* + Test asking for a change notify on a handle without permissions. +*/ + +#define BASEDIR_HPERM BASEDIR "_HPERM" + +static bool torture_smb2_notify_handle_permissions( + struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1 = {{0}}; + struct smb2_request *req; + + smb2_deltree(tree, BASEDIR_HPERM); + smb2_util_rmdir(tree, BASEDIR_HPERM); + + torture_comment(torture, + "TESTING CHANGE NOTIFY " + "ON A HANDLE WITHOUT PERMISSIONS\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR_HPERM; + + status = smb2_create(tree, torture, &io.smb2); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree, ¬ify.smb2); + torture_assert_goto(torture, + req != NULL, + ret, + done, + "smb2_notify_send failed\n"); + + /* + * Cancel it, we don't really want to wait. + */ + smb2_cancel(req); + status = smb2_notify_recv(req, torture, ¬ify.smb2); + /* Handle h1 doesn't have permissions for ChangeNotify. */ + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, BASEDIR_HPERM); + return ret; +} + +/* + basic testing of SMB2 change notify +*/ +struct torture_suite *torture_smb2_notify_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "notify"); + + torture_suite_add_1smb2_test(suite, "valid-req", test_valid_request); + torture_suite_add_1smb2_test(suite, "tcon", torture_smb2_notify_tcon); + torture_suite_add_2smb2_test(suite, "dir", torture_smb2_notify_dir); + torture_suite_add_2smb2_test(suite, "mask", torture_smb2_notify_mask); + torture_suite_add_1smb2_test(suite, "tdis", torture_smb2_notify_tree_disconnect); + torture_suite_add_1smb2_test(suite, "tdis1", torture_smb2_notify_tree_disconnect_1); + torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change); + torture_suite_add_1smb2_test(suite, "close", torture_smb2_notify_close); + torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); + torture_suite_add_1smb2_test(suite, "session-reconnect", torture_smb2_notify_session_reconnect); + torture_suite_add_2smb2_test(suite, "invalid-reauth", torture_smb2_notify_invalid_reauth); + torture_suite_add_1smb2_test(suite, "tree", torture_smb2_notify_tree); + torture_suite_add_2smb2_test(suite, "basedir", torture_smb2_notify_basedir); + torture_suite_add_2smb2_test(suite, "double", torture_smb2_notify_double); + torture_suite_add_1smb2_test(suite, "file", torture_smb2_notify_file); + torture_suite_add_1smb2_test(suite, "tcp", torture_smb2_notify_tcp_disconnect); + torture_suite_add_2smb2_test(suite, "rec", torture_smb2_notify_recursive); + torture_suite_add_1smb2_test(suite, "overflow", torture_smb2_notify_overflow); + torture_suite_add_1smb2_test(suite, "rmdir1", + torture_smb2_notify_rmdir1); + torture_suite_add_1smb2_test(suite, "rmdir2", + torture_smb2_notify_rmdir2); + torture_suite_add_2smb2_test(suite, "rmdir3", + torture_smb2_notify_rmdir3); + torture_suite_add_2smb2_test(suite, "rmdir4", + torture_smb2_notify_rmdir4); + torture_suite_add_1smb2_test(suite, + "handle-permissions", + torture_smb2_notify_handle_permissions); + + suite->description = talloc_strdup(suite, "SMB2-NOTIFY tests"); + + return suite; +} + +/* + basic testing of SMB2 change notify +*/ +struct torture_suite *torture_smb2_notify_inotify_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "notify-inotify"); + + suite->description = talloc_strdup(suite, "SMB2-NOTIFY tests that use inotify"); + + torture_suite_add_2smb2_test(suite, "inotify-rename", torture_smb2_inotify_rename); + + return suite; +} diff --git a/source4/torture/smb2/notify_disabled.c b/source4/torture/smb2/notify_disabled.c new file mode 100644 index 0000000..f38941c --- /dev/null +++ b/source4/torture/smb2/notify_disabled.c @@ -0,0 +1,120 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 notify test suite + + Copyright (C) Stefan Metzmacher 2006 + Copyright (C) Andrew Tridgell 2009 + Copyright (C) Ralph Boehme 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "torture/util.h" + +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/security.h" + +#include "lib/events/events.h" + +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +#define BASEDIR "test_notify_disabled" + +/* + basic testing of change notify on directories +*/ +static bool torture_smb2_notify_disabled(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + + torture_comment(torture, "TESTING CHANGE NOTIFY DISABLED\n"); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OK, + ret, done, "smb2_create"); + h1 = io.smb2.out.file.handle; + + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree1, &(notify.smb2)); + status = smb2_notify_recv(req, torture, &(notify.smb2)); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_NOT_IMPLEMENTED, + ret, done, "smb2_notify_recv"); + + status = smb2_util_close(tree1, h1); + torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OK, + ret, done, "smb2_create"); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + basic testing of SMB2 change notify +*/ +struct torture_suite *torture_smb2_notify_disabled_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, + "change_notify_disabled"); + + torture_suite_add_1smb2_test(suite, "notfiy_disabled", torture_smb2_notify_disabled); + suite->description = talloc_strdup(suite, "SMB2-CHANGE-NOTIFY-DISABLED tests"); + + return suite; +} diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c new file mode 100644 index 0000000..90bf4d2 --- /dev/null +++ b/source4/torture/smb2/oplock.c @@ -0,0 +1,5405 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 oplocks + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan Metzmacher 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" + +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" +#include "libcli/smb/smbXcli_base.h" + +#include "lib/cmdline/cmdline.h" +#include "lib/events/events.h" + +#include "param/param.h" +#include "system/filesys.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/smb2/block.h" + +#include "lib/util/sys_rw.h" +#include "libcli/security/security.h" + +#define CHECK_RANGE(v, min, max) do { \ + if ((v) < (min) || (v) > (max)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got %d - should be between %d and %d\n", \ + __location__, #v, (int)v, (int)min, (int)max); \ + ret = false; \ + }} while (0) + +#define CHECK_STRMATCH(v, correct) do { \ + if (!v || strstr((v),(correct)) == NULL) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s "\ + "got '%s' - should be '%s'\n", \ + __location__, #v, v?v:"NULL", correct); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define BASEDIR "oplock_test" + +static struct { + struct smb2_handle handle; + uint8_t level; + struct smb2_break br; + int count; + int failures; + NTSTATUS failure_status; +} break_info; + +static void torture_oplock_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + ZERO_STRUCT(br); + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + break_info.failure_status = status; + } + + return; +} + +/* A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. */ +static bool torture_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + ZERO_STRUCT(break_info.br); + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + printf("Acking to %s [0x%02X] in oplock handler\n", name, level); + + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} + +/* + A handler function for oplock break notifications. Send a break to none + request. +*/ +static bool torture_oplock_handler_ack_to_none(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Acking to none in oplock handler\n"); + + ZERO_STRUCT(break_info.br); + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + + return true; +} + +/* + A handler function for oplock break notifications. Break from level II to + none. SMB2 requires that the client does not send an oplock break request to + the server in this case. +*/ +static bool torture_oplock_handler_level2_to_none( + struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Break from level II to none in oplock handler\n"); + + return true; +} + +/* A handler function for oplock break notifications. This should be used when + * test expects two break notifications, first to level II, then to none. */ +static bool torture_oplock_handler_two_notifications( + struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + ZERO_STRUCT(break_info.br); + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + printf("Breaking to %s [0x%02X] in oplock handler\n", name, level); + + if (level == SMB2_OPLOCK_LEVEL_NONE) + return true; + + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} +static void torture_oplock_handler_close_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req)) { + printf("close failed in oplock_handler_close\n"); + break_info.failures++; + } +} + +/* + a handler function for oplock break requests - close the file +*/ +static bool torture_oplock_handler_close(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_close io; + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = RAW_CLOSE_SMB2; + req = smb2_close_send(tree, &io); + if (req == NULL) { + printf("failed to send close in oplock_handler_close\n"); + return false; + } + + req->async.fn = torture_oplock_handler_close_recv; + req->async.private_data = NULL; + + return true; +} + +/* + a handler function for oplock break requests. Let it timeout +*/ +static bool torture_oplock_handler_timeout(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Let oplock break timeout\n"); + return true; +} + +static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx, + struct smb2_tree **tree) +{ + NTSTATUS status; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct smbcli_options options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + options.use_level2_oplocks = false; + + status = smb2_connect(tctx, host, + lpcfg_smb_ports(tctx->lp_ctx), share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + tree, tctx->ev, &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to connect to SMB2 share " + "\\\\%s\\%s - %s\n", host, share, + nt_errstr(status)); + return false; + } + return true; +} + +static bool test_smb2_oplock_exclusive1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h1; + struct smb2_handle h; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should not cause a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "unlink it - should also be no break\n"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive " + "oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should cause a break to level 2\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + /* now we have 2 level II oplocks... */ + torture_comment(tctx, "try to unlink it - should cause a break\n"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Error unlinking the file"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "close both handles\n"); + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive " + "oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "setpathinfo EOF should trigger a break to " + "none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF disposition causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_exclusive6_1.dat"; + const char *fname2 = BASEDIR "\\test_exclusive6_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sinfo; + struct smb2_close closeio; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree2, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "rename with the parent directory handle open " + "for DELETE should not generate a break but get " + "a sharing violation\n"); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname2; + status = smb2_setinfo_file(tree1, &sinfo); + + torture_comment(tctx, "trying rename while parent handle open for delete.\n"); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* Close the parent directory handle. */ + ZERO_STRUCT(closeio); + closeio.in.file.handle = h; + status = smb2_close(tree1, &closeio); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "Incorrect status"); + + /* Re-open without DELETE access. */ + ZERO_STRUCT(io); + io.smb2.in.oplock_level = 0; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL & (~SEC_STD_DELETE); + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the base directory"); + + torture_comment(tctx, "rename with the parent directory handle open " + "without DELETE should succeed without a break\n"); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname2; + status = smb2_setinfo_file(tree1, &sinfo); + + torture_comment(tctx, "trying rename while parent handle open without delete\n"); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive9(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h1, h2; + int i; + + struct { + uint32_t create_disposition; + uint32_t break_level; + } levels[] = { + { NTCREATEX_DISP_SUPERSEDE, SMB2_OPLOCK_LEVEL_NONE }, + { NTCREATEX_DISP_OPEN, SMB2_OPLOCK_LEVEL_II }, + { NTCREATEX_DISP_OVERWRITE_IF, SMB2_OPLOCK_LEVEL_NONE }, + { NTCREATEX_DISP_OPEN_IF, SMB2_OPLOCK_LEVEL_II }, + }; + + + status = torture_smb2_testdir(tree1, BASEDIR, &h1); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + smb2_util_close(tree1, h1); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + for (i=0; isession->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH1: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break\n"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should generate a self break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + char c = 0; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH2: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break, which we ack " + "as break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_ack_to_none; + tree1->session->transport->oplock.private_data = tree1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should not generate a break\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH3: if we close on break then the unlink " + "can succeed\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_read r; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH4: a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(r); + r.in.file.handle = h1; + r.in.offset = 0; + + status = smb2_read(tree1, tree1, &r); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH5: a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch6.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH6: a 2nd open should give a break to " + "level II if the first open allowed shared read\n"); + ZERO_STRUCT(break_info); + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch7(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch7.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when " + "we close instead of ack\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree2, h1); + smb2_util_close(tree2, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch8(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch8.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH8: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch9(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create " + "file\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Subsequent normal open should break oplock on " + "attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, h2); + + torture_comment(tctx, "third oplocked open should grant level2 without " + "break\n"); + ZERO_STRUCT(break_info); + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree2, h2, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch9a(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch9a.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create " + "file\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error creating the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.create_action, FILE_WAS_CREATED); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Subsequent attributes open should not break\n"); + + ZERO_STRUCT(break_info); + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h3 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(io.smb2.out.create_action, FILE_WAS_OPENED); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree2, h3); + + torture_comment(tctx, "Subsequent normal open should break oplock on " + "attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, h2); + + torture_comment(tctx, "third oplocked open should grant level2 without " + "break\n"); + ZERO_STRUCT(break_info); + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree2, h2, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + + +static bool test_smb2_oplock_batch10(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch10.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock " + "open should grant level2\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, 0); + + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "write should trigger a break to none\n"); + { + struct smb2_write wr; + DATA_BLOB data; + data = data_blob_talloc_zero(tree1, UINT16_MAX); + data.data[0] = (const uint8_t)'x'; + ZERO_STRUCT(wr); + wr.in.file.handle = h1; + wr.in.offset = 0; + wr.in.data = data; + status = smb2_write(tree1, &wr); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + } + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch11(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch11.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-eof on pathname breaks an exclusive oplock. */ + torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks " + "oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch12(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch12.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-allocation size on pathname breaks an exclusive + * oplock. */ + torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size " + "breaks oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_ALLOCATION_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.allocation_info.in.alloc_size = 65536 * 8; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch13(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch13.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH13: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE disposition causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + + return ret; +} + +static bool test_smb2_oplock_batch14(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch14.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH14: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_SUPERSEDE disposition causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch15(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch15.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a qpathinfo all info on pathname breaks a batch oplock. */ + torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks " + "a batch oplock (should not).\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree2, tctx, &qfi); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch16(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch16.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH16: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF disposition causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH17 test. Since + * SMB2 doesn't have a RENAME command this test isn't applicable. However, + * it's much less confusing, when comparing test, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch17(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH18 test. Since + * SMB2 doesn't have an NTRENAME command this test isn't applicable. However, + * it's much less confusing, when comparing tests, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch18(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +static bool test_smb2_oplock_batch19(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname1 = BASEDIR "\\test_batch19_1.dat"; + const char *fname2 = BASEDIR "\\test_batch19_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH19: open a file with an batch oplock " + "(share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + smb2_deltree(tree1, fname2); + return ret; +} + +static bool test_smb2_oplock_batch20(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_batch20_1.dat"; + const char *fname2 = BASEDIR "\\test_batch20_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH20: open a file with an batch oplock " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + torture_comment(tctx, "open the file a second time requesting batch " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.fname = fname1; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(break_info); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h2; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree2, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h2; + + status = smb2_getinfo_file(tree2, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + return ret; +} + +static bool test_smb2_oplock_batch21(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch21.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH21: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "writing should not generate a break\n"); + status = smb2_util_write(tree1, h1, &c, 0, 1); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch22a(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch22a.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct timeval tv; + int timeout = torture_setting_int(tctx, "oplocktimeout", 35); + int te; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH22: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "a 2nd open should succeed after the oplock " + "break timeout\n"); + tv = timeval_current(); + tree1->session->transport->oplock.handler = + torture_oplock_handler_timeout; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + CHECK_RANGE(te, timeout - 1, timeout + 15); + torture_comment(tctx, "waited %d seconds for oplock timeout\n", te); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch22b(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch22b.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2 = {{0}}; + struct timeval tv; + int timeout = torture_setting_int(tctx, "oplocktimeout", 35); + struct smb2_transport *transport1 = tree1->session->transport; + bool block_setup = false; + bool block_ok = false; + int te; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH22: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "a 2nd open should succeed after the oplock " + "break timeout\n"); + tv = timeval_current(); + tree1->session->transport->oplock.handler = + torture_oplock_handler_timeout; + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + block_ok = test_block_smb2_transport(tctx, transport1); + torture_assert(tctx, block_ok, "test_block_smb2_transport"); + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + CHECK_RANGE(te, 0, timeout); + torture_comment(tctx, "waited %d seconds for oplock timeout\n", te); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + +done: + if (block_ok) { + test_unblock_smb2_transport(tctx, transport1); + } + test_cleanup_blocked_transports(tctx); + + smb2_util_close(tree1, h1); + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree1, h2); + } + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch23(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch23.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH23: an open and ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open without level2 oplock support " + "should generate a break to level2\n"); + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h3 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 3rd open with level2 oplock support should " + "not generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree3, h3); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch24(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch24.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH24: a open without level support and " + "ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open with level2 oplock support should " + "generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree3, h2); + smb2_util_close(tree2, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch25(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch25.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH25: open a file with an batch oplock " + "(share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + status = smb2_util_setatr(tree1, fname, FILE_ATTRIBUTE_HIDDEN); + torture_assert_ntstatus_ok(tctx, status, "Setting attributes " + "shouldn't trigger an oplock break"); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname); + return ret; +} + +static bool test_smb2_oplock_batch26(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + const char *fname_base = BASEDIR "\\test_oplock.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = 0x120089; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname_base; + + /* + Open base file with a batch oplock. + */ + torture_comment(tctx, "Open the base file with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening base file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Got batch oplock on base file\n"); + + torture_comment(tctx, "Opening stream file with batch oplock..\n"); + + io.smb2.in.fname = fname_stream; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening stream file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Got batch oplock on stream file\n"); + + torture_comment(tctx, "Open base file again with batch oplock\n"); + + io.smb2.in.fname = fname_base; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h3 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h3); + smb2_util_close(tree1, h); + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Test how oplocks work on streams. */ +static bool test_raw_oplock_stream1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + union smb_open io; + const char *fname_base = BASEDIR "\\test_stream1.txt"; + const char *fname_stream, *fname_default_stream; + const char *default_stream = "::$DATA"; + const char *stream = "Stream One:$DATA"; + bool ret = true; + struct smb2_handle h, h_base, h_stream; + int i; + +#define NSTREAM_OPLOCK_RESULTS 8 + struct { + const char **fname; + bool open_base_file; + uint32_t oplock_req; + uint32_t oplock_granted; + } stream_oplock_results[NSTREAM_OPLOCK_RESULTS] = { + /* Request oplock on stream without the base file open. */ + {&fname_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + + /* Request oplock on stream with the base file open. */ + {&fname_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_II}, + {&fname_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_II}, + }; + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); + fname_default_stream = talloc_asprintf(tctx, "%s%s", fname_base, + default_stream); + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* Initialize handles to "closed". Using -1 in the first 64-bytes + * as the sentry for this */ + h_stream.data[0] = -1; + + /* cleanup */ + smb2_util_unlink(tree1, fname_base); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* Setup generic open parameters. */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = (SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL); + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + + /* Create the file with a stream */ + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error creating file"); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Change the disposition to open now that the file has been created. */ + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + + /* Try some permutations of taking oplocks on streams. */ + for (i = 0; i < NSTREAM_OPLOCK_RESULTS; i++) { + const char *fname = *stream_oplock_results[i].fname; + bool open_base_file = stream_oplock_results[i].open_base_file; + uint32_t oplock_req = stream_oplock_results[i].oplock_req; + uint32_t oplock_granted = + stream_oplock_results[i].oplock_granted; + + if (open_base_file) { + torture_comment(tctx, "Opening base file: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, + "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, + SMB2_OPLOCK_LEVEL_BATCH); + h_base = io.smb2.out.file.handle; + } + + torture_comment(tctx, "%d: Opening stream: %s with %d\n", i, + fname, oplock_req); + io.smb2.in.fname = fname; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = oplock_req; + + /* Do the open with the desired oplock on the stream. */ + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, oplock_granted); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Cleanup the base file if it was opened. */ + if (open_base_file) + smb2_util_close(tree2, h_base); + } + + /* Open the stream with an exclusive oplock. */ + torture_comment(tctx, "Opening stream: %s with %d\n", + fname_stream, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + h_stream = io.smb2.out.file.handle; + + /* Open the base file and see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening base file: %s with %d\n", + fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* Open the stream again to see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening stream again: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + /* Close the stream. */ + if (h_stream.data[0] != -1) { + smb2_util_close(tree1, h_stream); + } + + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_doc(struct torture_context *tctx, struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_oplock_doc.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + union smb_setfileinfo sfinfo; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + smb2_util_close(tree, h); + + /* cleanup */ + smb2_util_unlink(tree, fname); + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "open a file with a batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Set delete on close\n"); + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = h1; + sfinfo.disposition_info.in.delete_on_close = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_comment(tctx, "2nd open should not break and get " + "DELETE_PENDING\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.create_options = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree2, tctx, &io.smb2); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_DELETE_PENDING, + "Incorrect status"); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree, h1); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock, then open it again from a second client + * requesting no oplock. Having two open file handles should break our own + * oplock during BRL acquisition. + */ +static bool test_smb2_oplock_brl1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1,buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + ret = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree2, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + ZERO_STRUCT(lock); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Open a file with a batch oplock on one tree and then acquire a brl. + * We should not contend our own oplock. + */ +static bool test_smb2_oplock_brl2(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + ret = false; + goto done; + } + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should not break to " + "none\n"); + + ZERO_STRUCT(lock); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + /* With one file handle open a BRL should not contend our oplock. + * Thus, no oplock break will be received and the entire break_info + * struct will be 0 */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock twice from one tree and then acquire a + * brl. BRL acquisition should break our own oplock. + */ +static bool test_smb2_oplock_brl3(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + bool ret = true; + uint8_t buf[1000]; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1, h2; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + ret = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree1, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + ZERO_STRUCT(lock); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Starting the SMB2 specific oplock tests at 500 so we can keep the SMB1 + * tests in sync with an identically numbered SMB2 test */ + +/* Test whether the server correctly returns an error when we send + * a response to a levelII to none oplock notification. */ +static bool test_smb2_oplock_levelII500(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_levelII500.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "LEVELII500: acknowledging a break from II to " + "none should return an error\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none and when " + "we reply, an oplock break failure\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + /* Wait several times to receive both the break notification, and the + * NT_STATUS_INVALID_OPLOCK_PROTOCOL error in the break response */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + /* There appears to be a race condition in W2K8 and W2K8R2 where + * sometimes the server will happily reply to our break response with + * NT_STATUS_OK, and sometimes it will return the OPLOCK_PROTOCOL + * error. As the MS-SMB2 doc states that a client should not reply to + * a level2 to none break notification, I'm leaving the protocol error + * as the expected behavior. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 1); + torture_assert_ntstatus_equal(tctx, break_info.failure_status, + NT_STATUS_INVALID_OPLOCK_PROTOCOL, + "Incorrect status"); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + * Test a double-break. Open a file with exclusive. Send off a second open + * request with OPEN_IF, triggering a break to level2. This should respond + * with level2. Before replying to the break to level2, fire off a third open + * with OVERWRITE_IF. The expected sequence would be that the 3rd opener gets + * a level2 immediately triggered by a break to none, but that seems not the + * case. Still investigating what the right behaviour should be. + */ + +struct levelII501_state { + struct torture_context *tctx; + struct smb2_tree *tree1; + struct smb2_tree *tree2; + struct smb2_tree *tree3; + struct smb2_handle h; + struct smb2_handle h1; + union smb_open io; + + struct smb2_handle break_handle; + uint8_t break_to; + struct smb2_break br; + + bool done; +}; + +static bool torture_oplock_break_delay(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data); +static void levelII501_break_done(struct smb2_request *req); +static void levelII501_open1_done(struct smb2_request *req); +static void levelII501_open2_done(struct smb2_request *req); +static void levelII501_2ndopen_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); +static void levelII501_break_timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); +static void levelII501_timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); + +static bool test_smb2_oplock_levelII501(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_levelII501.dat"; + NTSTATUS status; + bool ret = true; + struct levelII501_state *state; + struct smb2_request *req; + struct tevent_timer *te; + + state = talloc(tctx, struct levelII501_state); + state->tctx = tctx; + state->done = false; + state->tree1 = tree1; + state->tree2 = tree2; + + if (!torture_smb2_connection(tctx, &state->tree3)) { + torture_fail(tctx, "Establishing SMB2 connection failed\n"); + return false; + } + + status = torture_smb2_testdir(tree1, BASEDIR, &state->h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(state->io.smb2); + state->io.generic.level = RAW_OPEN_SMB2; + state->io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + state->io.smb2.in.alloc_size = 0; + state->io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + state->io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + state->io.smb2.in.create_options = 0; + state->io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + state->io.smb2.in.security_flags = 0; + state->io.smb2.in.fname = fname; + + torture_comment(tctx, "LEVELII501: Test double break sequence\n"); + ZERO_STRUCT(break_info); + + state->io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + state->io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + state->io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + state->io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + tree1->session->transport->oplock.handler = torture_oplock_break_delay; + tree1->session->transport->oplock.private_data = state; + + status = smb2_create(tree1, tctx, &(state->io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + state->h1 = state->io.smb2.out.file.handle; + CHECK_VAL(state->io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + /* + * Trigger a break to level2 + */ + + req = smb2_create_send(tree2, &state->io.smb2); + req->async.fn = levelII501_open1_done; + req->async.private_data = state; + + te = tevent_add_timer( + tctx->ev, tctx, tevent_timeval_current_ofs(0, 200000), + levelII501_2ndopen_cb, state); + torture_assert(tctx, te != NULL, "tevent_add_timer failed\n"); + + te = tevent_add_timer( + tctx->ev, tctx, tevent_timeval_current_ofs(2, 0), + levelII501_timeout_cb, state); + torture_assert(tctx, te != NULL, "tevent_add_timer failed\n"); + + while (!state->done) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "tevent_loop_once failed\n"); + } + } + + return ret; +} + +/* + * Fire off a second open after a little timeout + */ + +static void levelII501_2ndopen_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct levelII501_state *state = talloc_get_type_abort( + private_data, struct levelII501_state); + struct smb2_request *req; + + state->io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + req = smb2_create_send(state->tree3, &state->io.smb2); + req->async.fn = levelII501_open2_done; + req->async.private_data = state; +} + +/* + * Postpone the break response by 500 msec + */ +static bool torture_oplock_break_delay(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + struct levelII501_state *state = talloc_get_type_abort( + private_data, struct levelII501_state); + const char *name; + struct tevent_timer *te; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + state->break_handle = *handle; + state->break_to = level; + + switch(level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break; + } + printf("Got break to %s [0x%02X] in oplock handler, postponing " + "break response for 500msec\n", name, level); + + te = tevent_add_timer( + state->tctx->ev, state->tctx, + tevent_timeval_current_ofs(0, 500000), + levelII501_break_timeout_cb, state); + torture_assert(state->tctx, te != NULL, "tevent_add_timer failed\n"); + + return true; +} + +static void levelII501_break_timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct levelII501_state *state = talloc_get_type_abort( + private_data, struct levelII501_state); + struct smb2_request *req; + + talloc_free(te); + + ZERO_STRUCT(state->br); + state->br.in.file.handle = state->break_handle; + state->br.in.oplock_level = state->break_to; + + req = smb2_break_send(state->tree1, &state->br); + req->async.fn = levelII501_break_done; + req->async.private_data = state; +} + +static void levelII501_break_done(struct smb2_request *req) +{ + struct smb2_break io; + NTSTATUS status; + + status = smb2_break_recv(req, &io); + printf("break done: %s\n", nt_errstr(status)); +} + +static void levelII501_open1_done(struct smb2_request *req) +{ + struct levelII501_state *state = talloc_get_type_abort( + req->async.private_data, struct levelII501_state); + struct smb2_create io; + NTSTATUS status; + + status = smb2_create_recv(req, state, &io); + printf("open1 done: %s\n", nt_errstr(status)); +} + +static void levelII501_open2_done(struct smb2_request *req) +{ + struct levelII501_state *state = talloc_get_type_abort( + req->async.private_data, struct levelII501_state); + struct smb2_create io; + NTSTATUS status; + + status = smb2_create_recv(req, state, &io); + printf("open2 done: %s\n", nt_errstr(status)); +} + +static void levelII501_timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct levelII501_state *state = talloc_get_type_abort( + private_data, struct levelII501_state); + talloc_free(te); + state->done = true; +} + +static bool test_smb2_oplock_levelII502(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) + +{ + const char *fname = BASEDIR "\\test_levelII502.dat"; + NTSTATUS status; + union smb_open io; + struct smb2_close closeio; + struct smb2_handle h; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment( + tctx, + "LEVELII502: Open a stale LEVEL2 oplock with OVERWRITE"); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + torture_assert(tctx, + io.smb2.out.oplock_level==SMB2_OPLOCK_LEVEL_II, + "Did not get LEVEL_II oplock\n"); + + status = smbXcli_conn_samba_suicide( + tree1->session->transport->conn, 93); + torture_assert_ntstatus_ok(tctx, status, "suicide failed"); + + sleep(1); + + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + torture_assert(tctx, + io.smb2.out.oplock_level==SMB2_OPLOCK_LEVEL_BATCH, + "Did not get BATCH oplock\n"); + + closeio = (struct smb2_close) { + .in.file.handle = io.smb2.out.file.handle, + }; + status = smb2_close(tree2, &closeio); + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_OK, "close failed"); + + return true; +} + +static bool test_oplock_statopen1_do(struct torture_context *tctx, + struct smb2_tree *tree, + uint32_t access_mask, + bool expect_stat_open) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + const char *fname = "oplock_statopen1.dat"; + bool ret = true; + + /* Open file with exclusive oplock. */ + cr = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + .in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH, + }; + status = smb2_create(tree, mem_ctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = cr.out.file.handle; + CHECK_VAL(cr.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* Stat open */ + cr = (struct smb2_create) { + .in.desired_access = access_mask, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + }; + status = smb2_create(tree, mem_ctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h2 = cr.out.file.handle; + + if (expect_stat_open) { + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + if (!ret) { + goto done; + } + } else { + CHECK_VAL(break_info.count, 1); + } + +done: + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + talloc_free(mem_ctx); + return ret; +} + +static bool test_smb2_oplock_statopen1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "oplock_statopen1.dat"; + size_t i; + bool ret = true; + struct { + uint32_t access_mask; + bool expect_stat_open; + } tests[] = { + { + .access_mask = FILE_READ_DATA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_WRITE_DATA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_READ_EA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_WRITE_EA, + .expect_stat_open = false, + }, + { + .access_mask = FILE_EXECUTE, + .expect_stat_open = false, + }, + { + .access_mask = FILE_READ_ATTRIBUTES, + .expect_stat_open = true, + }, + { + .access_mask = FILE_WRITE_ATTRIBUTES, + .expect_stat_open = true, + }, + { + .access_mask = DELETE_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = READ_CONTROL_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = WRITE_DAC_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = WRITE_OWNER_ACCESS, + .expect_stat_open = false, + }, + { + .access_mask = SYNCHRONIZE_ACCESS, + .expect_stat_open = true, + }, + }; + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + ZERO_STRUCT(break_info); + + ret = test_oplock_statopen1_do(tctx, + tree, + tests[i].access_mask, + tests[i].expect_stat_open); + if (ret == true) { + continue; + } + torture_result(tctx, TORTURE_FAIL, + "test %zu: access_mask: %s, " + "expect_stat_open: %s\n", + i, + get_sec_mask_str(tree, tests[i].access_mask), + tests[i].expect_stat_open ? "yes" : "no"); + goto done; + } + +done: + smb2_util_unlink(tree, fname); + return ret; +} + +struct torture_suite *torture_smb2_oplocks_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "oplock"); + + torture_suite_add_2smb2_test(suite, "exclusive1", test_smb2_oplock_exclusive1); + torture_suite_add_2smb2_test(suite, "exclusive2", test_smb2_oplock_exclusive2); + torture_suite_add_2smb2_test(suite, "exclusive3", test_smb2_oplock_exclusive3); + torture_suite_add_2smb2_test(suite, "exclusive4", test_smb2_oplock_exclusive4); + torture_suite_add_2smb2_test(suite, "exclusive5", test_smb2_oplock_exclusive5); + torture_suite_add_2smb2_test(suite, "exclusive6", test_smb2_oplock_exclusive6); + torture_suite_add_2smb2_test(suite, "exclusive9", + test_smb2_oplock_exclusive9); + torture_suite_add_2smb2_test(suite, "batch1", test_smb2_oplock_batch1); + torture_suite_add_2smb2_test(suite, "batch2", test_smb2_oplock_batch2); + torture_suite_add_2smb2_test(suite, "batch3", test_smb2_oplock_batch3); + torture_suite_add_2smb2_test(suite, "batch4", test_smb2_oplock_batch4); + torture_suite_add_2smb2_test(suite, "batch5", test_smb2_oplock_batch5); + torture_suite_add_2smb2_test(suite, "batch6", test_smb2_oplock_batch6); + torture_suite_add_2smb2_test(suite, "batch7", test_smb2_oplock_batch7); + torture_suite_add_2smb2_test(suite, "batch8", test_smb2_oplock_batch8); + torture_suite_add_2smb2_test(suite, "batch9", test_smb2_oplock_batch9); + torture_suite_add_2smb2_test(suite, "batch9a", test_smb2_oplock_batch9a); + torture_suite_add_2smb2_test(suite, "batch10", test_smb2_oplock_batch10); + torture_suite_add_2smb2_test(suite, "batch11", test_smb2_oplock_batch11); + torture_suite_add_2smb2_test(suite, "batch12", test_smb2_oplock_batch12); + torture_suite_add_2smb2_test(suite, "batch13", test_smb2_oplock_batch13); + torture_suite_add_2smb2_test(suite, "batch14", test_smb2_oplock_batch14); + torture_suite_add_2smb2_test(suite, "batch15", test_smb2_oplock_batch15); + torture_suite_add_2smb2_test(suite, "batch16", test_smb2_oplock_batch16); + torture_suite_add_1smb2_test(suite, "batch19", test_smb2_oplock_batch19); + torture_suite_add_2smb2_test(suite, "batch20", test_smb2_oplock_batch20); + torture_suite_add_1smb2_test(suite, "batch21", test_smb2_oplock_batch21); + torture_suite_add_1smb2_test(suite, "batch22a", test_smb2_oplock_batch22a); + torture_suite_add_2smb2_test(suite, "batch22b", test_smb2_oplock_batch22b); + torture_suite_add_2smb2_test(suite, "batch23", test_smb2_oplock_batch23); + torture_suite_add_2smb2_test(suite, "batch24", test_smb2_oplock_batch24); + torture_suite_add_1smb2_test(suite, "batch25", test_smb2_oplock_batch25); + torture_suite_add_1smb2_test(suite, "batch26", test_smb2_oplock_batch26); + torture_suite_add_2smb2_test(suite, "stream1", test_raw_oplock_stream1); + torture_suite_add_2smb2_test(suite, "doc", test_smb2_oplock_doc); + torture_suite_add_2smb2_test(suite, "brl1", test_smb2_oplock_brl1); + torture_suite_add_1smb2_test(suite, "brl2", test_smb2_oplock_brl2); + torture_suite_add_1smb2_test(suite, "brl3", test_smb2_oplock_brl3); + torture_suite_add_1smb2_test(suite, "levelii500", test_smb2_oplock_levelII500); + torture_suite_add_2smb2_test(suite, "levelii501", + test_smb2_oplock_levelII501); + torture_suite_add_2smb2_test(suite, "levelii502", + test_smb2_oplock_levelII502); + torture_suite_add_1smb2_test(suite, "statopen1", test_smb2_oplock_statopen1); + suite->description = talloc_strdup(suite, "SMB2-OPLOCK tests"); + + return suite; +} + +/* + stress testing of oplocks +*/ +bool test_smb2_bench_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree **trees; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int i, count=0; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + union smb_open io; + struct timeval tv; + struct smb2_handle h; + + trees = talloc_array(mem_ctx, struct smb2_tree *, torture_nprocs); + + torture_comment(tctx, "Opening %d connections\n", torture_nprocs); + for (i=0;isession->transport->oplock.handler = + torture_oplock_handler_close; + trees[i]->session->transport->oplock.private_data = trees[i]; + } + + status = torture_smb2_testdir(trees[0], BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + ZERO_STRUCT(io.smb2); + io.smb2.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\test.dat"; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + tv = timeval_current(); + + /* + we open the same file with SHARE_ACCESS_NONE from all the + connections in a round robin fashion. Each open causes an + oplock break on the previous connection, which is answered + by the oplock_handler_close() to close the file. + + This measures how fast we can pass on oplocks, and stresses + the oplock handling code + */ + torture_comment(tctx, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + for (i=0;idata[0], + (unsigned long long) handle->data[1]); + return false; + } + + info = &hold_info[i]; + + if (info->close_on_break) { + printf("oplock break on %s - closing\n", info->fname); + torture_oplock_handler_close(transport, handle, + level, private_data); + return true; + } + + printf("oplock break on %s - acking break\n", info->fname); + printf("Acking to none in oplock handler\n"); + + torture_oplock_handler_ack_to_none(transport, handle, + level, private_data); + return true; +} + +/* + used for manual testing of oplocks - especially interaction with + other filesystems (such as NFS and local access) +*/ +bool test_smb2_hold_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct torture_context *mem_ctx = talloc_new(tctx); + struct tevent_context *ev = tctx->ev; + int i; + struct smb2_handle h; + NTSTATUS status; + + torture_comment(tctx, "Setting up open files with oplocks in %s\n", + BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + tree->session->transport->oplock.handler = torture_oplock_handler_hold; + tree->session->transport->oplock.private_data = tree; + + /* setup the files */ + for (i=0;isession->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "Open didn't return NT_STATUS_SHARING_VIOLATION\n"); + h2 = create.out.file.handle; + + torture_wait_for_oplock_break(tctx); + if (break_info.count != 0) { + torture_warning(tctx, "Open caused oplock break\n"); + } + + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +static bool test_smb2_kernel_oplocks2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "test_kernel_oplock2.dat"; + const char *sname = "test_kernel_oplock2.dat:foo"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + + smb2_util_unlink(tree, fname); + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h2 = create.out.file.handle; + + torture_wait_for_oplock_break(tctx); + if (break_info.count != 0) { + torture_warning(tctx, "Stream open caused oplock break\n"); + } + + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +/** + * 1. 1st client opens file with oplock + * 2. 2nd client opens file + * + * Verify 2 triggers an oplock break + **/ +static bool test_smb2_kernel_oplocks3(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + const char *fname = "test_kernel_oplock3.dat"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + + /* 1 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n"); + + /* 2 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h2 = create.out.file.handle; + + torture_wait_for_oplock_break(tctx); + torture_assert_goto(tctx, break_info.count == 1, ret, done, "Expected 1 oplock break\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +/** + * 1) create testfile with stream + * 2) open file r/w with batch oplock, sharing read/delete + * 3) open stream on file for reading + * + * Verify 3) doesn't trigger an oplock break + **/ +static bool test_smb2_kernel_oplocks4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "test_kernel_oplock4.dat"; + const char *sname = "test_kernel_oplock4.dat:foo"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + smb2_util_unlink(tree, fname); + + /* 1 */ + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* 2 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_BATCH, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_BATCH\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h2 = create.out.file.handle; + + torture_wait_for_oplock_break(tctx); + if (break_info.count != 0) { + torture_warning(tctx, "Stream open caused oplock break\n"); + } + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +/** + * 1) create testfile with stream + * 2) open stream r/w with batch oplock -> batch oplock granted + * 3) open stream r/o with batch oplock + * + * Verify 3) does trigger an oplock break + **/ +static bool test_smb2_kernel_oplocks5(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "test_kernel_oplock4.dat"; + const char *sname = "test_kernel_oplock4.dat:foo"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + smb2_util_unlink(tree, fname); + + /* 1 */ + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* 2 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_BATCH, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_BATCH\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h2 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_NONE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_NONE\n"); + + torture_wait_for_oplock_break(tctx); + if (break_info.count != 1) { + torture_warning(tctx, "Stream open didn't cause oplock break\n"); + } + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +/** + * 1) create testfile with stream + * 2) 1st client opens stream r/w with batch oplock -> batch oplock granted + * 3) 2nd client opens stream r/o with batch oplock + * + * Verify 3) does trigger an oplock break + **/ +static bool test_smb2_kernel_oplocks6(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + const char *fname = "test_kernel_oplock6.dat"; + const char *sname = "test_kernel_oplock6.dat:foo"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + + /* 1 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* 2 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h1 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n"); + + /* 3 */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_READ; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n"); + h2 = create.out.file.handle; + + torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_NONE, ret, done, + "Oplock level is not SMB2_OPLOCK_LEVEL_NONE\n"); + + torture_wait_for_oplock_break(tctx); + torture_assert_goto(tctx, break_info.count == 1, ret, done, "Expected 1 oplock break\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +/** + * Recreate regression test from bug: + * + * https://bugzilla.samba.org/show_bug.cgi?id=13058 + * + * 1. smbd-1 opens the file and sets the oplock + * 2. smbd-2 tries to open the file. open() fails(EAGAIN) and open is deferred. + * 3. smbd-1 sends oplock break request to the client. + * 4. smbd-1 closes the file. + * 5. smbd-1 opens the file and sets the oplock. + * 6. smbd-2 calls defer_open_done(), and should re-break the oplock. + **/ + +static bool test_smb2_kernel_oplocks7(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + const char *fname = "test_kernel_oplock7.dat"; + NTSTATUS status; + bool ret = true; + struct smb2_create create; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + struct smb2_create create_2; + struct smb2_create io; + struct smb2_request *req; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + /* Close the open file on break. */ + tree->session->transport->oplock.handler = torture_oplock_handler_close; + tree->session->transport->oplock.private_data = tree; + ZERO_STRUCT(break_info); + + /* 1 - open file with oplock */ + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = fname; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error opening the file\n"); + CHECK_VAL(create.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + /* 2 - open file to break oplock */ + ZERO_STRUCT(create_2); + create_2.in.desired_access = SEC_RIGHTS_FILE_ALL; + create_2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create_2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create_2.in.create_disposition = NTCREATEX_DISP_OPEN; + create_2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create_2.in.fname = fname; + create_2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + /* Open on tree2 - should cause a break on tree */ + req = smb2_create_send(tree2, &create_2); + torture_assert(tctx, req != NULL, "smb2_create_send"); + + /* The oplock break handler should close the file. */ + /* Steps 3 & 4. */ + torture_wait_for_oplock_break(tctx); + + tree->session->transport->oplock.handler = torture_oplock_handler; + + /* + * 5 - re-open on tree. NB. There is a race here + * depending on which smbd goes first. We either get + * an oplock level of SMB2_OPLOCK_LEVEL_EXCLUSIVE if + * the close and re-open on tree is processed first, or + * SMB2_OPLOCK_LEVEL_NONE if the pending create on + * tree2 is processed first. + */ + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error opening the file\n"); + + h1 = create.out.file.handle; + if (create.out.oplock_level != SMB2_OPLOCK_LEVEL_EXCLUSIVE && + create.out.oplock_level != SMB2_OPLOCK_LEVEL_NONE) { + torture_result(tctx, + TORTURE_FAIL, + "(%s): wrong value for oplock got 0x%x\n", + __location__, + (unsigned int)create.out.oplock_level); + ret = false; + goto done; + + } + + /* 6 - retrieve the second open. */ + status = smb2_create_recv(req, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error opening the file\n"); + h2 = io.out.file.handle; + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree2, h2); + } + smb2_util_unlink(tree, fname); + return ret; +} + +#ifdef HAVE_KERNEL_OPLOCKS_LINUX + +#ifndef RT_SIGNAL_LEASE +#define RT_SIGNAL_LEASE (SIGRTMIN+1) +#endif + +static int got_break; + +/* + * Signal handler. + */ + +static void got_rt_break(int sig) +{ + got_break = 1; +} + +static int got_alarm; + +/* + * Signal handler. + */ + +static void got_alarm_fn(int sig) +{ + got_alarm = 1; +} + +/* + * Child process function. + */ + +static int do_child_process(int pipefd, const char *name) +{ + int ret = 0; + int fd = -1; + char c = 0; + struct sigaction act; + sigset_t set; + sigset_t empty_set; + + /* Block RT_SIGNAL_LEASE and SIGALRM. */ + sigemptyset(&set); + sigemptyset(&empty_set); + sigaddset(&set, RT_SIGNAL_LEASE); + sigaddset(&set, SIGALRM); + ret = sigprocmask(SIG_SETMASK, &set, NULL); + if (ret == -1) { + return 11; + } + + /* Set up a signal handler for RT_SIGNAL_LEASE. */ + ZERO_STRUCT(act); + act.sa_handler = got_rt_break; + ret = sigaction(RT_SIGNAL_LEASE, &act, NULL); + if (ret == -1) { + return 1; + } + /* Set up a signal handler for SIGALRM. */ + ZERO_STRUCT(act); + act.sa_handler = got_alarm_fn; + ret = sigaction(SIGALRM, &act, NULL); + if (ret == -1) { + return 1; + } + /* Open the passed in file and get a kernel oplock. */ + fd = open(name, O_RDWR, 0666); + if (fd == -1) { + return 2; + } + + ret = fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE); + if (ret == -1) { + close(fd); + return 3; + } + + ret = fcntl(fd, F_SETLEASE, F_WRLCK); + if (ret == -1) { + close(fd); + return 4; + } + + /* Tell the parent we're ready. */ + ret = sys_write(pipefd, &c, 1); + if (ret != 1) { + close(fd); + return 5; + } + + /* Ensure the pause doesn't hang forever. */ + alarm(5); + + /* Wait for RT_SIGNAL_LEASE or SIGALRM. */ + ret = sigsuspend(&empty_set); + if (ret != -1 || errno != EINTR) { + close(fd); + return 6; + } + + if (got_alarm == 1) { + close(fd); + return 10; + } + + if (got_break != 1) { + close(fd); + return 7; + } + + /* Cancel any pending alarm. */ + alarm(0); + + /* Force the server to wait for 3 seconds. */ + sleep(3); + + /* Remove our lease. */ + ret = fcntl(fd, F_SETLEASE, F_UNLCK); + if (ret == -1) { + close(fd); + return 8; + } + + ret = close(fd); + if (ret == -1) { + return 9; + } + + /* All is well. */ + return 0; +} + +static bool wait_for_child_oplock(struct torture_context *tctx, + const char *localdir, + const char *fname) +{ + int fds[2]; + int ret; + pid_t pid; + char *name = talloc_asprintf(tctx, + "%s/%s", + localdir, + fname); + + torture_assert(tctx, name != NULL, "talloc failed"); + + ret = pipe(fds); + torture_assert(tctx, ret != -1, "pipe failed"); + + pid = fork(); + torture_assert(tctx, pid != (pid_t)-1, "fork failed"); + + if (pid != (pid_t)0) { + char c; + /* Parent. */ + TALLOC_FREE(name); + close(fds[1]); + ret = sys_read(fds[0], &c, 1); + torture_assert(tctx, ret == 1, "read failed"); + return true; + } + + /* Child process. */ + close(fds[0]); + ret = do_child_process(fds[1], name); + _exit(ret); + /* Notreached. */ +} +#else +static bool wait_for_child_oplock(struct torture_context *tctx, + const char *localdir, + const char *fname) +{ + return false; +} +#endif + +static void child_sig_term_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + int *pstatus = (int *)private_data; + int status = 0; + + wait(&status); + if (WIFEXITED(status)) { + *pstatus = WEXITSTATUS(status); + } else { + *pstatus = status; + } +} + +/* + * Deal with a non-smbd process holding a kernel oplock. + */ + +static bool test_smb2_kernel_oplocks8(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "test_kernel_oplock8.dat"; + const char *fname1 = "tmp_test_kernel_oplock8.dat"; + NTSTATUS status; + bool ret = true; + struct smb2_create io; + struct smb2_request *req = NULL; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + const char *localdir = torture_setting_string(tctx, "localdir", NULL); + struct tevent_signal *se = NULL; + int child_exit_code = -1; + time_t start; + time_t end; + +#ifndef HAVE_KERNEL_OPLOCKS_LINUX + torture_skip(tctx, "Need kernel oplocks for test"); +#endif + + if (localdir == NULL) { + torture_skip(tctx, "Need localdir for test"); + } + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname1); + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating testfile\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + se = tevent_add_signal(tctx->ev, + tctx, + SIGCHLD, + 0, + child_sig_term_handler, + &child_exit_code); + torture_assert(tctx, se != NULL, "tevent_add_signal failed\n"); + + /* Take the oplock locally in a sub-process. */ + ret = wait_for_child_oplock(tctx, localdir, fname); + torture_assert_goto(tctx, ret, ret, done, + "Wait for child process failed.\n"); + + /* + * Now try and open. This should block for 3 seconds. + * while the child process is still alive. + */ + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = fname; + + req = smb2_create_send(tree, &io); + torture_assert_goto(tctx, req != NULL, + ret, done, "smb2_create_send"); + + /* Ensure while the open is blocked the smbd is + still serving other requests. */ + io.in.fname = fname1; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + + /* Time the start -> end of the request. */ + start = time(NULL); + status = smb2_create(tree, tctx, &io); + end = time(NULL); + + /* Should succeed. */ + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error opening the second file\n"); + h1 = io.out.file.handle; + + /* in less than 2 seconds. Otherwise the server blocks. */ + torture_assert_goto(tctx, end - start < 2, + ret, done, "server was blocked !"); + + /* Pick up the return for the initial blocking open. */ + status = smb2_create_recv(req, tctx, &io); + + /* Which should also have succeeded. */ + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error opening the file\n"); + h2 = io.out.file.handle; + + /* Wait for the exit code from the child. */ + while (child_exit_code == -1) { + int rval = tevent_loop_once(tctx->ev); + torture_assert_goto(tctx, rval == 0, ret, + done, "tevent_loop_once error\n"); + } + + torture_assert_int_equal_goto(tctx, child_exit_code, 0, + ret, done, "Bad child exit code"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname1); + return ret; +} + +struct torture_suite *torture_smb2_kernel_oplocks_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "kernel-oplocks"); + + torture_suite_add_1smb2_test(suite, "kernel_oplocks1", test_smb2_kernel_oplocks1); + torture_suite_add_1smb2_test(suite, "kernel_oplocks2", test_smb2_kernel_oplocks2); + torture_suite_add_2smb2_test(suite, "kernel_oplocks3", test_smb2_kernel_oplocks3); + torture_suite_add_1smb2_test(suite, "kernel_oplocks4", test_smb2_kernel_oplocks4); + torture_suite_add_1smb2_test(suite, "kernel_oplocks5", test_smb2_kernel_oplocks5); + torture_suite_add_2smb2_test(suite, "kernel_oplocks6", test_smb2_kernel_oplocks6); + torture_suite_add_2smb2_test(suite, "kernel_oplocks7", test_smb2_kernel_oplocks7); + torture_suite_add_1smb2_test(suite, "kernel_oplocks8", test_smb2_kernel_oplocks8); + + suite->description = talloc_strdup(suite, "SMB2-KERNEL-OPLOCK tests"); + + return suite; +} diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c new file mode 100644 index 0000000..c17d32a --- /dev/null +++ b/source4/torture/smb2/oplock_break_handler.c @@ -0,0 +1,169 @@ +/* + * Unix SMB/CIFS implementation. + * + * test suite for SMB2 replay + * + * Copyright (C) Anubhav Rakshit 2014 + * Copyright (C) Stefan Metzmacher 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "oplock_break_handler.h" +#include "lib/events/events.h" + +struct break_info break_info; + +static void torture_oplock_ack_callback(struct smb2_request *req) +{ + NTSTATUS status; + + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + break_info.failure_status = status; + } +} + +/** + * A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. + */ + +bool torture_oplock_ack_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + + ZERO_STRUCT(break_info.br); + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + break_info.received_transport = tree->session->transport; + SMB_ASSERT(tree->session->transport == transport); + + if (break_info.oplock_skip_ack) { + torture_comment(break_info.tctx, + "transport[%p] skip acking to %s [0x%02X] in oplock handler\n", + transport, name, level); + return true; + } + + torture_comment(break_info.tctx, + "transport[%p] Acking to %s [0x%02X] in oplock handler\n", + transport, name, level); + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_ack_callback; + req->async.private_data = NULL; + + return true; +} + +/** + * A oplock break handler designed to ignore incoming break requests. + * This is used when incoming oplock break requests need to be ignored + */ +bool torture_oplock_ignore_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + return true; +} + +/* + * Timer handler function notifies the registering function that time is up + */ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; +} + +/* + * Wait a short period of time to receive a single oplock break request + */ +void torture_wait_for_oplock_break(struct torture_context *tctx) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + int old_count = break_info.count; + + /* Wait 1 second for an oplock break */ + ne = tevent_timeval_current_ofs(0, 1000000); + + te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up); + if (te == NULL) { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate.\n"); + goto done; + } + + torture_comment(tctx, "Waiting for a potential oplock break...\n"); + while (!timesup && break_info.count < old_count + 1) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate\n."); + goto done; + } + } + if (timesup) { + torture_comment(tctx, "... waiting for an oplock break timed out\n"); + } else { + torture_comment(tctx, "Got %u oplock breaks\n", + break_info.count - old_count); + } + +done: + /* We don't know if the timed event fired and was freed, we received + * our oplock break, or some other event triggered the loop. Thus, + * we create a tmp_ctx to be able to safely free/remove the timed + * event in all 3 cases. + */ + talloc_free(tmp_ctx); +} diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h new file mode 100644 index 0000000..c576782 --- /dev/null +++ b/source4/torture/smb2/oplock_break_handler.h @@ -0,0 +1,57 @@ +/* + * Unix SMB/CIFS implementation. + * + * test suite for SMB2 replay + * + * Copyright (C) Anubhav Rakshit 2014 + * Copyright (C) Stefan Metzmacher 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OPLOCK_BREAK_HANDLER_H__ +#define __OPLOCK_BREAK_HANDLER_H__ + +struct break_info { + struct torture_context *tctx; + bool oplock_skip_ack; + struct smb2_handle handle; + uint8_t level; + struct smb2_break br; + int count; + int failures; + NTSTATUS failure_status; + struct smb2_transport *received_transport; +}; + +extern struct break_info break_info; + +bool torture_oplock_ack_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data); +bool torture_oplock_ignore_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data); +void torture_wait_for_oplock_break(struct torture_context *tctx); + +static inline void torture_reset_break_info(struct torture_context *tctx, + struct break_info *r) +{ + ZERO_STRUCTP(r); + r->tctx = tctx; +} + +#endif /* __OPLOCK_BREAK_HANDLER_H__ */ diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c new file mode 100644 index 0000000..2bc0dc9 --- /dev/null +++ b/source4/torture/smb2/read.c @@ -0,0 +1,573 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 read test suite + + Copyright (C) Andrew Tridgell 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "librpc/gen_ndr/ndr_ioctl.h" + + +#define CHECK_STATUS(_status, _expected) \ + torture_assert_ntstatus_equal_goto(torture, _status, _expected, \ + ret, done, "Incorrect status") + +#define CHECK_VALUE(v, correct) \ + torture_assert_int_equal_goto(torture, v, correct, \ + ret, done, "Incorrect value") + +#define FNAME "smb2_readtest.dat" +#define DNAME "smb2_readtest.dir" + +static bool test_read_eof(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64*1024]; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + ZERO_STRUCT(buf); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 10); + + rd.in.min_count = 0; + rd.in.length = 10; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 0); + + rd.in.min_count = 1; + rd.in.length = 0; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 2; + rd.in.offset = sizeof(buf) - 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 1); + + rd.in.min_count = 2; + rd.in.length = 1; + rd.in.offset = sizeof(buf) - 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0x10000; + rd.in.length = 1; + rd.in.offset = 0; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0x10000 - 2; + rd.in.length = 1; + rd.in.offset = 0; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 10; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + +done: + talloc_free(tmp_ctx); + return ret; +} + + +static bool test_read_position(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64*1024]; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + union smb_fileinfo info; + + ZERO_STRUCT(buf); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 10); + + info.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + info.generic.in.file.handle = h; + + status = smb2_getinfo_file(tree, tmp_ctx, &info); + CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_VALUE(info.all_info2.out.position, 0); + } else { + CHECK_VALUE(info.all_info2.out.position, 10); + } + + +done: + talloc_free(tmp_ctx); + return ret; +} + +static bool test_read_dir(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + status = torture_smb2_testdir(tree, DNAME, &h); + if (!NT_STATUS_IS_OK(status)) { + printf(__location__ " Unable to create test directory '%s' - %s\n", DNAME, nt_errstr(status)); + return false; + } + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.min_count = 11; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.length = 0; + rd.in.min_count = 2592; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + + rd.in.length = 0; + rd.in.min_count = 0; + rd.in.channel = 0; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + +done: + talloc_free(tmp_ctx); + return ret; +} + +static bool test_read_access(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64 * 1024]; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + ZERO_STRUCT(buf); + + /* create a file */ + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* open w/ READ access - success */ + status = torture_smb2_testfile_access( + tree, FNAME, &h, SEC_FILE_READ_ATTRIBUTE | SEC_FILE_READ_DATA); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* open w/ EXECUTE access - success */ + status = torture_smb2_testfile_access( + tree, FNAME, &h, SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* open without READ or EXECUTE access - access denied */ + status = torture_smb2_testfile_access(tree, FNAME, &h, + SEC_FILE_READ_ATTRIBUTE); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + talloc_free(tmp_ctx); + return ret; +} + +/* + basic regression test for BUG 14607 + https://bugzilla.samba.org/show_bug.cgi?id=14607 +*/ +static bool test_read_bug14607(struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64 * 1024]; + struct smb2_read rd; + uint32_t timeout_msec; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + uint8_t *data = NULL; + uint32_t data_length = 0; + + memset(buf, 0x1f, ARRAY_SIZE(buf)); + + /* create a file */ + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = ARRAY_SIZE(buf); + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, ARRAY_SIZE(buf)); + torture_assert_mem_equal_goto(torture, rd.out.data.data, + buf, ARRAY_SIZE(buf), + ret, done, + "Invalid content smb2_read"); + + timeout_msec = tree->session->transport->options.request_timeout * 1000; + + status = smb2cli_read(tree->session->transport->conn, + timeout_msec, + tree->session->smbXcli, + tree->smbXcli, + rd.in.length, + rd.in.offset, + h.data[0], + h.data[1], + rd.in.min_count, + rd.in.remaining, + tmp_ctx, + &data, &data_length); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(data_length, ARRAY_SIZE(buf)); + torture_assert_mem_equal_goto(torture, data, + buf, ARRAY_SIZE(buf), + ret, done, + "Invalid content smb2cli_read"); + + status = smb2cli_ioctl(tree->session->transport->conn, + timeout_msec, + tree->session->smbXcli, + tree->smbXcli, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_SMBTORTURE_GLOBAL_READ_RESPONSE_BODY_PADDING8, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + 1, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL, + tmp_ctx, + &out_input_buffer, + &out_output_buffer); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) || + NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED) || + NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) + { + torture_comment(torture, + "FSCTL_SMBTORTURE_GLOBAL_READ_RESPONSE_BODY_PADDING8: %s\n", + nt_errstr(status)); + torture_skip(torture, "server doesn't support FSCTL_SMBTORTURE_GLOBAL_READ_RESPONSE_BODY_PADDING8\n"); + } + torture_assert_ntstatus_ok(torture, status, "FSCTL_SMBTORTURE_GLOBAL_READ_RESPONSE_BODY_PADDING8"); + + torture_assert_int_equal(torture, out_output_buffer.length, 0, + "output length"); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = ARRAY_SIZE(buf); + rd.in.offset = 0; + status = smb2_read(tree, tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, ARRAY_SIZE(buf)); + torture_assert_mem_equal_goto(torture, rd.out.data.data, + buf, ARRAY_SIZE(buf), + ret, done, + "Invalid content after padding smb2_read"); + + status = smb2cli_read(tree->session->transport->conn, + timeout_msec, + tree->session->smbXcli, + tree->smbXcli, + rd.in.length, + rd.in.offset, + h.data[0], + h.data[1], + rd.in.min_count, + rd.in.remaining, + tmp_ctx, + &data, &data_length); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(data_length, ARRAY_SIZE(buf)); + torture_assert_mem_equal_goto(torture, data, + buf, ARRAY_SIZE(buf), + ret, done, + "Invalid content after padding smb2cli_read"); + + status = smb2_util_close(tree, h); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + talloc_free(tmp_ctx); + return ret; +} + +/* + basic testing of SMB2 read +*/ +struct torture_suite *torture_smb2_read_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "read"); + + torture_suite_add_1smb2_test(suite, "eof", test_read_eof); + torture_suite_add_1smb2_test(suite, "position", test_read_position); + torture_suite_add_1smb2_test(suite, "dir", test_read_dir); + torture_suite_add_1smb2_test(suite, "access", test_read_access); + torture_suite_add_1smb2_test(suite, "bug14607", + test_read_bug14607); + + suite->description = talloc_strdup(suite, "SMB2-READ tests"); + + return suite; +} + +static bool test_aio_cancel(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle h; + uint8_t buf[64 * 1024]; + struct smb2_read r; + struct smb2_request *req = NULL; + int rc; + NTSTATUS status; + bool ret = true; + + ZERO_STRUCT(buf); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "torture_smb2_testfile failed\n"); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "smb2_util_write failed\n"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "smb2_util_close failed\n"); + + status = torture_smb2_testfile_access( + tree, FNAME, &h, SEC_RIGHTS_FILE_ALL); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "torture_smb2_testfile_access failed\n"); + + r = (struct smb2_read) { + .in.file.handle = h, + .in.length = 1, + .in.offset = 0, + .in.min_count = 1, + }; + + req = smb2_read_send(tree, &r); + torture_assert_goto( + tctx, + req != NULL, + ret, + done, + "smb2_read_send failed\n"); + + while (!req->cancel.can_cancel) { + rc = tevent_loop_once(tctx->ev); + torture_assert_goto( + tctx, + rc == 0, + ret, + done, + "tevent_loop_once failed\n"); + } + + status = smb2_cancel(req); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "smb2_cancel failed\n"); + + status = smb2_read_recv(req, tree, &r); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "smb2_read_recv failed\n"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "smb2_util_close failed\n"); + +done: + smb2_util_unlink(tree, FNAME); + return ret; +} + +/* + * aio testing against share with VFS module "delay_inject" + */ +struct torture_suite *torture_smb2_aio_delay_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "aio_delay"); + + torture_suite_add_1smb2_test(suite, "aio_cancel", test_aio_cancel); + + suite->description = talloc_strdup(suite, "SMB2 delayed aio tests"); + + return suite; +} diff --git a/source4/torture/smb2/read_write.c b/source4/torture/smb2/read_write.c new file mode 100644 index 0000000..707a49b --- /dev/null +++ b/source4/torture/smb2/read_write.c @@ -0,0 +1,361 @@ +/* + Unix SMB/CIFS implementation. + SMB read/write torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + Copyright (C) David Mulder 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "includes.h" +#include "torture/smbtorture.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +#define CHECK_STATUS(_status, _expected) \ + torture_assert_ntstatus_equal_goto(torture, _status, _expected, \ + ret, done, "Incorrect status") + +#define CHECK_VALUE(v, correct) \ + torture_assert_int_equal_goto(torture, v, correct, \ + ret, done, "Incorrect value") + +#define FNAME "smb2_writetest.dat" + +static bool run_smb2_readwritetest(struct torture_context *tctx, + struct smb2_tree *t1, struct smb2_tree *t2) +{ + const char *lockfname = "torture2.lck"; + struct smb2_create f1 = {0}; + struct smb2_create f2 = {0}; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + int i; + uint8_t buf[131072]; + bool correct = true; + NTSTATUS status; + int ret = 0; + + ret = smb2_deltree(t1, lockfname); + torture_assert(tctx, ret != -1, "unlink failed"); + + f1.in.desired_access = SEC_FILE_ALL; + f1.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + f1.in.create_disposition = FILE_CREATE; + f1.in.fname = lockfname; + + status = smb2_create(t1, tctx, &f1); + torture_assert_ntstatus_ok_goto(tctx, status, correct, done, + talloc_asprintf(tctx, "first open read/write of %s failed (%s)", + lockfname, nt_errstr(status))); + h1 = f1.out.file.handle; + + f2.in.desired_access = SEC_FILE_READ_DATA; + f2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + f2.in.create_disposition = FILE_OPEN; + f2.in.fname = lockfname; + + status = smb2_create(t2, tctx, &f2); + torture_assert_ntstatus_ok_goto(tctx, status, correct, done, + talloc_asprintf(tctx, "second open read-only of %s failed (%s)", + lockfname, nt_errstr(status))); + h2 = f2.out.file.handle; + + torture_comment(tctx, "Checking data integrity over %d ops\n", + torture_numops); + + for (i = 0; i < torture_numops; i++) { + struct smb2_write w = {0}; + struct smb2_read r = {0}; + size_t buf_size = ((unsigned int)random()%(sizeof(buf)-1))+ 1; + + if (i % 10 == 0) { + if (torture_setting_bool(tctx, "progress", true)) { + torture_comment(tctx, "%d\r", i); fflush(stdout); + } + } + + generate_random_buffer(buf, buf_size); + + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data.data = buf; + w.in.data.length = buf_size; + + status = smb2_write(t1, &w); + if (!NT_STATUS_IS_OK(status) || w.out.nwritten != buf_size) { + torture_comment(tctx, "write failed (%s)\n", + nt_errstr(status)); + torture_result(tctx, TORTURE_FAIL, + "wrote %d, expected %d\n", + (int)w.out.nwritten, (int)buf_size); + correct = false; + goto done; + } + + r.in.file.handle = h2; + r.in.offset = 0; + r.in.length = buf_size; + status = smb2_read(t2, tctx, &r); + if (!NT_STATUS_IS_OK(status) || r.out.data.length != buf_size) { + torture_comment(tctx, "read failed (%s)\n", + nt_errstr(status)); + torture_result(tctx, TORTURE_FAIL, + "read %d, expected %d\n", + (int)r.out.data.length, (int)buf_size); + correct = false; + goto done; + } + + torture_assert_mem_equal_goto(tctx, r.out.data.data, buf, + buf_size, correct, done, "read/write compare failed\n"); + } + + status = smb2_util_close(t2, h2); + torture_assert_ntstatus_ok_goto(tctx, status, correct, done, + talloc_asprintf(tctx, "close failed (%s)", nt_errstr(status))); + ZERO_STRUCT(h2); + + status = smb2_util_close(t1, h1); + torture_assert_ntstatus_ok_goto(tctx, status, correct, done, + talloc_asprintf(tctx, "close failed (%s)", nt_errstr(status))); + ZERO_STRUCT(h1); + +done: + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(t2, h2); + } + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(t1, h1); + } + + status = smb2_util_unlink(t1, lockfname); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "unlink failed (%s)", nt_errstr(status)); + } + + return correct; +} + + +static bool run_smb2_wrap_readwritetest(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return run_smb2_readwritetest(tctx, tree1, tree1); +} + +static bool test_rw_invalid(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64*1024]; + struct smb2_read rd; + struct smb2_write w = {0}; + union smb_setfileinfo sfinfo; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + ZERO_STRUCT(buf); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* set delete-on-close */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.in.file.handle = h; + status = smb2_setinfo_file(tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 10); + + rd.in.min_count = 0; + rd.in.length = 10; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 0); + + rd.in.min_count = 0; + rd.in.length = 1; + rd.in.offset = INT64_MAX - 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = INT64_MAX; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 0); + + rd.in.min_count = 0; + rd.in.length = 1; + rd.in.offset = INT64_MAX; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)INT64_MAX + 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)INT64_MIN; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-2; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-3; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (int64_t)-1; + w.in.data.data = buf; + w.in.data.length = ARRAY_SIZE(buf); + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (int64_t)-2; + w.in.data.data = buf; + w.in.data.length = ARRAY_SIZE(buf); + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MIN; + w.in.data.data = buf; + w.in.data.length = 1; + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MIN; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MAX; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 0); + + w.in.file.handle = h; + w.in.offset = INT64_MAX; + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (uint64_t)INT64_MAX + 1; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000; /* MAXFILESIZE */ + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000 - 1; /* MAXFILESIZE - 1 */ + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + if (TARGET_IS_SAMBA3(torture) || TARGET_IS_SAMBA4(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 1); + } else { + CHECK_STATUS(status, NT_STATUS_DISK_FULL); + } + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000; /* MAXFILESIZE */ + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 0); + +done: + talloc_free(tmp_ctx); + return ret; +} + +struct torture_suite *torture_smb2_readwrite_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "rw"); + + torture_suite_add_2smb2_test(suite, "rw1", run_smb2_readwritetest); + torture_suite_add_2smb2_test(suite, "rw2", run_smb2_wrap_readwritetest); + torture_suite_add_1smb2_test(suite, "invalid", test_rw_invalid); + + suite->description = talloc_strdup(suite, "SMB2 Samba4 Read/Write"); + + return suite; +} diff --git a/source4/torture/smb2/rename.c b/source4/torture/smb2/rename.c new file mode 100644 index 0000000..12636c4 --- /dev/null +++ b/source4/torture/smb2/rename.c @@ -0,0 +1,1751 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 rename test suite + + Copyright (C) Christian Ambach 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include +#include "lib/util/tevent_ntstatus.h" + +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +#include "librpc/gen_ndr/security.h" + +#define CHECK_VAL(v, correct) \ + do { \ + if ((v) != (correct)) { \ + torture_result(torture, \ + TORTURE_FAIL, \ + "(%s): wrong value for %s got " \ + "0x%llx - should be 0x%llx\n", \ + __location__, #v, \ + (unsigned long long)v, \ + (unsigned long long)correct); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define BASEDIR "test_rename" + +/* + * basic testing of rename: open file with DELETE access + * this should pass + */ + +static bool torture_smb2_rename_simple(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + union smb_fileinfo fi; + struct smb2_handle h1; + + ZERO_STRUCT(h1); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL|SEC_STD_DELETE; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Checking for new filename\n"); + + ZERO_STRUCT(fi); + fi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + fi.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree1, torture, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(h1); + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (h1.data[0] || h1.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + * basic testing of rename, this time do not request DELETE access + * for the file, this should fail + */ + +static bool torture_smb2_rename_simple2(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle h1; + + ZERO_STRUCT(h1); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(h1); + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (h1.data[0] || h1.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + + +/* + * testing of rename with no sharing allowed on file + * this should work + */ + +static bool torture_smb2_rename_no_sharemode(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + union smb_fileinfo fi; + struct smb2_handle h1; + + ZERO_STRUCT(h1); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = io.smb2.out.file.handle; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Checking for new filename\n"); + + ZERO_STRUCT(fi); + fi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + fi.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree1, torture, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(h1); + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (h1.data[0] || h1.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + * testing of rename when opening parent dir with delete access and delete + * sharing allowed + * should result in sharing violation + */ + +static bool torture_smb2_rename_with_delete_access(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle fh, dh; + + ZERO_STRUCT(fh); + ZERO_STRUCT(dh); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Opening parent directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE | SEC_FILE_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + dh = io.smb2.out.file.handle; + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_EA | SEC_FILE_READ_EA | + SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + fh = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = fh; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(fh); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(dh); + + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (fh.data[0] || fh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + } + if (dh.data[0] || dh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + } + + smb2_deltree(tree1, BASEDIR); + return ret; +} + + +/* + * testing of rename with delete access on parent dir + * this is a variation of the test above: parent dir is opened + * without share_delete, so rename must fail + */ + +static bool torture_smb2_rename_with_delete_access2(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle fh, dh; + + ZERO_STRUCT(fh); + ZERO_STRUCT(dh); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Opening parent directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE | SEC_FILE_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + dh = io.smb2.out.file.handle; + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_EA | SEC_FILE_READ_EA | + SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + fh = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = fh; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(fh); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(dh); + + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (fh.data[0] || fh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + } + if (dh.data[0] || dh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + } + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + * testing of rename when opening parent dir with no delete access and delete + * sharing allowed + * this should pass + */ + +static bool torture_smb2_rename_no_delete_access(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + union smb_fileinfo fi; + struct smb2_handle fh, dh; + + ZERO_STRUCT(fh); + ZERO_STRUCT(dh); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Opening parent directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE | SEC_FILE_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + dh = io.smb2.out.file.handle; + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_EA | SEC_FILE_READ_EA | + SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + fh = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = fh; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Checking for new filename\n"); + + ZERO_STRUCT(fi); + fi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + fi.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree1, torture, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(fh); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(dh); + + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (fh.data[0] || fh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + } + if (dh.data[0] || dh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + } + + smb2_deltree(tree1, BASEDIR); + return ret; +} + + +/* + * testing of rename with no delete access on parent dir + * this is the negative case of the test above: parent dir is opened + * without share_delete, so rename must fail + */ + +static bool torture_smb2_rename_no_delete_access2(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle fh, dh; + + ZERO_STRUCT(fh); + ZERO_STRUCT(dh); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Opening parent directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE | SEC_FILE_WRITE_EA | + SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + dh = io.smb2.out.file.handle; + + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_DAC | + SEC_STD_READ_CONTROL | SEC_STD_DELETE | SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_EA | SEC_FILE_READ_EA | + SEC_FILE_APPEND_DATA | SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + fh = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = fh; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(fh); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(dh); + + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (fh.data[0] || fh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + } + if (dh.data[0] || dh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + } + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* + * this is a replay of how Word 2010 saves a file + * this should pass + */ + +static bool torture_smb2_rename_msword(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + union smb_fileinfo fi; + struct smb2_handle fh, dh; + + ZERO_STRUCT(fh); + ZERO_STRUCT(dh); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = 0x60; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + fh = io.smb2.out.file.handle; + + torture_comment(torture, "Opening parent directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x00100080; + io.smb2.in.create_options = 0x00800021; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + dh = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming test file\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = fh; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "\\newname.txt"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Checking for new filename\n"); + + ZERO_STRUCT(fi); + fi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + fi.generic.in.file.handle = fh; + status = smb2_getinfo_file(tree1, torture, &fi); + CHECK_STATUS(status, NT_STATUS_OK); + + + torture_comment(torture, "Closing test file\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(fh); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(dh); + + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (fh.data[0] || fh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = fh; + status = smb2_close(tree1, &(cl.smb2)); + } + if (dh.data[0] || dh.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = dh; + status = smb2_close(tree1, &(cl.smb2)); + } + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool torture_smb2_rename_dir_openfile(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle d1, h1; + + ZERO_STRUCT(d1); + ZERO_STRUCT(h1); + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + d1 = io.smb2.out.file.handle; + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming directory\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = d1; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "-new"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = d1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(d1); + + torture_comment(torture, "Closing test file\n"); + + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(h1); + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (h1.data[0] || h1.data[1]) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + +struct rename_one_dir_cycle_state { + struct tevent_context *ev; + struct smb2_tree *tree; + struct smb2_handle file; + const char *base_name; + char *new_name; + unsigned *rename_counter; + + unsigned current; + unsigned max; + union smb_setfileinfo sinfo; +}; + +static void rename_one_dir_cycle_done(struct smb2_request *subreq); + +static struct tevent_req *rename_one_dir_cycle_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smb2_tree *tree, + struct smb2_handle file, + unsigned max_renames, + const char *base_name, + unsigned *rename_counter) +{ + struct tevent_req *req; + struct rename_one_dir_cycle_state *state; + struct smb2_request *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct rename_one_dir_cycle_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->tree = tree; + state->file = file; + state->base_name = base_name; + state->rename_counter = rename_counter; + state->current = 0; + state->max = max_renames; + + ZERO_STRUCT(state->sinfo); + state->sinfo.rename_information.level = + RAW_SFILEINFO_RENAME_INFORMATION; + state->sinfo.rename_information.in.file.handle = state->file; + state->sinfo.rename_information.in.overwrite = 0; + state->sinfo.rename_information.in.root_fid = 0; + + state->new_name = talloc_asprintf( + state, "%s-%u", state->base_name, state->current); + if (tevent_req_nomem(state->new_name, req)) { + return tevent_req_post(req, ev); + } + state->sinfo.rename_information.in.new_name = state->new_name; + + subreq = smb2_setinfo_file_send(state->tree, &state->sinfo); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + subreq->async.fn = rename_one_dir_cycle_done; + subreq->async.private_data = req; + return req; +} + +static void rename_one_dir_cycle_done(struct smb2_request *subreq) +{ + struct tevent_req *req = talloc_get_type_abort( + subreq->async.private_data, struct tevent_req); + struct rename_one_dir_cycle_state *state = tevent_req_data( + req, struct rename_one_dir_cycle_state); + NTSTATUS status; + + status = smb2_setinfo_recv(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + TALLOC_FREE(state->new_name); + + *state->rename_counter += 1; + + state->current += 1; + if (state->current >= state->max) { + tevent_req_done(req); + return; + } + + ZERO_STRUCT(state->sinfo); + state->sinfo.rename_information.level = + RAW_SFILEINFO_RENAME_INFORMATION; + state->sinfo.rename_information.in.file.handle = state->file; + state->sinfo.rename_information.in.overwrite = 0; + state->sinfo.rename_information.in.root_fid = 0; + + state->new_name = talloc_asprintf( + state, "%s-%u", state->base_name, state->current); + if (tevent_req_nomem(state->new_name, req)) { + return; + } + state->sinfo.rename_information.in.new_name = state->new_name; + + subreq = smb2_setinfo_file_send(state->tree, &state->sinfo); + if (tevent_req_nomem(subreq, req)) { + return; + } + subreq->async.fn = rename_one_dir_cycle_done; + subreq->async.private_data = req; +} + +static NTSTATUS rename_one_dir_cycle_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct rename_dir_bench_state { + struct tevent_context *ev; + struct smb2_tree *tree; + const char *base_name; + unsigned max_renames; + unsigned *rename_counter; + + struct smb2_create io; + union smb_setfileinfo sinfo; + struct smb2_close cl; + + struct smb2_handle file; +}; + +static void rename_dir_bench_opened(struct smb2_request *subreq); +static void rename_dir_bench_renamed(struct tevent_req *subreq); +static void rename_dir_bench_set_doc(struct smb2_request *subreq); +static void rename_dir_bench_closed(struct smb2_request *subreq); + +static struct tevent_req *rename_dir_bench_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smb2_tree *tree, + const char *base_name, + unsigned max_renames, + unsigned *rename_counter) +{ + struct tevent_req *req; + struct rename_dir_bench_state *state; + struct smb2_request *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct rename_dir_bench_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->tree = tree; + state->base_name = base_name; + state->max_renames = max_renames; + state->rename_counter = rename_counter; + + ZERO_STRUCT(state->io); + state->io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + state->io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + state->io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + state->io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + state->io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + state->io.in.fname = state->base_name; + + subreq = smb2_create_send(state->tree, &state->io); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + subreq->async.fn = rename_dir_bench_opened; + subreq->async.private_data = req; + return req; +} + +static void rename_dir_bench_opened(struct smb2_request *subreq) +{ + struct tevent_req *req = talloc_get_type_abort( + subreq->async.private_data, struct tevent_req); + struct rename_dir_bench_state *state = tevent_req_data( + req, struct rename_dir_bench_state); + struct smb2_create *io; + struct tevent_req *subreq2; + NTSTATUS status; + + io = talloc(state, struct smb2_create); + if (tevent_req_nomem(io, req)) { + return; + } + + status = smb2_create_recv(subreq, io, io); + if (tevent_req_nterror(req, status)) { + return; + } + state->file = io->out.file.handle; + TALLOC_FREE(io); + + subreq2 = rename_one_dir_cycle_send( + state, state->ev, state->tree, state->file, + state->max_renames, state->base_name, + state->rename_counter); + if (tevent_req_nomem(subreq2, req)) { + return; + } + tevent_req_set_callback(subreq2, rename_dir_bench_renamed, req); +} + +static void rename_dir_bench_renamed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct rename_dir_bench_state *state = tevent_req_data( + req, struct rename_dir_bench_state); + struct smb2_request *subreq2; + NTSTATUS status; + + status = rename_one_dir_cycle_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + ZERO_STRUCT(state->sinfo); + state->sinfo.disposition_info.level = + RAW_SFILEINFO_DISPOSITION_INFORMATION; + state->sinfo.disposition_info.in.file.handle = state->file; + state->sinfo.disposition_info.in.delete_on_close = true; + + subreq2 = smb2_setinfo_file_send(state->tree, &state->sinfo); + if (tevent_req_nomem(subreq2, req)) { + return; + } + subreq2->async.fn = rename_dir_bench_set_doc; + subreq2->async.private_data = req; +} + +static void rename_dir_bench_set_doc(struct smb2_request *subreq) +{ + struct tevent_req *req = talloc_get_type_abort( + subreq->async.private_data, struct tevent_req); + struct rename_dir_bench_state *state = tevent_req_data( + req, struct rename_dir_bench_state); + NTSTATUS status; + + status = smb2_setinfo_recv(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + ZERO_STRUCT(state->cl); + state->cl.in.file.handle = state->file; + + subreq = smb2_close_send(state->tree, &state->cl); + if (tevent_req_nomem(subreq, req)) { + return; + } + subreq->async.fn = rename_dir_bench_closed; + subreq->async.private_data = req; +} + +static void rename_dir_bench_closed(struct smb2_request *subreq) +{ + struct tevent_req *req = talloc_get_type_abort( + subreq->async.private_data, struct tevent_req); + struct smb2_close cl; + NTSTATUS status; + + status = smb2_close_recv(subreq, &cl); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS rename_dir_bench_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct rename_dirs_bench_state { + unsigned num_reqs; + unsigned num_done; +}; + +static void rename_dirs_bench_done(struct tevent_req *subreq); + +static struct tevent_req *rename_dirs_bench_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smb2_tree *tree, + const char *base_name, + unsigned num_parallel, + unsigned max_renames, + unsigned *rename_counter) +{ + struct tevent_req *req; + struct rename_dirs_bench_state *state; + unsigned i; + + req = tevent_req_create(mem_ctx, &state, + struct rename_dirs_bench_state); + if (req == NULL) { + return NULL; + } + state->num_reqs = num_parallel; + state->num_done = 0; + + for (i=0; inum_done += 1; + if (state->num_done >= state->num_reqs) { + tevent_req_done(req); + } +} + +static NTSTATUS rename_dirs_bench_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static bool torture_smb2_rename_dir_bench(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct tevent_req *req; + NTSTATUS status; + unsigned counter = 0; + bool ret; + + req = rename_dirs_bench_send(tctx, tctx->ev, tree, "dir", 3, 10, + &counter); + torture_assert(tctx, req != NULL, "rename_dirs_bench_send failed"); + + ret = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ret, "tevent_req_poll failed"); + + status = rename_dirs_bench_recv(req); + torture_comment(tctx, "rename_dirs_bench returned %s\n", + nt_errstr(status)); + TALLOC_FREE(req); + torture_assert_ntstatus_ok(tctx, status, "bench failed"); + return true; +} + +/* + * This test basically verifies that modify and change timestamps are preserved + * after file rename with outstanding open file handles. + */ + +static bool torture_smb2_rename_simple_modtime( + struct torture_context *torture, + struct smb2_tree *tree1) +{ + struct smb2_create c1, c2; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree1, BASEDIR); + smb2_util_mkdir(tree1, BASEDIR); + + torture_comment(torture, "Creating test file: file1.txt\n"); + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL|SEC_STD_DELETE, + .in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR "\\file1.txt", + }; + + status = smb2_create(tree1, torture, &c1); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + torture_comment(torture, "Waitig for 5 secs..\n"); + sleep(5); + + torture_comment(torture, "Creating test file: file2.txt\n"); + + c2 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL|SEC_STD_DELETE, + .in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR "\\file2.txt", + }; + + status = smb2_create(tree1, torture, &c2); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_create failed\n"); + h2 = c2.out.file.handle; + + torture_comment(torture, "Renaming file1.txt --> tmp1.txt\n"); + + si = (union smb_setfileinfo) { + .rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION, + .rename_information.in.file.handle = h1, + .rename_information.in.new_name = + BASEDIR "\\tmp1.txt", + }; + + status = smb2_setinfo_file(tree1, &si); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_setinfo_file failed\n"); + + torture_comment(torture, "GetInfo of tmp1.txt\n"); + + gi = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree1, torture, &gi); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(torture, "Check if timestamps are good after rename(file1.txt --> tmp1.txt).\n"); + + torture_assert_nttime_equal( + torture, c1.out.write_time, gi.all_info.out.write_time, + "Bad timestamp\n"); + torture_assert_nttime_equal( + torture, c1.out.change_time, gi.all_info.out.change_time, + "Bad timestamp\n"); + + torture_comment(torture, "Renaming file2.txt --> file1.txt\n"); + + si = (union smb_setfileinfo) { + .rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION, + .rename_information.in.file.handle = h2, + .rename_information.in.new_name = + BASEDIR "\\file1.txt", + }; + status = smb2_setinfo_file(tree1, &si); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_setinfo_file failed\n"); + + torture_comment(torture, "GetInfo of file1.txt\n"); + + gi = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h2, + }; + + status = smb2_getinfo_file(tree1, torture, &gi); + torture_assert_ntstatus_ok_goto(torture, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(torture, "Check if timestamps are good after rename(file2.txt --> file1.txt).\n"); + + torture_assert_nttime_equal( + torture, c2.out.write_time, gi.all_info.out.write_time, + "Bad timestamp\n"); + torture_assert_nttime_equal( + torture, c2.out.change_time, gi.all_info.out.change_time, + "Bad timestamp\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree1, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree1, h2); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_close_full_information(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + union smb_close cl; + struct smb2_create io = {0}; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + struct smb2_handle h3 = {{0}}; + union smb_setfileinfo sinfo; + NTSTATUS status; + const char *fname_src = "request.dat"; + const char *fname_dst = "renamed.dat"; + bool ret = true; + + /* Start with a tidy share. */ + smb2_util_unlink(tree1, fname_src); + smb2_util_unlink(tree1, fname_dst); + + /* Create the test file, and leave it open. */ + io.in.fname = fname_src; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tree1, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + /* Open the test file on the second connection. */ + ZERO_STRUCT(io); + io.in.fname = fname_src; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree2, tree2, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + + /* Now open for rename on the first connection. */ + ZERO_STRUCT(io); + io.in.fname = fname_src; + io.in.desired_access = SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tree1, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io.out.file.handle; + + /* Do the rename. */ + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h3; + sinfo.rename_information.in.new_name = fname_dst; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* And close h3. */ + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h3; + status = smb2_close(tree1, &cl.smb2); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(h3); + + /* + * Close h1 with SMB2_CLOSE_FLAGS_FULL_INFORMATION. + * Ensure we get data. + */ + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + cl.smb2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + status = smb2_close(tree1, &cl.smb2); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(h1); + CHECK_VAL(cl.smb2.out.file_attr, 0x20); + + /* + * Wait 3 seconds for name change to propagate + * to the other connection. + */ + sleep(3); + + /* + * Close h2 with SMB2_CLOSE_FLAGS_FULL_INFORMATION. + * This is on connection2. + * Ensure we get data. + */ + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h2; + cl.smb2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + status = smb2_close(tree2, &cl.smb2); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(h2); + CHECK_VAL(cl.smb2.out.file_attr, 0x20); + + done: + + if (h1.data[0] != 0 || h1.data[1] != 0) { + smb2_util_close(tree1, h1); + } + if (h2.data[0] != 0 || h2.data[1] != 0) { + smb2_util_close(tree2, h2); + } + if (h3.data[0] != 0 || h3.data[1] != 0) { + smb2_util_close(tree1, h3); + } + + smb2_util_unlink(tree1, fname_src); + smb2_util_unlink(tree1, fname_dst); + + return ret; +} + +/* + basic testing of SMB2 rename + */ +struct torture_suite *torture_smb2_rename_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "rename"); + + torture_suite_add_1smb2_test(suite, "simple", + torture_smb2_rename_simple); + + torture_suite_add_1smb2_test(suite, "simple_modtime", + torture_smb2_rename_simple_modtime); + + torture_suite_add_1smb2_test(suite, "simple_nodelete", + torture_smb2_rename_simple2); + + torture_suite_add_1smb2_test(suite, "no_sharing", + torture_smb2_rename_no_sharemode); + + torture_suite_add_1smb2_test(suite, + "share_delete_and_delete_access", + torture_smb2_rename_with_delete_access); + + torture_suite_add_1smb2_test(suite, + "no_share_delete_but_delete_access", + torture_smb2_rename_with_delete_access2); + + torture_suite_add_1smb2_test(suite, + "share_delete_no_delete_access", + torture_smb2_rename_no_delete_access); + + torture_suite_add_1smb2_test(suite, + "no_share_delete_no_delete_access", + torture_smb2_rename_no_delete_access2); + + torture_suite_add_1smb2_test(suite, + "msword", + torture_smb2_rename_msword); + + torture_suite_add_1smb2_test( + suite, "rename_dir_openfile", + torture_smb2_rename_dir_openfile); + + torture_suite_add_1smb2_test(suite, + "rename_dir_bench", + torture_smb2_rename_dir_bench); + + torture_suite_add_2smb2_test(suite, + "close-full-information", + test_smb2_close_full_information); + + suite->description = talloc_strdup(suite, "smb2.rename tests"); + + return suite; +} diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c new file mode 100644 index 0000000..d84ced8 --- /dev/null +++ b/source4/torture/smb2/replay.c @@ -0,0 +1,5515 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 replay + + Copyright (C) Anubhav Rakshit 2014 + Copyright (C) Stefan Metzmacher 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "libcli/security/security.h" +#include "libcli/resolve/resolve.h" +#include "lib/param/param.h" +#include "lib/events/events.h" +#include "oplock_break_handler.h" +#include "lease_break_handler.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +#define CHECK_HANDLE(__h1, __h2) \ + do { \ + CHECK_VAL((__h1)->data[0], (__h2)->data[0]); \ + CHECK_VAL((__h1)->data[1], (__h2)->data[1]); \ + } while(0) + +#define __IO_OUT_VAL(__io1, __io2, __m) \ + CHECK_VAL((__io1)->out.__m, (__io2)->out.__m) + +#define CHECK_CREATE_OUT(__io1, __io2) \ + do { \ + CHECK_HANDLE(&(__io1)->out.file.handle, \ + &(__io2)->out.file.handle); \ + __IO_OUT_VAL(__io1, __io2, oplock_level); \ + __IO_OUT_VAL(__io1, __io2, create_action); \ + __IO_OUT_VAL(__io1, __io2, create_time); \ + __IO_OUT_VAL(__io1, __io2, access_time); \ + __IO_OUT_VAL(__io1, __io2, write_time); \ + __IO_OUT_VAL(__io1, __io2, change_time); \ + __IO_OUT_VAL(__io1, __io2, alloc_size); \ + __IO_OUT_VAL(__io1, __io2, size); \ + __IO_OUT_VAL(__io1, __io2, file_attr); \ + __IO_OUT_VAL(__io1, __io2, durable_open); \ + __IO_OUT_VAL(__io1, __io2, durable_open_v2); \ + __IO_OUT_VAL(__io1, __io2, persistent_open); \ + __IO_OUT_VAL(__io1, __io2, timeout); \ + __IO_OUT_VAL(__io1, __io2, blobs.num_blobs); \ + if ((__io1)->out.oplock_level == SMB2_OPLOCK_LEVEL_LEASE) { \ + __IO_OUT_VAL(__io1, __io2, lease_response.lease_state);\ + __IO_OUT_VAL(__io1, __io2, lease_response.lease_key.data[0]);\ + __IO_OUT_VAL(__io1, __io2, lease_response.lease_key.data[1]);\ + } \ + } while(0) + +#define WAIT_FOR_ASYNC_RESPONSE(__tctx, __req) do { \ + torture_comment((__tctx), "Waiting for async response: %s\n", #__req); \ + while (!(__req)->cancel.can_cancel && (__req)->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once((__tctx)->ev) != 0) { \ + break; \ + } \ + } \ +} while(0) + +#define BASEDIR "replaytestdir" + +/** + * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various + * commands. We want to verify if the server returns an error code or not. + */ +static bool test_replay_commands(struct torture_context *tctx, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_read rd; + union smb_setfileinfo sfinfo; + union smb_fileinfo qfinfo; + union smb_ioctl ioctl; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + struct smb2_flush f; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + const char *fname = BASEDIR "\\replay_commands.dat"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "Replay tests\n"); + } + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + smb2cli_session_start_replay(tree->session->smbXcli); + + torture_comment(tctx, "Try Commands with Replay Flags Enabled\n"); + + torture_comment(tctx, "Trying create\n"); + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + /* + * Wireshark shows that the response has SMB2_FLAGS_REPLAY_OPERATION + * flags set. The server should ignore this flag. + */ + + torture_comment(tctx, "Trying write\n"); + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + f = (struct smb2_flush) { + .in.file.handle = h + }; + torture_comment(tctx, "Trying flush\n"); + status = smb2_flush(tree, &f); + CHECK_STATUS(status, NT_STATUS_OK); + + rd = (struct smb2_read) { + .in.file.handle = h, + .in.length = 10, + .in.offset = 0, + .in.min_count = 1 + }; + torture_comment(tctx, "Trying read\n"); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(rd.out.data.length, 10); + + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.position_information.in.file.handle = h; + sfinfo.position_information.in.position = 0x1000; + torture_comment(tctx, "Trying setinfo\n"); + status = smb2_setinfo_file(tree, &sfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + qfinfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_POSITION_INFORMATION, + .generic.in.file.handle = h + }; + torture_comment(tctx, "Trying getinfo\n"); + status = smb2_getinfo_file(tree, tmp_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0x1000); + + ioctl = (union smb_ioctl) { + .smb2.level = RAW_IOCTL_SMB2, + .smb2.in.file.handle = h, + .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID, + .smb2.in.max_output_response = 64, + .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL + }; + torture_comment(tctx, "Trying ioctl\n"); + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + CHECK_STATUS(status, NT_STATUS_OK); + + lck = (struct smb2_lock) { + .in.locks = el, + .in.lock_count = 0x0001, + .in.lock_sequence = 0x00000000, + .in.file.handle = h + }; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + torture_comment(tctx, "Trying lock\n"); + el[0].offset = 0x0000000000000000; + el[0].length = 0x0000000000000100; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + lck.in.file.handle = h; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(break_info.count, 0); +done: + smb2cli_session_stop_replay(tree->session->smbXcli); + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + + talloc_free(tmp_ctx); + + return ret; +} + +/** + * Test replay detection without create GUID on single channel. + * Regular creates can not be replayed. + * The return code is unaffected of the REPLAY_OPERATION flag. + */ +static bool test_replay_regular(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + uint32_t perms = 0; + bool ret = true; + const char *fname = BASEDIR "\\replay_regular.dat"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "No replay detection for regular create\n"); + + perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE | + SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE | + SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA | + SEC_FILE_WRITE_DATA; + + io = (struct smb2_create) { + .in.desired_access = perms, + .in.file_attributes = 0, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.share_access = NTCREATEX_SHARE_ACCESS_DELETE, + .in.create_options = 0x0, + .in.fname = fname + }; + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, tctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree, *h); + h = NULL; + smb2_util_unlink(tree, fname); + + /* + * Same experiment with different create disposition. + */ + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, tctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree, *h); + h = NULL; + smb2_util_unlink(tree, fname); + + /* + * Now with more generous share mode. + */ + io.in.share_access = smb2_util_share_access("RWD"); + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, tctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Durability V2 Create Replay Detection on Single Channel. + */ +static bool test_replay_dhv2_oplock1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io, ref1; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 on Single " + "Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io; + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + + /* + * Replay Durable V2 Create on single channel + */ + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io, &ref1); + CHECK_VAL(break_info.count, 0); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Durability V2 Create Replay Detection on Single Channel. + * Hand in a different oplock level in the replay. + * Server responds with the handed in oplock level and + * corresponding durable status, but does not change the + * oplock level or durable status of the opened file. + */ +static bool test_replay_dhv2_oplock2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io, ref1, ref2; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay_dhv2_oplock2.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 on Single " + "Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io; + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + + /* + * Replay durable v2 create on single channel: + * + * Replay the create with a different oplock (none). + * The server replies with the requested oplock level + * and also only replies with durable handle based + * on whether it could have been granted based on + * the requested oplock type. + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + /* + * Adapt the response to the expected values + */ + ref2 = ref1; + ref2.out.oplock_level = smb2_util_oplock_level(""); + ref2.out.durable_open_v2 = false; + ref2.out.timeout = 0; + ref2.out.blobs.num_blobs = 0; + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io, &ref2); + CHECK_VAL(break_info.count, 0); + + /* + * Prove that the open file still has a batch oplock + * by breaking it with another open. + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = GUID_random(); + io.in.timeout = UINT32_MAX; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + if (!share_is_so) { + CHECK_VAL(break_info.count, 1); + CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle); + CHECK_VAL(break_info.level, smb2_util_oplock_level("s")); + torture_reset_break_info(tctx, &break_info); + } + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Durability V2 Create Replay Detection on Single Channel. + * Replay with a different share mode. The share mode of + * the opened file is not changed by this. + */ +static bool test_replay_dhv2_oplock3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io, ref1; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay_dhv2_oplock3.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 on Single " + "Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io; + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + + /* + * Replay durable v2 create on single channel: + * + * Replay the create with a different share mode. + * The server replies with the requested share + * mode instead of that which is associated to + * the handle. + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io, &ref1); + CHECK_VAL(break_info.count, 0); + + /* + * In order to prove that the different share mode in the + * replayed create had no effect on the open file handle, + * show that a new create yields NT_STATUS_SHARING_VIOLATION. + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = GUID_random(); + io.in.timeout = UINT32_MAX; + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + if (!share_is_so) { + CHECK_VAL(break_info.count, 1); + CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle); + CHECK_VAL(break_info.level, smb2_util_oplock_level("s")); + torture_reset_break_info(tctx, &break_info); + } + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Durability V2 Create Replay Detection on Single Channel. + * Create with an oplock, and replay with a lease. + */ +static bool test_replay_dhv2_oplock_lease(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + struct smb2_lease ls; + uint64_t lease_key; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 on Single " + "Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + + /* + * Replay Durable V2 Create on single channel + * but replay it with a lease instead of an oplock. + */ + lease_key = random(); + smb2_lease_create(&io, &ls, false /* dir */, fname, + lease_key, smb2_util_lease_state("RH")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + + +/** + * Test durability v2 create replay detection on single channel. + * Variant with leases instead of oplocks: + * - open a file with a rh lease + * - upgrade to a rwh lease with a second create + * - replay the first create. + * ==> it gets back the upgraded lease level + */ +static bool test_replay_dhv2_lease1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2, ref1; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay2_lease1.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + struct smb2_lease ls1, ls2; + uint64_t lease_key; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease " + "on Single Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key = random(); + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease_key, smb2_util_lease_state("RH")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io1; + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key); + if (share_is_so) { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("R")); + CHECK_VAL(io1.out.durable_open_v2, false); + CHECK_VAL(io1.out.timeout, 0); + } else { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + } + + /* + * Upgrade the lease to RWH + */ + smb2_lease_create(&io2, &ls2, false /* dir */, fname, + lease_key, smb2_util_lease_state("RHW")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = GUID_random(); /* new guid... */ + io2.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = io2.out.file.handle; + h2 = &_h2; + + /* + * Replay Durable V2 Create on single channel. + * We get the io from open #1 but with the + * upgraded lease. + */ + + /* adapt expected lease in response */ + if (!share_is_so) { + ref1.out.lease_response.lease_state = + smb2_util_lease_state("RHW"); + } + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io1); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io1, &ref1); + CHECK_VAL(break_info.count, 0); + +done: + smb2cli_session_stop_replay(tree->session->smbXcli); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree, *h2); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test durability v2 create replay detection on single channel. + * Variant with leases instead of oplocks, where the + * replay does not specify the original lease level but + * just a "R" lease. This still gives the upgraded lease + * level in the reply. + * - open a file with a rh lease + * - upgrade to a rwh lease with a second create + * - replay the first create. + * ==> it gets back the upgraded lease level + */ +static bool test_replay_dhv2_lease2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2, ref1; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay2_lease2.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + struct smb2_lease ls1, ls2; + uint64_t lease_key; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease " + "on Single Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key = random(); + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease_key, smb2_util_lease_state("RH")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key); + if (share_is_so) { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("R")); + CHECK_VAL(io1.out.durable_open_v2, false); + CHECK_VAL(io1.out.timeout, 0); + } else { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + } + ref1 = io1; + _h1 = io1.out.file.handle; + h1 = &_h1; + + /* + * Upgrade the lease to RWH + */ + smb2_lease_create(&io2, &ls2, false /* dir */, fname, + lease_key, smb2_util_lease_state("RHW")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = GUID_random(); /* new guid... */ + io2.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = io2.out.file.handle; + h2 = &_h2; + + /* + * Replay Durable V2 Create on single channel. + * Changing the requested lease level to "R" + * does not change the response: + * We get the reply from open #1 but with the + * upgraded lease. + */ + + /* adapt the expected response */ + if (!share_is_so) { + ref1.out.lease_response.lease_state = + smb2_util_lease_state("RHW"); + } + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease_key, smb2_util_lease_state("R")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io1); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io1, &ref1); + CHECK_VAL(break_info.count, 0); + +done: + smb2cli_session_stop_replay(tree->session->smbXcli); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree, *h2); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test durability v2 create replay detection on single channel. + * create with a lease, and replay with a different lease key + */ +static bool test_replay_dhv2_lease3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay2_lease2.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + struct smb2_lease ls1, ls2; + uint64_t lease_key; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease " + "on Single Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key = random(); + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease_key, smb2_util_lease_state("RH")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key); + if (share_is_so) { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("R")); + CHECK_VAL(io1.out.durable_open_v2, false); + CHECK_VAL(io1.out.timeout, 0); + } else { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + } + _h1 = io1.out.file.handle; + h1 = &_h1; + + /* + * Upgrade the lease to RWH + */ + smb2_lease_create(&io2, &ls2, false /* dir */, fname, + lease_key, smb2_util_lease_state("RHW")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = GUID_random(); /* new guid... */ + io2.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = io2.out.file.handle; + h2 = &_h2; + + /* + * Replay Durable V2 Create on single channel. + * use a different lease key. + */ + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + random() /* lease key */, + smb2_util_lease_state("RH")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io1); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + smb2cli_session_stop_replay(tree->session->smbXcli); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree, *h2); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test durability v2 create replay detection on single channel. + * Do the original create with a lease, and do the replay + * with an oplock. + */ +static bool test_replay_dhv2_lease_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2, ref1; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay2_lease1.dat"; + struct smb2_transport *transport = tree->session->transport; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + struct smb2_lease ls1, ls2; + uint64_t lease_key; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease " + "on Single Channel\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key = random(); + + smb2_lease_create(&io1, &ls1, false /* dir */, fname, + lease_key, smb2_util_lease_state("RH")); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io1; + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key); + CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key); + if (share_is_so) { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("R")); + CHECK_VAL(io1.out.durable_open_v2, false); + CHECK_VAL(io1.out.timeout, 0); + } else { + CHECK_VAL(io1.out.lease_response.lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + } + + /* + * Upgrade the lease to RWH + */ + smb2_lease_create(&io2, &ls2, false /* dir */, fname, + lease_key, smb2_util_lease_state("RHW")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = GUID_random(); /* new guid... */ + io2.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = io2.out.file.handle; + h2 = &_h2; + + /* + * Replay Durable V2 Create on single channel. + * We get the io from open #1 but with the + * upgraded lease. + */ + + smb2_oplock_create_share(&io2, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io2.in.durable_open = false; + io2.in.durable_open_v2 = true; + io2.in.persistent_open = false; + io2.in.create_guid = create_guid; + io2.in.timeout = UINT32_MAX; + + /* adapt expected lease in response */ + if (!share_is_so) { + ref1.out.lease_response.lease_state = + smb2_util_lease_state("RHW"); + } + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io1); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io1, &ref1); + CHECK_VAL(break_info.count, 0); + +done: + smb2cli_session_stop_replay(tree->session->smbXcli); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree, *h2); + } + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +/** + * This tests replay with a pending open on a single + * channel. It tests the case where the client2 open + * is deferred because it conflicts with a HANDLE lease, + * which is broken because the operation should otherwise + * return NT_STATUS_SHARING_VIOLATION. + * + * With a durablev2 request containing a create_guid: + * - client2_level = NONE: + * but without asking for an oplock nor a lease. + * - client2_level = BATCH: + * and asking for a batch oplock. + * - client2_level = LEASE + * and asking for an RWH lease. + * + * While another client holds a batch oplock or + * RWH lease. (client1_level => LEASE or BATCH). + * + * There are two modes of this test one, with releaseing + * the oplock/lease of client1 via close or ack. + * (release_op SMB2_OP_CLOSE/SMB2_OP_BREAK). + * + * Windows doesn't detect replays in this case and + * always result in NT_STATUS_SHARING_VIOLATION. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + */ +static bool _test_dhv2_pending1_vs_violation(struct torture_context *tctx, + const char *testname, + struct smb2_tree *tree1, + uint8_t client1_level, + uint8_t release_op, + struct smb2_tree *tree2, + uint8_t client2_level, + NTSTATUS orig21_reject_status, + NTSTATUS replay22_reject_status, + NTSTATUS replay23_reject_status) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle *h2f = NULL; + struct smb2_handle _h21; + struct smb2_handle *h21 = NULL; + struct smb2_handle _h23; + struct smb2_handle *h23 = NULL; + struct smb2_handle _h24; + struct smb2_handle *h24 = NULL; + struct smb2_create io1, io21, io22, io23, io24; + struct GUID create_guid1 = GUID_random(); + struct GUID create_guid2 = GUID_random(); + struct smb2_request *req21 = NULL; + struct smb2_request *req22 = NULL; + bool ret = true; + char fname[256]; + struct smb2_transport *transport1 = tree1->session->transport; + uint32_t server_capabilities; + uint32_t share_capabilities; + struct smb2_lease ls1; + uint64_t lease_key1; + uint16_t lease_epoch1 = 0; + struct smb2_break op_ack1; + struct smb2_lease_break_ack lb_ack1; + struct smb2_lease ls2; + uint64_t lease_key2; + uint16_t lease_epoch2 = 0; + bool share_is_so; + struct smb2_transport *transport2 = tree2->session->transport; + int request_timeout2 = transport2->options.request_timeout; + struct smb2_session *session2 = tree2->session; + const char *hold_name = NULL; + + switch (client1_level) { + case SMB2_OPLOCK_LEVEL_LEASE: + hold_name = "RWH Lease"; + break; + case SMB2_OPLOCK_LEVEL_BATCH: + hold_name = "BATCH Oplock"; + break; + default: + smb_panic(__location__); + break; + } + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport1->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE || + client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_skip(tctx, "leases are not supported"); + } + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + if (share_is_so) { + torture_skip(tctx, talloc_asprintf(tctx, + "%s not supported on SCALEOUT share", + hold_name)); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s\\%s_%s.dat", + BASEDIR, testname, generate_random_str(tctx, 8)); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + ZERO_STRUCT(op_ack1); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + ZERO_STRUCT(lb_ack1); + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + smb2_keepalive(transport1); + transport2->oplock.handler = torture_oplock_ack_handler; + transport2->oplock.private_data = tree2; + transport2->lease.handler = torture_lease_handler; + transport2->lease.private_data = tree2; + smb2_keepalive(transport2); + + smb2_util_unlink(tree1, fname); + status = torture_smb2_testdir(tree1, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key1 = random(); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io1, &ls1, false /* dir */, fname, + lease_key1, NULL, smb2_util_lease_state("RWH"), lease_epoch1++); + } else { + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + } + io1.in.share_access = 0; + io1.in.desired_access = SEC_RIGHTS_FILE_ALL; + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid1; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[0], lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[1], ~lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_epoch, lease_epoch1); + CHECK_VAL(io1.out.lease_response_v2.lease_state, + smb2_util_lease_state("RWH")); + } else { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + } + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + + lease_key2 = random(); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io21, &ls2, false /* dir */, fname, + lease_key2, NULL, smb2_util_lease_state("RWH"), lease_epoch2++); + } else { + smb2_oplock_create(&io21, fname, client2_level); + } + io21.in.share_access = 0; + io21.in.desired_access = SEC_RIGHTS_FILE_ALL; + io21.in.desired_access = SEC_RIGHTS_FILE_READ; + io21.in.durable_open = false; + io21.in.durable_open_v2 = true; + io21.in.persistent_open = false; + io21.in.create_guid = create_guid2; + io21.in.timeout = UINT32_MAX; + io24 = io23 = io22 = io21; + + req21 = smb2_create_send(tree2, &io21); + torture_assert(tctx, req21 != NULL, "req21"); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + const struct smb2_lease_break *lb = + &lease_break_info.lease_break; + const struct smb2_lease *l = &lb->current_lease; + const struct smb2_lease_key *k = &l->lease_key; + + torture_wait_for_lease_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 1); + + torture_assert(tctx, + lease_break_info.lease_transport == transport1, + "expect lease break on transport1\n"); + CHECK_VAL(k->data[0], lease_key1); + CHECK_VAL(k->data[1], ~lease_key1); + /* + * With share none the handle lease + * is broken. + */ + CHECK_VAL(lb->new_lease_state, + smb2_util_lease_state("RW")); + CHECK_VAL(lb->break_flags, + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + CHECK_VAL(lb->new_epoch, lease_epoch1+1); + lease_epoch1 += 1; + + lb_ack1.in.lease.lease_key = lb->current_lease.lease_key; + lb_ack1.in.lease.lease_state = lb->new_lease_state; + } else { + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(lease_break_info.count, 0); + + torture_assert(tctx, + break_info.received_transport == transport1, + "expect oplock break on transport1\n"); + CHECK_VAL(break_info.handle.data[0], _h1.data[0]); + CHECK_VAL(break_info.handle.data[1], _h1.data[1]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + + op_ack1.in = break_info.br.in; + } + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + WAIT_FOR_ASYNC_RESPONSE(tctx, req21); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + if (NT_STATUS_EQUAL(replay22_reject_status, NT_STATUS_SHARING_VIOLATION)) { + /* + * The server is broken and doesn't + * detect a replay, so we start an async + * request and send a lease break ack + * after 5 seconds in order to avoid + * the 35 second delay. + */ + torture_comment(tctx, "Starting ASYNC Replay req22 expecting %s\n", + nt_errstr(replay22_reject_status)); + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 15; + req22 = smb2_create_send(tree2, &io22); + torture_assert(tctx, req22 != NULL, "req22"); + transport2->options.request_timeout = request_timeout2; + smb2cli_session_stop_replay(session2->smbXcli); + + WAIT_FOR_ASYNC_RESPONSE(tctx, req22); + } else { + torture_comment(tctx, "SYNC Replay io22 expecting %s\n", + nt_errstr(replay22_reject_status)); + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 5; + status = smb2_create(tree2, tctx, &io22); + CHECK_STATUS(status, replay22_reject_status); + transport2->options.request_timeout = request_timeout2; + smb2cli_session_stop_replay(session2->smbXcli); + } + + /* + * We don't expect any action for 35 seconds + * + * But we sleep just 5 seconds before we + * ack the break. + */ + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + if (release_op == SMB2_OP_CLOSE) { + torture_comment(tctx, "Closing h1\n"); + smb2_util_close(tree1, _h1); + h1 = NULL; + } else { + torture_comment(tctx, "Acking lease_key1\n"); + status = smb2_lease_break_ack(tree1, &lb_ack1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(lb_ack1.out.lease.lease_flags, 0); + CHECK_VAL(lb_ack1.out.lease.lease_state, lb_ack1.in.lease.lease_state); + CHECK_VAL(lb_ack1.out.lease.lease_key.data[0], lease_key1); + CHECK_VAL(lb_ack1.out.lease.lease_key.data[1], ~lease_key1); + CHECK_VAL(lb_ack1.out.lease.lease_duration, 0); + } + } else { + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + if (release_op == SMB2_OP_CLOSE) { + torture_comment(tctx, "Closing h1\n"); + smb2_util_close(tree1, _h1); + h1 = NULL; + } else { + torture_comment(tctx, "Acking break h1\n"); + status = smb2_break(tree1, &op_ack1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(op_ack1.out.oplock_level, op_ack1.in.oplock_level); + } + } + + torture_comment(tctx, "Checking req21 expecting %s\n", + nt_errstr(orig21_reject_status)); + status = smb2_create_recv(req21, tctx, &io21); + CHECK_STATUS(status, orig21_reject_status); + if (NT_STATUS_IS_OK(orig21_reject_status)) { + _h21 = io21.out.file.handle; + h21 = &_h21; + if (h2f == NULL) { + h2f = h21; + } + CHECK_VAL(h21->data[0], h2f->data[0]); + CHECK_VAL(h21->data[1], h2f->data[1]); + CHECK_CREATED(&io21, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io21.out.oplock_level, client2_level); + CHECK_VAL(io21.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io21.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else { + CHECK_VAL(io21.out.durable_open_v2, false); + } + } + + if (NT_STATUS_EQUAL(replay22_reject_status, NT_STATUS_SHARING_VIOLATION)) { + torture_comment(tctx, "Checking req22 expecting %s\n", + nt_errstr(replay22_reject_status)); + status = smb2_create_recv(req22, tctx, &io22); + CHECK_STATUS(status, replay22_reject_status); + } + + torture_comment(tctx, "SYNC Replay io23 expecting %s\n", + nt_errstr(replay23_reject_status)); + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 5; + status = smb2_create(tree2, tctx, &io23); + transport2->options.request_timeout = request_timeout2; + CHECK_STATUS(status, replay23_reject_status); + smb2cli_session_stop_replay(session2->smbXcli); + if (NT_STATUS_IS_OK(replay23_reject_status)) { + _h23 = io23.out.file.handle; + h23 = &_h23; + if (h2f == NULL) { + h2f = h23; + } + CHECK_VAL(h23->data[0], h2f->data[0]); + CHECK_VAL(h23->data[1], h2f->data[1]); + CHECK_CREATED(&io23, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io23.out.oplock_level, client2_level); + CHECK_VAL(io23.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io23.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io23.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io23.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io23.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io23.out.durable_open_v2, true); + CHECK_VAL(io23.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io23.out.durable_open_v2, true); + CHECK_VAL(io23.out.timeout, 300*1000); + } else { + CHECK_VAL(io23.out.durable_open_v2, false); + } + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + if (h1 != NULL) { + torture_comment(tctx, "Closing h1\n"); + smb2_util_close(tree1, _h1); + h1 = NULL; + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "SYNC Replay io24 expecting %s\n", + nt_errstr(NT_STATUS_OK)); + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 5; + status = smb2_create(tree2, tctx, &io24); + transport2->options.request_timeout = request_timeout2; + smb2cli_session_stop_replay(session2->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + _h24 = io24.out.file.handle; + h24 = &_h24; + if (h2f == NULL) { + h2f = h24; + } + CHECK_VAL(h24->data[0], h2f->data[0]); + CHECK_VAL(h24->data[1], h2f->data[1]); + CHECK_CREATED(&io24, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io24.out.oplock_level, client2_level); + CHECK_VAL(io24.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io24.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else { + CHECK_VAL(io24.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + status = smb2_util_close(tree2, *h24); + CHECK_STATUS(status, NT_STATUS_OK); + h24 = NULL; + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + +done: + + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_deltree(tree1, BASEDIR); + + TALLOC_FREE(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/* + * This tests replay with a pending open on a single + * channel. It tests the case where the client2 open + * is deferred because it conflicts with a HANDLE lease, + * which is broken because the operation should otherwise + * return NT_STATUS_SHARING_VIOLATION. + * + * With a durablev2 request containing a create_guid, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease, + * which is released by a close. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_SHARING_VIOLATION to the replay (after + * 35 seconds), and this tests reports NT_STATUS_IO_TIMEOUT, + * as it expects a NT_STATUS_FILE_NOT_AVAILABLE within 5 seconds. + * see test_dhv2_pending1n_vs_violation_lease_close_windows(). + */ +static bool test_dhv2_pending1n_vs_violation_lease_close_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_violation(tctx, __func__, + tree1, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OP_CLOSE, + tree2, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_OK, + NT_STATUS_FILE_NOT_AVAILABLE, + NT_STATUS_OK); +} + +/* + * This tests replay with a pending open on a single + * channel. It tests the case where the client2 open + * is deferred because it conflicts with a HANDLE lease, + * which is broken because the operation should otherwise + * return NT_STATUS_SHARING_VIOLATION. + * + * With a durablev2 request containing a create_guid, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease, + * which is released by a close. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange behavior of ignoring the + * replay, which is returned done by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE + * see test_dhv2_pending1n_vs_violation_lease_close_sane(). + */ +static bool test_dhv2_pending1n_vs_violation_lease_close_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_violation(tctx, __func__, + tree1, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OP_CLOSE, + tree2, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_OK, + NT_STATUS_SHARING_VIOLATION, + NT_STATUS_OK); +} + +/* + * This tests replay with a pending open on a single + * channel. It tests the case where the client2 open + * is deferred because it conflicts with a HANDLE lease, + * which is broken because the operation should otherwise + * return NT_STATUS_SHARING_VIOLATION. + * + * With a durablev2 request containing a create_guid, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease, + * which is released by a lease break ack. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_SHARING_VIOLATION to the replay (after + * 35 seconds), and this tests reports NT_STATUS_IO_TIMEOUT, + * as it expects a NT_STATUS_FILE_NOT_AVAILABLE within 5 seconds. + * see test_dhv2_pending1n_vs_violation_lease_ack_windows(). + */ +static bool test_dhv2_pending1n_vs_violation_lease_ack_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_violation(tctx, __func__, + tree1, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OP_BREAK, + tree2, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_SHARING_VIOLATION, + NT_STATUS_FILE_NOT_AVAILABLE, + NT_STATUS_SHARING_VIOLATION); +} + +/* + * This tests replay with a pending open on a single + * channel. It tests the case where the client2 open + * is deferred because it conflicts with a HANDLE lease, + * which is broken because the operation should otherwise + * return NT_STATUS_SHARING_VIOLATION. + * + * With a durablev2 request containing a create_guid, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease, + * which is released by a close. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange behavior of ignoring the + * replay, which is returned done by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE + * see test_dhv2_pending1n_vs_violation_lease_ack_sane(). + */ +static bool test_dhv2_pending1n_vs_violation_lease_ack_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_violation(tctx, __func__, + tree1, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OP_BREAK, + tree2, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_SHARING_VIOLATION, + NT_STATUS_SHARING_VIOLATION, + NT_STATUS_SHARING_VIOLATION); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid and + * a share_access of READ/WRITE/DELETE: + * - client2_level = NONE: + * but without asking for an oplock nor a lease. + * - client2_level = BATCH: + * and asking for a batch oplock. + * - client2_level = LEASE + * and asking for an RWH lease. + * + * While another client holds a batch oplock or + * RWH lease. (client1_level => LEASE or BATCH). + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + */ +static bool _test_dhv2_pending1_vs_hold(struct torture_context *tctx, + const char *testname, + uint8_t client1_level, + uint8_t client2_level, + NTSTATUS reject_status, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h21; + struct smb2_handle *h21 = NULL; + struct smb2_handle _h24; + struct smb2_handle *h24 = NULL; + struct smb2_create io1, io21, io22, io23, io24; + struct GUID create_guid1 = GUID_random(); + struct GUID create_guid2 = GUID_random(); + struct smb2_request *req21 = NULL; + bool ret = true; + char fname[256]; + struct smb2_transport *transport1 = tree1->session->transport; + uint32_t server_capabilities; + uint32_t share_capabilities; + struct smb2_lease ls1; + uint64_t lease_key1; + uint16_t lease_epoch1 = 0; + struct smb2_lease ls2; + uint64_t lease_key2; + uint16_t lease_epoch2 = 0; + bool share_is_so; + struct smb2_transport *transport2 = tree2->session->transport; + int request_timeout2 = transport2->options.request_timeout; + struct smb2_session *session2 = tree2->session; + const char *hold_name = NULL; + + switch (client1_level) { + case SMB2_OPLOCK_LEVEL_LEASE: + hold_name = "RWH Lease"; + break; + case SMB2_OPLOCK_LEVEL_BATCH: + hold_name = "BATCH Oplock"; + break; + default: + smb_panic(__location__); + break; + } + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport1->conn); + if (!(server_capabilities & SMB2_CAP_LEASING)) { + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE || + client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_skip(tctx, "leases are not supported"); + } + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + if (share_is_so) { + torture_skip(tctx, talloc_asprintf(tctx, + "%s not supported on SCALEOUT share", + hold_name)); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s\\%s_%s.dat", + BASEDIR, testname, generate_random_str(tctx, 8)); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + smb2_keepalive(transport1); + transport2->oplock.handler = torture_oplock_ack_handler; + transport2->oplock.private_data = tree2; + transport2->lease.handler = torture_lease_handler; + transport2->lease.private_data = tree2; + smb2_keepalive(transport2); + + smb2_util_unlink(tree1, fname); + status = torture_smb2_testdir(tree1, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key1 = random(); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io1, &ls1, false /* dir */, fname, + lease_key1, NULL, smb2_util_lease_state("RWH"), lease_epoch1++); + } else { + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + } + io1.in.share_access = smb2_util_share_access("RWD"); + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid1; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[0], lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[1], ~lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_epoch, lease_epoch1); + CHECK_VAL(io1.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + } else { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + } + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + + lease_key2 = random(); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io21, &ls2, false /* dir */, fname, + lease_key2, NULL, smb2_util_lease_state("RWH"), lease_epoch2++); + } else { + smb2_oplock_create(&io21, fname, client2_level); + } + io21.in.share_access = smb2_util_share_access("RWD"); + io21.in.durable_open = false; + io21.in.durable_open_v2 = true; + io21.in.persistent_open = false; + io21.in.create_guid = create_guid2; + io21.in.timeout = UINT32_MAX; + io24 = io23 = io22 = io21; + + req21 = smb2_create_send(tree2, &io21); + torture_assert(tctx, req21 != NULL, "req21"); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + const struct smb2_lease_break *lb = + &lease_break_info.lease_break; + const struct smb2_lease *l = &lb->current_lease; + const struct smb2_lease_key *k = &l->lease_key; + + torture_wait_for_lease_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 1); + + torture_assert(tctx, + lease_break_info.lease_transport == transport1, + "expect lease break on transport1\n"); + CHECK_VAL(k->data[0], lease_key1); + CHECK_VAL(k->data[1], ~lease_key1); + CHECK_VAL(lb->new_lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(lb->break_flags, + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + CHECK_VAL(lb->new_epoch, lease_epoch1+1); + lease_epoch1 += 1; + } else { + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(lease_break_info.count, 0); + + torture_assert(tctx, + break_info.received_transport == transport1, + "expect oplock break on transport1\n"); + CHECK_VAL(break_info.handle.data[0], _h1.data[0]); + CHECK_VAL(break_info.handle.data[1], _h1.data[1]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + } + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + WAIT_FOR_ASYNC_RESPONSE(tctx, req21); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 5; + status = smb2_create(tree2, tctx, &io22); + transport2->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2cli_session_start_replay(session2->smbXcli); + transport2->options.request_timeout = 5; + status = smb2_create(tree2, tctx, &io23); + transport2->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2_util_close(tree1, _h1); + h1 = NULL; + + status = smb2_create_recv(req21, tctx, &io21); + CHECK_STATUS(status, NT_STATUS_OK); + _h21 = io21.out.file.handle; + h21 = &_h21; + CHECK_CREATED(&io21, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io21.out.oplock_level, client2_level); + CHECK_VAL(io21.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io21.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else { + CHECK_VAL(io21.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2cli_session_start_replay(session2->smbXcli); + status = smb2_create(tree2, tctx, &io24); + smb2cli_session_stop_replay(session2->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + _h24 = io24.out.file.handle; + h24 = &_h24; + CHECK_CREATED(&io24, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(h24->data[0], h21->data[0]); + CHECK_VAL(h24->data[1], h21->data[1]); + CHECK_VAL(io24.out.oplock_level, client2_level); + CHECK_VAL(io24.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io24.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else { + CHECK_VAL(io24.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + status = smb2_util_close(tree2, *h24); + CHECK_STATUS(status, NT_STATUS_OK); + h24 = NULL; + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + +done: + + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_deltree(tree1, BASEDIR); + + TALLOC_FREE(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1n_vs_oplock_windows(). + */ +static bool test_dhv2_pending1n_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1n_vs_oplock_sane. + */ +static bool test_dhv2_pending1n_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1n_vs_lease_windows(). + */ +static bool test_dhv2_pending1n_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1n_vs_lease_sane. + */ +static bool test_dhv2_pending1n_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1l_vs_oplock_windows(). + */ +static bool test_dhv2_pending1l_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1l_vs_oplock_sane. + */ +static bool test_dhv2_pending1l_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1l_vs_lease_windows(). + */ +static bool test_dhv2_pending1l_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1l_vs_lease_sane. + */ +static bool test_dhv2_pending1l_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1o_vs_oplock_windows(). + */ +static bool test_dhv2_pending1o_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1o_vs_oplock_sane. + */ +static bool test_dhv2_pending1o_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending1o_vs_lease_windows(). + */ +static bool test_dhv2_pending1o_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open on a single + * channel. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending1o_vs_lease_sane. + */ +static bool test_dhv2_pending1o_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return _test_dhv2_pending1_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid and + * a share_access of READ/WRITE/DELETE: + * - client2_level = NONE: + * but without asking for an oplock nor a lease. + * - client2_level = BATCH: + * and asking for a batch oplock. + * - client2_level = LEASE + * and asking for an RWH lease. + * + * While another client holds a batch oplock or + * RWH lease. (client1_level => LEASE or BATCH). + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + */ +static bool _test_dhv2_pending2_vs_hold(struct torture_context *tctx, + const char *testname, + uint8_t client1_level, + uint8_t client2_level, + NTSTATUS reject_status, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h24; + struct smb2_handle *h24 = NULL; + struct smb2_create io1, io21, io22, io23, io24; + struct GUID create_guid1 = GUID_random(); + struct GUID create_guid2 = GUID_random(); + struct smb2_request *req21 = NULL; + bool ret = true; + char fname[256]; + struct smb2_transport *transport1 = tree1->session->transport; + uint32_t server_capabilities; + uint32_t share_capabilities; + struct smb2_lease ls1; + uint64_t lease_key1; + uint16_t lease_epoch1 = 0; + struct smb2_lease ls2; + uint64_t lease_key2; + uint16_t lease_epoch2 = 0; + bool share_is_so; + struct smb2_transport *transport2_1 = tree2_1->session->transport; + int request_timeout2 = transport2_1->options.request_timeout; + struct smbcli_options options2x; + struct smb2_tree *tree2_2 = NULL; + struct smb2_tree *tree2_3 = NULL; + struct smb2_tree *tree2_4 = NULL; + struct smb2_transport *transport2_2 = NULL; + struct smb2_transport *transport2_3 = NULL; + struct smb2_transport *transport2_4 = NULL; + struct smb2_session *session2_1 = tree2_1->session; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session2_3 = NULL; + struct smb2_session *session2_4 = NULL; + uint16_t csn2 = 1; + const char *hold_name = NULL; + + switch (client1_level) { + case SMB2_OPLOCK_LEVEL_LEASE: + hold_name = "RWH Lease"; + break; + case SMB2_OPLOCK_LEVEL_BATCH: + hold_name = "BATCH Oplock"; + break; + default: + smb_panic(__location__); + break; + } + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport1->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "MULTI_CHANNEL are not supported"); + } + if (!(server_capabilities & SMB2_CAP_LEASING)) { + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE || + client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_skip(tctx, "leases are not supported"); + } + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + if (share_is_so) { + torture_skip(tctx, talloc_asprintf(tctx, + "%s not supported on SCALEOUT share", + hold_name)); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s\\%s_%s.dat", + BASEDIR, testname, generate_random_str(tctx, 8)); + + options2x = transport2_1->options; + options2x.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_2, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_2 = tree2_2->session->transport; + + session2_2 = smb2_session_channel(transport2_2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_2->smbXcli = tree2_1->smbXcli; + tree2_2->session = session2_2; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_3, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_3 = tree2_3->session->transport; + + session2_3 = smb2_session_channel(transport2_3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_3->smbXcli = tree2_1->smbXcli; + tree2_3->session = session2_3; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_4, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_4 = tree2_4->session->transport; + + session2_4 = smb2_session_channel(transport2_4, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_4 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_4, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_4->smbXcli = tree2_1->smbXcli; + tree2_4->session = session2_4; + + smb2cli_session_reset_channel_sequence(session2_2->smbXcli, csn2++); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + smb2_keepalive(transport1); + transport2_1->oplock.handler = torture_oplock_ack_handler; + transport2_1->oplock.private_data = tree2_1; + transport2_1->lease.handler = torture_lease_handler; + transport2_1->lease.private_data = tree2_1; + smb2_keepalive(transport2_1); + transport2_2->oplock.handler = torture_oplock_ack_handler; + transport2_2->oplock.private_data = tree2_2; + transport2_2->lease.handler = torture_lease_handler; + transport2_2->lease.private_data = tree2_2; + smb2_keepalive(transport2_2); + transport2_3->oplock.handler = torture_oplock_ack_handler; + transport2_3->oplock.private_data = tree2_3; + transport2_3->lease.handler = torture_lease_handler; + transport2_3->lease.private_data = tree2_3; + smb2_keepalive(transport2_3); + transport2_4->oplock.handler = torture_oplock_ack_handler; + transport2_4->oplock.private_data = tree2_4; + transport2_4->lease.handler = torture_lease_handler; + transport2_4->lease.private_data = tree2_4; + smb2_keepalive(transport2_4); + + smb2_util_unlink(tree1, fname); + status = torture_smb2_testdir(tree1, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key1 = random(); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io1, &ls1, false /* dir */, fname, + lease_key1, NULL, smb2_util_lease_state("RWH"), lease_epoch1++); + } else { + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + } + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid1; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[0], lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[1], ~lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_epoch, lease_epoch1); + CHECK_VAL(io1.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + } else { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + } + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + + lease_key2 = random(); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io21, &ls2, false /* dir */, fname, + lease_key2, NULL, smb2_util_lease_state("RWH"), lease_epoch2++); + } else { + smb2_oplock_create(&io21, fname, client2_level); + } + io21.in.durable_open = false; + io21.in.durable_open_v2 = true; + io21.in.persistent_open = false; + io21.in.create_guid = create_guid2; + io21.in.timeout = UINT32_MAX; + io24 = io23 = io22 = io21; + + req21 = smb2_create_send(tree2_1, &io21); + torture_assert(tctx, req21 != NULL, "req21"); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + const struct smb2_lease_break *lb = + &lease_break_info.lease_break; + const struct smb2_lease *l = &lb->current_lease; + const struct smb2_lease_key *k = &l->lease_key; + + torture_wait_for_lease_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 1); + + torture_assert(tctx, + lease_break_info.lease_transport == transport1, + "expect lease break on transport1\n"); + CHECK_VAL(k->data[0], lease_key1); + CHECK_VAL(k->data[1], ~lease_key1); + CHECK_VAL(lb->new_lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(lb->break_flags, + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + CHECK_VAL(lb->new_epoch, lease_epoch1+1); + lease_epoch1 += 1; + } else { + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(lease_break_info.count, 0); + + torture_assert(tctx, + break_info.received_transport == transport1, + "expect oplock break on transport1\n"); + CHECK_VAL(break_info.handle.data[0], _h1.data[0]); + CHECK_VAL(break_info.handle.data[1], _h1.data[1]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + } + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + WAIT_FOR_ASYNC_RESPONSE(tctx, req21); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smbXcli_conn_disconnect(transport2_1->conn, NT_STATUS_LOCAL_DISCONNECT); + smb2cli_session_reset_channel_sequence(session2_1->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_2->smbXcli); + transport2_2->options.request_timeout = 5; + status = smb2_create(tree2_2, tctx, &io22); + transport2_2->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2_2->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smbXcli_conn_disconnect(transport2_2->conn, NT_STATUS_LOCAL_DISCONNECT); + smb2cli_session_reset_channel_sequence(session2_2->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_3->smbXcli); + transport2_3->options.request_timeout = 5; + status = smb2_create(tree2_3, tctx, &io23); + transport2_3->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2_3->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2_util_close(tree1, _h1); + h1 = NULL; + + status = smb2_create_recv(req21, tctx, &io21); + CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smbXcli_conn_disconnect(transport2_3->conn, NT_STATUS_LOCAL_DISCONNECT); + smb2cli_session_reset_channel_sequence(session2_3->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_4->smbXcli); + status = smb2_create(tree2_4, tctx, &io24); + smb2cli_session_stop_replay(session2_4->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + _h24 = io24.out.file.handle; + h24 = &_h24; + CHECK_CREATED(&io24, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io24.out.oplock_level, client2_level); + CHECK_VAL(io24.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io24.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else { + CHECK_VAL(io24.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + status = smb2_util_close(tree2_4, *h24); + CHECK_STATUS(status, NT_STATUS_OK); + h24 = NULL; + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + +done: + + smbXcli_conn_disconnect(transport2_1->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_2->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_3->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_4->conn, NT_STATUS_LOCAL_DISCONNECT); + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_deltree(tree1, BASEDIR); + + TALLOC_FREE(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2n_vs_lease_windows(). + */ +static bool test_dhv2_pending2n_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2n_vs_lease_sane(). + */ +static bool test_dhv2_pending2n_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2n_vs_oplock_windows(). + */ +static bool test_dhv2_pending2n_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2n_vs_oplock_sane(). + */ +static bool test_dhv2_pending2n_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2l_vs_oplock_windows(). + */ +static bool test_dhv2_pending2l_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2l_vs_oplock_sane(). + */ +static bool test_dhv2_pending2l_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2l_vs_oplock_windows(). + */ +static bool test_dhv2_pending2l_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2l_vs_oplock_sane(). + */ +static bool test_dhv2_pending2l_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2o_vs_oplock_windows(). + */ +static bool test_dhv2_pending2o_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2o_vs_oplock_sane(). + */ +static bool test_dhv2_pending2o_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending2o_vs_lease_windows(). + */ +static bool test_dhv2_pending2o_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and closed transports on the client and server side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending2o_vs_lease_sane(). + */ +static bool test_dhv2_pending2o_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending2_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid and + * a share_access of READ/WRITE/DELETE: + * - client2_level = NONE: + * but without asking for an oplock nor a lease. + * - client2_level = BATCH: + * and asking for a batch oplock. + * - client2_level = LEASE + * and asking for an RWH lease. + * + * While another client holds a batch oplock or + * RWH lease. (client1_level => LEASE or BATCH). + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + */ +static bool _test_dhv2_pending3_vs_hold(struct torture_context *tctx, + const char *testname, + uint8_t client1_level, + uint8_t client2_level, + NTSTATUS reject_status, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h21; + struct smb2_handle *h21 = NULL; + struct smb2_handle _h24; + struct smb2_handle *h24 = NULL; + struct smb2_create io1, io21, io22, io23, io24; + struct GUID create_guid1 = GUID_random(); + struct GUID create_guid2 = GUID_random(); + struct smb2_request *req21 = NULL; + bool ret = true; + char fname[256]; + struct smb2_transport *transport1 = tree1->session->transport; + uint32_t server_capabilities; + uint32_t share_capabilities; + struct smb2_lease ls1; + uint64_t lease_key1; + uint16_t lease_epoch1 = 0; + struct smb2_lease ls2; + uint64_t lease_key2; + uint16_t lease_epoch2 = 0; + bool share_is_so; + struct smb2_transport *transport2_1 = tree2_1->session->transport; + int request_timeout2 = transport2_1->options.request_timeout; + struct smbcli_options options2x; + struct smb2_tree *tree2_2 = NULL; + struct smb2_tree *tree2_3 = NULL; + struct smb2_tree *tree2_4 = NULL; + struct smb2_transport *transport2_2 = NULL; + struct smb2_transport *transport2_3 = NULL; + struct smb2_transport *transport2_4 = NULL; + struct smb2_session *session2_1 = tree2_1->session; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session2_3 = NULL; + struct smb2_session *session2_4 = NULL; + bool block_setup = false; + bool blocked2_1 = false; + bool blocked2_2 = false; + bool blocked2_3 = false; + uint16_t csn2 = 1; + const char *hold_name = NULL; + + switch (client1_level) { + case SMB2_OPLOCK_LEVEL_LEASE: + hold_name = "RWH Lease"; + break; + case SMB2_OPLOCK_LEVEL_BATCH: + hold_name = "BATCH Oplock"; + break; + default: + smb_panic(__location__); + break; + } + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities(transport1->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "MULTI_CHANNEL are not supported"); + } + if (!(server_capabilities & SMB2_CAP_LEASING)) { + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE || + client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_skip(tctx, "leases are not supported"); + } + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + if (share_is_so) { + torture_skip(tctx, talloc_asprintf(tctx, + "%s not supported on SCALEOUT share", + hold_name)); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s\\%s_%s.dat", + BASEDIR, testname, generate_random_str(tctx, 8)); + + options2x = transport2_1->options; + options2x.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_2, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_2 = tree2_2->session->transport; + + session2_2 = smb2_session_channel(transport2_2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_2->smbXcli = tree2_1->smbXcli; + tree2_2->session = session2_2; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_3, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_3 = tree2_3->session->transport; + + session2_3 = smb2_session_channel(transport2_3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_3->smbXcli = tree2_1->smbXcli; + tree2_3->session = session2_3; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_4, + tctx->ev, + &options2x, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2_4 = tree2_4->session->transport; + + session2_4 = smb2_session_channel(transport2_4, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx, + session2_1); + torture_assert(tctx, session2_4 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_4, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2_4->smbXcli = tree2_1->smbXcli; + tree2_4->session = session2_4; + + smb2cli_session_reset_channel_sequence(session2_2->smbXcli, csn2++); + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + smb2_keepalive(transport1); + transport2_1->oplock.handler = torture_oplock_ack_handler; + transport2_1->oplock.private_data = tree2_1; + transport2_1->lease.handler = torture_lease_handler; + transport2_1->lease.private_data = tree2_1; + smb2_keepalive(transport2_1); + transport2_2->oplock.handler = torture_oplock_ack_handler; + transport2_2->oplock.private_data = tree2_2; + transport2_2->lease.handler = torture_lease_handler; + transport2_2->lease.private_data = tree2_2; + smb2_keepalive(transport2_2); + transport2_3->oplock.handler = torture_oplock_ack_handler; + transport2_3->oplock.private_data = tree2_3; + transport2_3->lease.handler = torture_lease_handler; + transport2_3->lease.private_data = tree2_3; + smb2_keepalive(transport2_3); + transport2_4->oplock.handler = torture_oplock_ack_handler; + transport2_4->oplock.private_data = tree2_4; + transport2_4->lease.handler = torture_lease_handler; + transport2_4->lease.private_data = tree2_4; + smb2_keepalive(transport2_4); + + smb2_util_unlink(tree1, fname); + status = torture_smb2_testdir(tree1, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h1); + CHECK_VAL(break_info.count, 0); + + lease_key1 = random(); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io1, &ls1, false /* dir */, fname, + lease_key1, NULL, smb2_util_lease_state("RWH"), lease_epoch1++); + } else { + smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH); + } + io1.in.durable_open = false; + io1.in.durable_open_v2 = true; + io1.in.persistent_open = false; + io1.in.create_guid = create_guid1; + io1.in.timeout = UINT32_MAX; + + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io1.out.durable_open, false); + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[0], lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_key.data[1], ~lease_key1); + CHECK_VAL(io1.out.lease_response_v2.lease_epoch, lease_epoch1); + CHECK_VAL(io1.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + } else { + CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + } + CHECK_VAL(io1.out.durable_open_v2, true); + CHECK_VAL(io1.out.timeout, 300*1000); + + lease_key2 = random(); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + smb2_lease_v2_create(&io21, &ls2, false /* dir */, fname, + lease_key2, NULL, smb2_util_lease_state("RWH"), lease_epoch2++); + } else { + smb2_oplock_create(&io21, fname, client2_level); + } + io21.in.durable_open = false; + io21.in.durable_open_v2 = true; + io21.in.persistent_open = false; + io21.in.create_guid = create_guid2; + io21.in.timeout = UINT32_MAX; + io24 = io23 = io22 = io21; + + req21 = smb2_create_send(tree2_1, &io21); + torture_assert(tctx, req21 != NULL, "req21"); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + const struct smb2_lease_break *lb = + &lease_break_info.lease_break; + const struct smb2_lease *l = &lb->current_lease; + const struct smb2_lease_key *k = &l->lease_key; + + torture_wait_for_lease_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 1); + + torture_assert(tctx, + lease_break_info.lease_transport == transport1, + "expect lease break on transport1\n"); + CHECK_VAL(k->data[0], lease_key1); + CHECK_VAL(k->data[1], ~lease_key1); + CHECK_VAL(lb->new_lease_state, + smb2_util_lease_state("RH")); + CHECK_VAL(lb->break_flags, + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + CHECK_VAL(lb->new_epoch, lease_epoch1+1); + lease_epoch1 += 1; + } else { + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(lease_break_info.count, 0); + + torture_assert(tctx, + break_info.received_transport == transport1, + "expect oplock break on transport1\n"); + CHECK_VAL(break_info.handle.data[0], _h1.data[0]); + CHECK_VAL(break_info.handle.data[1], _h1.data[1]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + } + + torture_reset_break_info(tctx, &break_info); + break_info.oplock_skip_ack = true; + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + + WAIT_FOR_ASYNC_RESPONSE(tctx, req21); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + block_setup = test_setup_blocked_transports(tctx); + torture_assert(tctx, block_setup, "test_setup_blocked_transports"); + + blocked2_1 = _test_block_smb2_transport(tctx, transport2_1, "transport2_1"); + torture_assert_goto(tctx, blocked2_1, ret, done, "we could not block tcp transport"); + smb2cli_session_reset_channel_sequence(session2_1->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_2->smbXcli); + transport2_2->options.request_timeout = 5; + status = smb2_create(tree2_2, tctx, &io22); + transport2_2->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2_2->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + blocked2_2 = _test_block_smb2_transport(tctx, transport2_2, "transport2_2"); + torture_assert_goto(tctx, blocked2_2, ret, done, "we could not block tcp transport"); + smb2cli_session_reset_channel_sequence(session2_2->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_3->smbXcli); + transport2_3->options.request_timeout = 5; + status = smb2_create(tree2_3, tctx, &io23); + transport2_3->options.request_timeout = request_timeout2; + CHECK_STATUS(status, reject_status); + smb2cli_session_stop_replay(session2_3->smbXcli); + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + smb2_util_close(tree1, _h1); + h1 = NULL; + + status = smb2_create_recv(req21, tctx, &io21); + CHECK_STATUS(status, NT_STATUS_OK); + _h21 = io21.out.file.handle; + h21 = &_h21; + CHECK_CREATED(&io21, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io21.out.oplock_level, client2_level); + CHECK_VAL(io21.out.durable_open, false); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io21.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io21.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io21.out.durable_open_v2, true); + CHECK_VAL(io21.out.timeout, 300*1000); + } else { + CHECK_VAL(io21.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + + blocked2_3 = _test_block_smb2_transport(tctx, transport2_3, "transport2_3"); + torture_assert_goto(tctx, blocked2_3, ret, done, "we could not block tcp transport"); + smb2cli_session_reset_channel_sequence(session2_3->smbXcli, csn2++); + + smb2cli_session_start_replay(session2_4->smbXcli); + status = smb2_create(tree2_4, tctx, &io24); + smb2cli_session_stop_replay(session2_4->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + _h24 = io24.out.file.handle; + h24 = &_h24; + CHECK_CREATED(&io24, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(h24->data[0], h21->data[0]); + CHECK_VAL(h24->data[1], h21->data[1]); + if (client2_level == SMB2_OPLOCK_LEVEL_LEASE) { + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[0], lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_key.data[1], ~lease_key2); + CHECK_VAL(io24.out.lease_response_v2.lease_epoch, lease_epoch2); + CHECK_VAL(io24.out.lease_response_v2.lease_state, + smb2_util_lease_state("RHW")); + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else if (client2_level == SMB2_OPLOCK_LEVEL_BATCH) { + CHECK_VAL(io24.out.durable_open_v2, true); + CHECK_VAL(io24.out.timeout, 300*1000); + } else { + CHECK_VAL(io24.out.durable_open_v2, false); + } + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + status = smb2_util_close(tree2_4, *h24); + CHECK_STATUS(status, NT_STATUS_OK); + h24 = NULL; + + if (client1_level == SMB2_OPLOCK_LEVEL_LEASE) { + torture_wait_for_lease_break(tctx); + } else { + torture_wait_for_oplock_break(tctx); + } + CHECK_VAL(break_info.count, 0); + CHECK_VAL(lease_break_info.count, 0); + +done: + + if (blocked2_3) { + _test_unblock_smb2_transport(tctx, transport2_3, "transport2_3"); + } + if (blocked2_2) { + _test_unblock_smb2_transport(tctx, transport2_2, "transport2_2"); + } + if (blocked2_1) { + _test_unblock_smb2_transport(tctx, transport2_1, "transport2_1"); + } + if (block_setup) { + test_cleanup_blocked_transports(tctx); + } + + smbXcli_conn_disconnect(transport2_1->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_2->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_3->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2_4->conn, NT_STATUS_LOCAL_DISCONNECT); + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_deltree(tree1, BASEDIR); + + TALLOC_FREE(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3n_vs_lease_windows(). + */ +static bool test_dhv2_pending3n_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3n_vs_lease_sane. + */ +static bool test_dhv2_pending3n_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3n_vs_oplock_windows(). + */ +static bool test_dhv2_pending3n_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * but without asking for an oplock nor a lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3n_vs_oplock_sane. + */ +static bool test_dhv2_pending3n_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_NONE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3l_vs_oplock_windows(). + */ +static bool test_dhv2_pending3l_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3l_vs_oplock_sane. + */ +static bool test_dhv2_pending3l_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3l_vs_lease_windows(). + */ +static bool test_dhv2_pending3l_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a v2 lease. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3l_vs_lease_sane(). + */ +static bool test_dhv2_pending3l_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_LEASE, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3o_vs_oplock_windows(). + */ +static bool test_dhv2_pending3o_vs_oplock_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds a batch oplock. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3o_vs_oplock_sane(). + */ +static bool test_dhv2_pending3o_vs_oplock_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_BATCH, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the sane reject status of + * NT_STATUS_FILE_NOT_AVAILABLE. + * + * It won't pass against Windows as it returns + * NT_STATUS_ACCESS_DENIED see + * test_dhv2_pending3o_vs_lease_windows(). + */ +static bool test_dhv2_pending3o_vs_lease_sane(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_FILE_NOT_AVAILABLE, + tree1, tree2_1); +} + +/** + * This tests replay with a pending open with 4 channels + * and blocked transports on the client side. + * + * With a durablev2 request containing a create_guid, + * a share_access of READ/WRITE/DELETE, + * and asking for a batch oplock. + * + * While another client holds an RWH lease. + * And allows share_access of READ/WRITE/DELETE. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14449 + * + * This expects the strange reject status of + * NT_STATUS_ACCESS_DENIED, which is returned + * by Windows Servers. + * + * It won't pass against Samba as it returns + * NT_STATUS_FILE_NOT_AVAILABLE. see + * test_dhv2_pending3o_vs_lease_sane(). + */ +static bool test_dhv2_pending3o_vs_lease_windows(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2_1) +{ + return _test_dhv2_pending3_vs_hold(tctx, __func__, + SMB2_OPLOCK_LEVEL_LEASE, + SMB2_OPLOCK_LEVEL_BATCH, + NT_STATUS_ACCESS_DENIED, + tree1, tree2_1); +} + +static bool test_channel_sequence_table(struct torture_context *tctx, + struct smb2_tree *tree, + bool do_replay, + uint16_t opcode) +{ + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle handle; + struct smb2_handle *phandle = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\channel_sequence.dat"; + uint16_t csn = 0; + uint16_t limit = UINT16_MAX - 0x7fff; + int i; + struct { + uint16_t csn; + bool csn_rand_low; + bool csn_rand_high; + NTSTATUS expected_status; + } tests[] = { + { + .csn = 0, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0x7fff + 1, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 0x7fff + 2, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = -1, + .csn_rand_high = true, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 0xffff, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 0x7fff, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0x7ffe, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 0, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = -1, + .csn_rand_low = true, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 0x7fff + 1, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0xffff, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0, + .expected_status = NT_STATUS_OK, + },{ + .csn = 1, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + },{ + .csn = 1, + .expected_status = NT_STATUS_OK, + },{ + .csn = 0xffff, + .expected_status = NT_STATUS_FILE_NOT_AVAILABLE, + } + }; + + smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0); + + csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli); + torture_comment(tctx, "Testing create with channel sequence number: 0x%04x\n", csn); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + torture_assert_ntstatus_ok_goto(tctx, + smb2_create(tree, mem_ctx, &io), + ret, done, "failed to call smb2_create"); + + handle = io.out.file.handle; + phandle = &handle; + + for (i=0; i session->smbXcli, csn); + csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli); + + torture_comment(tctx, "Testing %s (replay: %s) with CSN 0x%04x, expecting: %s\n", + opstr, do_replay ? "true" : "false", csn, + nt_errstr(tests[i].expected_status)); + + if (do_replay) { + smb2cli_session_start_replay(tree->session->smbXcli); + } + + switch (opcode) { + case SMB2_OP_WRITE: { + DATA_BLOB blob = data_blob_talloc(tctx, NULL, 255); + + generate_random_buffer(blob.data, blob.length); + + status = smb2_util_write(tree, handle, blob.data, 0, blob.length); + if (NT_STATUS_IS_OK(status)) { + struct smb2_read rd; + + rd = (struct smb2_read) { + .in.file.handle = handle, + .in.length = blob.length, + .in.offset = 0 + }; + + torture_assert_ntstatus_ok_goto(tctx, + smb2_read(tree, tree, &rd), + ret, done, "failed to read after write"); + + torture_assert_data_blob_equal(tctx, + rd.out.data, blob, + "read/write mismatch"); + } + break; + } + case SMB2_OP_IOCTL: { + union smb_ioctl ioctl; + ioctl = (union smb_ioctl) { + .smb2.level = RAW_IOCTL_SMB2, + .smb2.in.file.handle = handle, + .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID, + .smb2.in.max_output_response = 64, + .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL + }; + status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2); + break; + } + case SMB2_OP_SETINFO: { + union smb_setfileinfo sfinfo; + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.generic.in.file.handle = handle; + sfinfo.position_information.in.position = 0x1000; + status = smb2_setinfo_file(tree, &sfinfo); + break; + } + default: + break; + } + + qfinfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_POSITION_INFORMATION, + .generic.in.file.handle = handle + }; + + torture_assert_ntstatus_ok_goto(tctx, + smb2_getinfo_file(tree, mem_ctx, &qfinfo), + ret, done, "failed to read after write"); + + if (do_replay) { + smb2cli_session_stop_replay(tree->session->smbXcli); + } + + torture_assert_ntstatus_equal_goto(tctx, + status, tests[i].expected_status, + ret, done, "got unexpected failure code"); + + } +done: + if (phandle != NULL) { + smb2_util_close(tree, *phandle); + } + + smb2_util_unlink(tree, fname); + + return ret; +} + +static bool test_channel_sequence(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + bool ret = true; + const char *fname = BASEDIR "\\channel_sequence.dat"; + struct smb2_transport *transport1 = tree->session->transport; + struct smb2_handle handle; + uint16_t opcodes[] = { SMB2_OP_WRITE, SMB2_OP_IOCTL, SMB2_OP_SETINFO }; + int i; + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "Replay tests\n"); + } + + torture_comment(tctx, "Testing channel sequence numbers\n"); + + smbXcli_conn_set_force_channel_sequence(transport1->conn, true); + + torture_assert_ntstatus_ok_goto(tctx, + torture_smb2_testdir(tree, BASEDIR, &handle), + ret, done, "failed to setup test directory"); + + smb2_util_close(tree, handle); + smb2_util_unlink(tree, fname); + + for (i=0; i session->transport; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "Replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, + "Server does not support multi-channel."); + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + + torture_comment(tctx, "Replay of DurableHandleReqV2 on Multi " + "Channel\n"); + status = torture_smb2_testdir(tree1, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h); + smb2_util_unlink(tree1, fname); + CHECK_VAL(break_info.count, 0); + + /* + * use the 1st channel, 1st session + */ + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(break_info.count, 0); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree2, + tctx->ev, + &transport1->options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2 = tree2->session->transport; + + transport2->oplock.handler = torture_oplock_ack_handler; + transport2->oplock.private_data = tree2; + + /* + * Now bind the 1st session to 2nd transport channel + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * use the 2nd channel, 1st session + */ + tree1->session = session1_2; + smb2cli_session_start_replay(tree1->session->smbXcli); + status = smb2_create(tree1, mem_ctx, &io); + smb2cli_session_stop_replay(tree1->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(break_info.count, 0); + + tree1->session = session1_1; + smb2_util_close(tree1, *h); + h = NULL; + +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h != NULL) { + smb2_util_close(tree1, *h); + } + + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number + */ +static bool test_replay4(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + uint8_t buf[64]; + struct smb2_read rd; + union smb_setfileinfo sfinfo; + bool ret = true; + const char *fname = BASEDIR "\\replay4.dat"; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + uint16_t curr_cs; + uint32_t share_capabilities; + bool share_is_so; + uint32_t server_capabilities; + + if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "Replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, + "Server does not support multi-channel."); + } + + share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli); + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + + torture_reset_break_info(tctx, &break_info); + transport1->oplock.handler = torture_oplock_ack_handler; + transport1->oplock.private_data = tree1; + + torture_comment(tctx, "IO Ordering for Multi Channel\n"); + status = torture_smb2_testdir(tree1, BASEDIR, &_h1); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree1, _h1); + smb2_util_unlink(tree1, fname); + CHECK_VAL(break_info.count, 0); + + /* + * use the 1st channel, 1st session + */ + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = io.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + if (share_is_so) { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); + CHECK_VAL(io.out.durable_open_v2, false); + CHECK_VAL(io.out.timeout, 0); + } else { + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.timeout, 300*1000); + } + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(break_info.count, 0); + + status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Increment ChannelSequence so that server thinks that there's a + * Channel Failure + */ + smb2cli_session_increment_channel_sequence(tree1->session->smbXcli); + + /* + * Perform a Read with incremented ChannelSequence + */ + rd = (struct smb2_read) { + .in.file.handle = *h1, + .in.length = sizeof(buf), + .in.offset = 0 + }; + status = smb2_read(tree1, tree1, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Performing a Write with Stale ChannelSequence is not allowed by + * server + */ + curr_cs = smb2cli_session_reset_channel_sequence( + tree1->session->smbXcli, 0); + status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE); + + /* + * Performing a Write Replay with Stale ChannelSequence is not allowed + * by server + */ + smb2cli_session_start_replay(tree1->session->smbXcli); + smb2cli_session_reset_channel_sequence(tree1->session->smbXcli, 0); + status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf)); + smb2cli_session_stop_replay(tree1->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE); + + /* + * Performing a SetInfo with stale ChannelSequence is not allowed by + * server + */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; + sfinfo.generic.in.file.handle = *h1; + sfinfo.position_information.in.position = 0x1000; + status = smb2_setinfo_file(tree1, &sfinfo); + CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE); + + /* + * Performing a Read with stale ChannelSequence is allowed + */ + rd = (struct smb2_read) { + .in.file.handle = *h1, + .in.length = ARRAY_SIZE(buf), + .in.offset = 0 + }; + status = smb2_read(tree1, tree1, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree2, + tctx->ev, + &transport1->options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport2 = tree2->session->transport; + + transport2->oplock.handler = torture_oplock_ack_handler; + transport2->oplock.private_data = tree2; + + /* + * Now bind the 1st session to 2nd transport channel + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * use the 2nd channel, 1st session + */ + tree1->session = session1_2; + + /* + * Write Replay with Correct ChannelSequence is allowed by the server + */ + smb2cli_session_start_replay(tree1->session->smbXcli); + smb2cli_session_reset_channel_sequence(tree1->session->smbXcli, + curr_cs); + status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2cli_session_stop_replay(tree1->session->smbXcli); + + /* + * See what happens if we change the Buffer and perform a Write Replay. + * This is to show that Write Replay does not really care about the data + */ + memset(buf, 'r', ARRAY_SIZE(buf)); + smb2cli_session_start_replay(tree1->session->smbXcli); + status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2cli_session_stop_replay(tree1->session->smbXcli); + + /* + * Read back from File to verify what was written + */ + rd = (struct smb2_read) { + .in.file.handle = *h1, + .in.length = ARRAY_SIZE(buf), + .in.offset = 0 + }; + status = smb2_read(tree1, tree1, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + if ((rd.out.data.length != ARRAY_SIZE(buf)) || + memcmp(rd.out.data.data, buf, ARRAY_SIZE(buf))) { + torture_comment(tctx, "Write Replay Data Mismatch\n"); + } + + tree1->session = session1_1; + smb2_util_close(tree1, *h1); + h1 = NULL; + + if (share_is_so) { + CHECK_VAL(break_info.count, 1); + } else { + CHECK_VAL(break_info.count, 0); + } +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + + talloc_free(tree1); + talloc_free(mem_ctx); + + return ret; +} + +/** + * Test Durability V2 Persistent Create Replay on a Single Channel + */ +static bool test_replay5(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io; + struct GUID create_guid = GUID_random(); + bool ret = true; + uint32_t share_capabilities; + bool share_is_ca; + bool share_is_so; + uint32_t server_capabilities; + const char *fname = BASEDIR "\\replay5.dat"; + struct smb2_transport *transport = tree->session->transport; + struct smbcli_options options = tree->session->transport->options; + uint8_t expect_oplock = smb2_util_oplock_level("b"); + NTSTATUS expect_status = NT_STATUS_DUPLICATE_OBJECTID; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "Replay tests\n"); + } + + server_capabilities = smb2cli_conn_server_capabilities( + tree->session->transport->conn); + if (!(server_capabilities & SMB2_CAP_PERSISTENT_HANDLES)) { + torture_skip(tctx, + "Server does not support persistent handles."); + } + + share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli); + + share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; + if (!share_is_ca) { + torture_skip(tctx, "Share is not continuously available."); + } + + share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT; + if (share_is_so) { + expect_oplock = smb2_util_oplock_level("s"); + expect_status = NT_STATUS_FILE_NOT_AVAILABLE; + } + + torture_reset_break_info(tctx, &break_info); + transport->oplock.handler = torture_oplock_ack_handler; + transport->oplock.private_data = tree; + + torture_comment(tctx, "Replay of Persistent DurableHandleReqV2 on Single " + "Channel\n"); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + smb2_util_unlink(tree, fname); + CHECK_VAL(break_info.count, 0); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = true; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, expect_oplock); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + CHECK_VAL(io.out.persistent_open, true); + CHECK_VAL(io.out.timeout, 300*1000); + CHECK_VAL(break_info.count, 0); + + /* disconnect, leaving the durable open */ + TALLOC_FREE(tree); + + if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + /* a re-open of a persistent handle causes an error */ + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, expect_status); + + /* SMB2_FLAGS_REPLAY_OPERATION must be set to open the Persistent Handle */ + smb2cli_session_start_replay(tree->session->smbXcli); + smb2cli_session_increment_channel_sequence(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.persistent_open, true); + CHECK_VAL(io.out.oplock_level, expect_oplock); + _h = io.out.file.handle; + h = &_h; + + smb2_util_close(tree, *h); + h = NULL; +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + + +/** + * Test Error Codes when a DurableHandleReqV2 with matching CreateGuid is + * re-sent with or without SMB2_FLAGS_REPLAY_OPERATION + */ +static bool test_replay6(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io, ref1; + union smb_fileinfo qfinfo; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay6.dat"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Error Codes for DurableHandleReqV2 Replay\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io; + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io, &ref1); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + qfinfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_POSITION_INFORMATION, + .generic.in.file.handle = *h + }; + torture_comment(tctx, "Trying getinfo\n"); + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0); + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[0], + ref1.out.file.handle.data[0], + ret, done, "data 0"); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[1], + ref1.out.file.handle.data[1], + ret, done, "data 1"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, smb2_util_oplock_level("s")); + torture_reset_break_info(tctx, &break_info); + + /* + * Resend the matching Durable V2 Create without + * SMB2_FLAGS_REPLAY_OPERATION. This triggers an oplock break and still + * gets NT_STATUS_DUPLICATE_OBJECTID + */ + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_DUPLICATE_OBJECTID); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + /* + * According to MS-SMB2 3.3.5.9.10 if Durable V2 Create is replayed and + * FileAttributes or CreateDisposition do not match the earlier Create + * request the Server fails request with + * NT_STATUS_INVALID_PARAMETER. But through this test we see that server + * does not really care about changed FileAttributes or + * CreateDisposition. + */ + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[0], + ref1.out.file.handle.data[0], + ret, done, "data 0"); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[1], + ref1.out.file.handle.data[1], + ret, done, "data 1"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_replay7(struct torture_context *tctx, struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_transport *transport = tree->session->transport; + NTSTATUS status; + struct smb2_handle _dh; + struct smb2_handle *dh = NULL; + struct smb2_notify notify; + struct smb2_request *req; + union smb_fileinfo qfinfo; + bool ret = false; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + torture_comment(tctx, "Notify across increment/decrement of csn\n"); + + smbXcli_conn_set_force_channel_sequence(transport->conn, true); + + status = torture_smb2_testdir(tree, BASEDIR, &_dh); + CHECK_STATUS(status, NT_STATUS_OK); + dh = &_dh; + + notify.in.recursive = 0x0000; + notify.in.buffer_size = 0xffff; + notify.in.file.handle = _dh; + notify.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME; + notify.in.unknown = 0x00000000; + + /* + * This posts a long-running request with csn==0 to "dh". Now + * op->request_count==1 in smb2_server.c. + */ + smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0); + req = smb2_notify_send(tree, ¬ify); + + qfinfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_POSITION_INFORMATION, + .generic.in.file.handle = _dh + }; + + /* + * This sequence of 2 dummy requests moves + * op->request_count==1 to op->pre_request_count. The numbers + * used avoid int16 overflow. + */ + + smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 30000); + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 60000); + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * This final request turns the op->global->channel_sequence + * to the same as we had when sending the notify above. The + * notify's request count has in the meantime moved to + * op->pre_request_count. + */ + + smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0); + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * At this point op->request_count==0. + * + * The next cancel makes us reply to the notify. Because the + * csn we currently use is the same as we used when sending + * the notify, smbd thinks it must decrement op->request_count + * and not op->pre_request_count. + */ + + status = smb2_cancel(req); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + + ret = true; + +done: + if (dh != NULL) { + smb2_util_close(tree, _dh); + } + smb2_deltree(tree, BASEDIR); + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + +struct torture_suite *torture_smb2_replay_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "replay"); + + torture_suite_add_1smb2_test(suite, "replay-commands", test_replay_commands); + torture_suite_add_1smb2_test(suite, "replay-regular", test_replay_regular); + torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock1", test_replay_dhv2_oplock1); + torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock2", test_replay_dhv2_oplock2); + torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock3", test_replay_dhv2_oplock3); + torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock-lease", test_replay_dhv2_oplock_lease); + torture_suite_add_1smb2_test(suite, "replay-dhv2-lease1", test_replay_dhv2_lease1); + torture_suite_add_1smb2_test(suite, "replay-dhv2-lease2", test_replay_dhv2_lease2); + torture_suite_add_1smb2_test(suite, "replay-dhv2-lease3", test_replay_dhv2_lease3); + torture_suite_add_1smb2_test(suite, "replay-dhv2-lease-oplock", test_replay_dhv2_lease_oplock); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-violation-lease-close-sane", test_dhv2_pending1n_vs_violation_lease_close_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-violation-lease-ack-sane", test_dhv2_pending1n_vs_violation_lease_ack_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-violation-lease-close-windows", test_dhv2_pending1n_vs_violation_lease_close_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-violation-lease-ack-windows", test_dhv2_pending1n_vs_violation_lease_ack_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-oplock-sane", test_dhv2_pending1n_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-oplock-windows", test_dhv2_pending1n_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-lease-sane", test_dhv2_pending1n_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1n-vs-lease-windows", test_dhv2_pending1n_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1l-vs-oplock-sane", test_dhv2_pending1l_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1l-vs-oplock-windows", test_dhv2_pending1l_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1l-vs-lease-sane", test_dhv2_pending1l_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1l-vs-lease-windows", test_dhv2_pending1l_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1o-vs-oplock-sane", test_dhv2_pending1o_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1o-vs-oplock-windows", test_dhv2_pending1o_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending1o-vs-lease-sane", test_dhv2_pending1o_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending1o-vs-lease-windows", test_dhv2_pending1o_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2n-vs-oplock-sane", test_dhv2_pending2n_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2n-vs-oplock-windows", test_dhv2_pending2n_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2n-vs-lease-sane", test_dhv2_pending2n_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2n-vs-lease-windows", test_dhv2_pending2n_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2l-vs-oplock-sane", test_dhv2_pending2l_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2l-vs-oplock-windows", test_dhv2_pending2l_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2l-vs-lease-sane", test_dhv2_pending2l_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2l-vs-lease-windows", test_dhv2_pending2l_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2o-vs-oplock-sane", test_dhv2_pending2o_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2o-vs-oplock-windows", test_dhv2_pending2o_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending2o-vs-lease-sane", test_dhv2_pending2o_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending2o-vs-lease-windows", test_dhv2_pending2o_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3n-vs-oplock-sane", test_dhv2_pending3n_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3n-vs-oplock-windows", test_dhv2_pending3n_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3n-vs-lease-sane", test_dhv2_pending3n_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3n-vs-lease-windows", test_dhv2_pending3n_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3l-vs-oplock-sane", test_dhv2_pending3l_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3l-vs-oplock-windows", test_dhv2_pending3l_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3l-vs-lease-sane", test_dhv2_pending3l_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3l-vs-lease-windows", test_dhv2_pending3l_vs_lease_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3o-vs-oplock-sane", test_dhv2_pending3o_vs_oplock_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3o-vs-oplock-windows", test_dhv2_pending3o_vs_oplock_windows); + torture_suite_add_2smb2_test(suite, "dhv2-pending3o-vs-lease-sane", test_dhv2_pending3o_vs_lease_sane); + torture_suite_add_2smb2_test(suite, "dhv2-pending3o-vs-lease-windows", test_dhv2_pending3o_vs_lease_windows); + torture_suite_add_1smb2_test(suite, "channel-sequence", test_channel_sequence); + torture_suite_add_1smb2_test(suite, "replay3", test_replay3); + torture_suite_add_1smb2_test(suite, "replay4", test_replay4); + torture_suite_add_1smb2_test(suite, "replay5", test_replay5); + torture_suite_add_1smb2_test(suite, "replay6", test_replay6); + torture_suite_add_1smb2_test(suite, "replay7", test_replay7); + + suite->description = talloc_strdup(suite, "SMB2 REPLAY tests"); + + return suite; +} diff --git a/source4/torture/smb2/samba3misc.c b/source4/torture/smb2/samba3misc.c new file mode 100644 index 0000000..6696c06 --- /dev/null +++ b/source4/torture/smb2/samba3misc.c @@ -0,0 +1,189 @@ +/* + Unix SMB/CIFS implementation. + + Test some misc Samba3 code paths + + Copyright (C) Volker Lendecke 2006 + Copyright (C) Stefan Metzmacher 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" +#include "lib/events/events.h" +#include "param/param.h" + +#define CHECK_STATUS(status, correct) do { \ + const char *_cmt = "(" __location__ ")"; \ + torture_assert_ntstatus_equal_goto(tctx,status,correct, \ + ret,done,_cmt); \ + } while (0) + +#define BASEDIR "samba3misc.smb2" + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(tctx->ev) != 0) { \ + break; \ + } \ + } + +static void torture_smb2_tree_disconnect_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + struct smb2_tree *tree = + talloc_get_type_abort(private_data, + struct smb2_tree); + + smbXcli_conn_disconnect(tree->session->transport->conn, + NT_STATUS_CTX_CLIENT_QUERY_TIMEOUT); +} + +/* + * Check that Samba3 correctly deals with conflicting local posix byte range + * locks on an underlying file via "normal" SMB2 (without posix extensions). + * + * Note: This test depends on "posix locking = yes". + * Note: To run this test, use "--option=torture:localdir=" + */ +static bool torture_samba3_localposixlock1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + int rc; + const char *fname = "posixtimedlock.dat"; + const char *fpath; + const char *localdir; + const char *localname; + struct smb2_handle h = {{0}}; + struct smb2_lock lck = {0}; + struct smb2_lock_element el[1] = {{0}}; + struct smb2_request *req = NULL; + int fd = -1; + struct flock posix_lock; + struct tevent_timer *te; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + fpath = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fname); + torture_assert(tctx, fpath != NULL, "fpath\n"); + + status = torture_smb2_testfile(tree, fpath, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + localdir = torture_setting_string(tctx, "localdir", NULL); + torture_assert(tctx, localdir != NULL, + "--option=torture:localdir= required\n"); + + localname = talloc_asprintf(tctx, "%s/%s/%s", + localdir, BASEDIR, fname); + torture_assert(tctx, localname != NULL, "localname\n"); + + /* + * Lock a byte range from posix + */ + + torture_comment(tctx, " local open(%s)\n", localname); + fd = open(localname, O_RDWR); + if (fd == -1) { + torture_warning(tctx, "open(%s) failed: %s\n", + localname, strerror(errno)); + torture_assert(tctx, fd != -1, "open localname\n"); + } + + posix_lock.l_type = F_WRLCK; + posix_lock.l_whence = SEEK_SET; + posix_lock.l_start = 0; + posix_lock.l_len = 1; + + torture_comment(tctx, " local fcntl\n"); + rc = fcntl(fd, F_SETLK, &posix_lock); + if (rc == -1) { + torture_warning(tctx, "fcntl failed: %s\n", strerror(errno)); + torture_assert_goto(tctx, rc != -1, ret, done, + "fcntl lock\n"); + } + + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0x00000000; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = h; + + torture_comment(tctx, " remote non-blocking lock\n"); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(tctx, " remote async blocking lock\n"); + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + req = smb2_lock_send(tree, &lck); + torture_assert_goto(tctx, req != NULL, ret, done, "smb2_lock_send()\n"); + + te = tevent_add_timer(tctx->ev, + tctx, timeval_current_ofs(5, 0), + torture_smb2_tree_disconnect_timer, + tree); + torture_assert_goto(tctx, te != NULL, ret, done, "tevent_add_timer\n"); + + torture_comment(tctx, " remote wait for STATUS_PENDING\n"); + WAIT_FOR_ASYNC_RESPONSE(req); + + torture_comment(tctx, " local close file\n"); + close(fd); + fd = -1; + + torture_comment(tctx, " remote lock should now succeed\n"); + status = smb2_lock_recv(req, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + if (fd != -1) { + close(fd); + } + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + +struct torture_suite *torture_smb2_samba3misc_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "samba3misc"); + + torture_suite_add_1smb2_test(suite, "localposixlock1", + torture_samba3_localposixlock1); + + suite->description = talloc_strdup(suite, "SMB2 Samba3 MISC"); + + return suite; +} diff --git a/source4/torture/smb2/scan.c b/source4/torture/smb2/scan.c new file mode 100644 index 0000000..086cc75 --- /dev/null +++ b/source4/torture/smb2/scan.c @@ -0,0 +1,265 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 opcode scanner + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/cmdline/cmdline.h" +#include "torture/torture.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +#include "torture/smb2/proto.h" + +/* + scan for valid SMB2 getinfo levels +*/ +static bool torture_smb2_getinfo_scan(struct torture_context *tctx) +{ + struct smb2_tree *tree; + NTSTATUS status; + struct smb2_getinfo io; + struct smb2_handle fhandle, dhandle; + int c, i; + + static const char *FNAME = "scan-getinfo.dat"; + static const char *FNAME2 = "scan-getinfo.dat:2ndstream"; + static const char *DNAME = "scan-getinfo.dir"; + static const char *DNAME2 = "scan-getinfo.dir:2ndstream"; + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + status = torture_setup_complex_file(tctx, tree, FNAME); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to setup complex file '%s': %s\n", + FNAME, nt_errstr(status)); + return false; + } + torture_setup_complex_file(tctx, tree, FNAME2); + + status = torture_setup_complex_dir(tctx, tree, DNAME); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to setup complex dir '%s': %s\n", + DNAME, nt_errstr(status)); + smb2_util_unlink(tree, FNAME); + return false; + } + torture_setup_complex_file(tctx, tree, DNAME2); + + torture_smb2_testfile(tree, FNAME, &fhandle); + torture_smb2_testdir(tree, DNAME, &dhandle); + + + ZERO_STRUCT(io); + io.in.output_buffer_length = 0xFFFF; + + for (c=1;c<5;c++) { + for (i=0;i<0x100;i++) { + io.in.info_type = c; + io.in.info_class = i; + + io.in.file.handle = fhandle; + status = smb2_getinfo(tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + torture_comment(tctx, "file level 0x%02x:%02x %u is %ld bytes - %s\n", + io.in.info_type, io.in.info_class, + (unsigned)io.in.info_class, + (long)io.out.blob.length, nt_errstr(status)); + dump_data(1, io.out.blob.data, io.out.blob.length); + } + + io.in.file.handle = dhandle; + status = smb2_getinfo(tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + torture_comment(tctx, "dir level 0x%02x:%02x %u is %ld bytes - %s\n", + io.in.info_type, io.in.info_class, + (unsigned)io.in.info_class, + (long)io.out.blob.length, nt_errstr(status)); + dump_data(1, io.out.blob.data, io.out.blob.length); + } + } + } + + smb2_util_unlink(tree, FNAME); + smb2_util_rmdir(tree, DNAME); + return true; +} + +/* + scan for valid SMB2 setinfo levels +*/ +static bool torture_smb2_setinfo_scan(struct torture_context *tctx) +{ + static const char *FNAME = "scan-setinfo.dat"; + static const char *FNAME2 = "scan-setinfo.dat:2ndstream"; + + struct smb2_tree *tree; + NTSTATUS status; + struct smb2_setinfo io; + struct smb2_handle handle; + int c, i; + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + status = torture_setup_complex_file(tctx, tree, FNAME); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to setup complex file '%s': %s\n", + FNAME, nt_errstr(status)); + return false; + } + torture_setup_complex_file(tctx, tree, FNAME2); + + torture_smb2_testfile(tree, FNAME, &handle); + + ZERO_STRUCT(io); + io.in.blob = data_blob_talloc_zero(tctx, 1024); + + for (c=1;c<5;c++) { + for (i=0;i<0x100;i++) { + io.in.level = (i<<8) | c; + io.in.file.handle = handle; + status = smb2_setinfo(tree, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + torture_comment(tctx, "file level 0x%04x - %s\n", + io.in.level, nt_errstr(status)); + } + } + } + + smb2_util_unlink(tree, FNAME); + return true; +} + + +/* + scan for valid SMB2 scan levels +*/ +static bool torture_smb2_find_scan(struct torture_context *tctx) +{ + struct smb2_tree *tree; + NTSTATUS status; + struct smb2_find io; + struct smb2_handle handle; + int i; + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + + torture_assert_ntstatus_ok(tctx, + smb2_util_roothandle(tree, &handle), + "Failed to open roothandle"); + + ZERO_STRUCT(io); + io.in.file.handle = handle; + io.in.pattern = "*"; + io.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; + io.in.max_response_size = 0x10000; + + for (i=1;i<0x100;i++) { + io.in.level = i; + + io.in.file.handle = handle; + status = smb2_find(tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS) && + !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + torture_comment(tctx, "find level 0x%04x is %ld bytes - %s\n", + io.in.level, (long)io.out.blob.length, nt_errstr(status)); + dump_data(1, io.out.blob.data, io.out.blob.length); + } + } + + return true; +} + +/* + scan for valid SMB2 opcodes +*/ +static bool torture_smb2_scan(struct torture_context *tctx) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_tree *tree; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + int opcode; + struct smb2_request *req; + struct smbcli_options options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + status = smb2_connect(mem_ctx, host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree, tctx->ev, &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "Connection failed"); + + tree->session->transport->options.request_timeout = 3; + + for (opcode=0;opcode<1000;opcode++) { + req = smb2_request_init_tree(tree, opcode, 2, false, 0); + SSVAL(req->out.body, 0, 0); + smb2_transport_send(req); + if (!smb2_request_receive(req)) { + talloc_free(tree); + status = smb2_connect(mem_ctx, host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + &tree, tctx->ev, &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(mem_ctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "Connection failed"); + tree->session->transport->options.request_timeout = 3; + } else { + status = smb2_request_destroy(req); + torture_comment(tctx, "active opcode %4d gave status %s\n", opcode, nt_errstr(status)); + } + } + + talloc_free(mem_ctx); + + return true; +} + +struct torture_suite *torture_smb2_scan_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "scan"); + + torture_suite_add_simple_test(suite, "scan", torture_smb2_scan); + torture_suite_add_simple_test(suite, "getinfo", torture_smb2_getinfo_scan); + torture_suite_add_simple_test(suite, "setinfo", torture_smb2_setinfo_scan); + torture_suite_add_simple_test(suite, "find", torture_smb2_find_scan); + + suite->description = talloc_strdup(suite, "scan target (not a test)"); + + return suite; +} diff --git a/source4/torture/smb2/secleak.c b/source4/torture/smb2/secleak.c new file mode 100644 index 0000000..ca709ed --- /dev/null +++ b/source4/torture/smb2/secleak.c @@ -0,0 +1,91 @@ +/* + Unix SMB/CIFS implementation. + + find security related memory leaks + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) David Mulder 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "system/time.h" +#include "libcli/smb_composite/smb_composite.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" + +static bool try_failed_login(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + struct cli_credentials *credentials = NULL; + uint32_t sessid = 0; + struct smb2_session *session = NULL; + bool result = true; + + session = smb2_session_init(tree->session->transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, session, "Session initialization failed"); + + sessid = smb2cli_session_current_id(tree->session->smbXcli); + credentials = cli_credentials_init(session); + torture_assert_goto(tctx, credentials, result, done, + "Credential allocation failed"); + + cli_credentials_set_conf(credentials, tctx->lp_ctx); + cli_credentials_set_domain(credentials, "INVALID-DOMAIN", CRED_SPECIFIED); + cli_credentials_set_username(credentials, "INVALID-USERNAME", CRED_SPECIFIED); + cli_credentials_set_password(credentials, "INVALID-PASSWORD", CRED_SPECIFIED); + + status = smb2_session_setup_spnego(session, credentials, sessid); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_LOGON_FAILURE, result, done, + "Allowed session setup with invalid credentials?!\n"); + +done: + /* smb2_session_init() steals the transport, and if we don't steal it + * back before freeing session, then we segfault on the next iteration + * because the transport pointer in the tree is now invalid. + */ + tree->session->transport = talloc_steal(tree->session, session->transport); + talloc_free(session); + + return result; +} + +bool torture_smb2_sec_leak(struct torture_context *tctx, struct smb2_tree *tree) +{ + time_t t1 = time_mono(NULL); + int timelimit = torture_setting_int(tctx, "timelimit", 20); + bool result; + + while (time_mono(NULL) < t1+timelimit) { + result = try_failed_login(tctx, tree); + torture_assert(tctx, result, + "Invalid credentials should have failed"); + + talloc_report(NULL, stdout); + } + + return true; +} diff --git a/source4/torture/smb2/sessid.c b/source4/torture/smb2/sessid.c new file mode 100644 index 0000000..bdcec05 --- /dev/null +++ b/source4/torture/smb2/sessid.c @@ -0,0 +1,100 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + Copyright (C) David Mulder 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "system/filesys.h" +#include "system/time.h" +#include "libcli/resolve/resolve.h" +#include "lib/events/events.h" +#include "param/param.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/smb2/proto.h" +#include "libcli/smb/smbXcli_base.h" + + +static void smb2cli_session_set_id(struct smbXcli_session *session, + uint64_t session_id) +{ + smb2cli_session_set_id_and_flags(session, session_id, + smb2cli_session_get_flags(session)); +} + +/** + Try with a wrong session id and check error message. + */ + +bool run_sessidtest(struct torture_context *tctx, struct smb2_tree *tree) +{ + const char *fname = "sessid.tst"; + struct smb2_handle fnum; + struct smb2_create io = {0}; + uint32_t session_id; + union smb_fileinfo finfo; + + NTSTATUS status; + + smb2_util_unlink(tree, fname); + + io.in.fname = fname; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, tree, &io); + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", + fname, nt_errstr(status)); + return false; + } + fnum = io.out.file.handle; + + session_id = smb2cli_session_current_id(tree->session->smbXcli); + smb2cli_session_set_id(tree->session->smbXcli, session_id+1234); + + torture_comment(tctx, "Testing qfileinfo with wrong sessid\n"); + + finfo.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + finfo.all_info2.in.file.handle = fnum; + status = smb2_getinfo_file(tree, tctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + torture_fail(tctx, "smb2_getinfo_file passed with wrong sessid"); + } + + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_USER_SESSION_DELETED, + "smb2_getinfo_file should have returned " + "NT_STATUS_USER_SESSION_DELETED"); + + smb2cli_session_set_id(tree->session->smbXcli, session_id); + + status = smb2_util_close(tree, fnum); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, "close failed (%s)", nt_errstr(status))); + + smb2_util_unlink(tree, fname); + + return true; +} diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c new file mode 100644 index 0000000..823304f --- /dev/null +++ b/source4/torture/smb2/session.c @@ -0,0 +1,5670 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 session setups + + Copyright (C) Michael Adam 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" +#include "libcli/security/security.h" +#include "libcli/resolve/resolve.h" +#include "lib/param/param.h" +#include "lib/util/tevent_ntstatus.h" + +/* Ticket lifetime we want to request in seconds */ +#define KRB5_TICKET_LIFETIME 5 +/* Allowed clock skew in seconds */ +#define KRB5_CLOCKSKEW 5 +/* Time till ticket fully expired in seconds */ +#define KRB5_TICKET_EXPIRETIME KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + +#define texpand(x) #x +#define GENSEC_GSSAPI_REQUESTED_LIFETIME(x) \ + "gensec_gssapi:requested_life_time=" texpand(x) + +#define CHECK_CREATED(tctx, __io, __created, __attribute) \ + do { \ + torture_assert_int_equal(tctx, (__io)->out.create_action, \ + NTCREATEX_ACTION_ ## __created, \ + "out.create_action incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.size, 0, \ + "out.size incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.file_attr, \ + (__attribute), \ + "out.file_attr incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.reserved2, 0, \ + "out.reserverd2 incorrect"); \ + } while(0) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(tctx->ev) != 0) { \ + break; \ + } \ + } + +static void sleep_remaining(struct torture_context *tctx, + const struct timeval *endtime) +{ + struct timeval current = tevent_timeval_current(); + double remaining_secs = timeval_elapsed2(¤t, endtime); + + remaining_secs = remaining_secs < 1.0 ? 1.0 : remaining_secs; + torture_comment( + tctx, + "sleep for %2.f second(s) that the krb5 ticket expires", + remaining_secs); + smb_msleep((int)(remaining_secs * 1000)); +} + +/** + * basic test for doing a session reconnect + */ +bool test_session_reconnect1(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2; + uint64_t previous_session_id; + bool ret = true; + struct smb2_tree *tree2 = NULL; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reconnect_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* disconnect, reconnect and then do durable reopen */ + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + torture_assert_goto(tctx, torture_smb2_connection_ext(tctx, previous_session_id, + &tree->session->transport->options, &tree2), + ret, done, + "session reconnect failed\n"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_USER_SESSION_DELETED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + h1 = NULL; + + smb2_oplock_create_share(&io2, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree2, mem_ctx, &io2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + CHECK_CREATED(tctx, &io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + _h2 = io2.out.file.handle; + h2 = &_h2; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree2, *h2); + } + + if (tree2 != NULL) { + smb2_util_unlink(tree2, fname); + } + smb2_util_unlink(tree, fname); + + talloc_free(tree); + talloc_free(tree2); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a session reconnect on one connection + */ +bool test_session_reconnect2(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + uint64_t previous_session_id; + bool ret = true; + struct smb2_session *session2 = NULL; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reconnect_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* disconnect, reconnect and then do durable reopen */ + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + torture_assert(tctx, torture_smb2_session_setup(tctx, tree->session->transport, + previous_session_id, tctx, &session2), + "session reconnect (on the same connection) failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_USER_SESSION_DELETED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + h1 = NULL; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + talloc_free(session2); + + talloc_free(mem_ctx); + + return ret; +} + +bool test_session_reauth1(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +bool test_session_reauth2(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + struct cli_credentials *anon_creds = NULL; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test getting security descriptor after reauth + */ +bool test_session_reauth3(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth3_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test setting security descriptor after reauth. + */ +bool test_session_reauth4(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + struct security_descriptor *sd1; + struct security_ace ace; + struct dom_sid *extra_sid; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth4_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sd1 = qfinfo.query_secdesc.out.sd; + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* give full access on the file to anonymous */ + + extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL | SEC_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _h1; + sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; + sfinfo.set_secdesc.in.sd = sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* re-get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test renaming after reauth. + * compare security descriptors before and after rename/reauth + */ +bool test_session_reauth5(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char dname[128]; + char fname[256]; + char fname2[256]; + struct smb2_handle _dh1; + struct smb2_handle *dh1 = NULL; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + bool ok; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + struct security_descriptor *f_sd1; + struct security_descriptor *d_sd1 = NULL; + struct security_ace ace; + struct dom_sid *extra_sid; + + /* Add some random component to the file name. */ + snprintf(dname, sizeof(dname), "session_reauth5_%s.d", + generate_random_str(tctx, 8)); + snprintf(fname, sizeof(fname), "%s\\file.dat", dname); + + ok = smb2_util_setup_dir(tctx, tree, dname); + torture_assert(tctx, ok, "smb2_util_setup_dir not ok"); + + status = torture_smb2_testdir(tree, dname, &_dh1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed"); + dh1 = &_dh1; + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + f_sd1 = qfinfo.query_secdesc.out.sd; + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to rename the file: fails */ + + snprintf(fname2, sizeof(fname2), "%s\\file2.dat", dname); + + status = smb2_util_unlink(tree, fname2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* give full access on the file to anonymous */ + + extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_RIGHTS_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(f_sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _h1; + sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; + sfinfo.set_secdesc.in.sd = f_sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* re-get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as anonymous - again */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to rename the file: fails */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* give full access on the parent dir to anonymous */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _dh1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + d_sd1 = qfinfo.query_secdesc.out.sd; + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_RIGHTS_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(d_sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _dh1; + sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; + sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; + sfinfo.set_secdesc.in.sd = d_sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _dh1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + status = smb2_util_close(tree, _dh1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + dh1 = NULL; + + /* try to rename the file: still fails */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* re-authenticate as original user - again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* rename the file - for verification that it works */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* closs the file, check it is gone and reopen under the new name */ + + status = smb2_util_close(tree, _h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + ZERO_STRUCT(io1); + + smb2_generic_create_share(&io1, + NULL /* lease */, false /* dir */, + fname, + NTCREATEX_DISP_OPEN, + smb2_util_share_access(""), + smb2_util_oplock_level("b"), + 0 /* leasekey */, 0 /* leasestate */); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "smb2_create " + "returned unexpected status"); + + ZERO_STRUCT(io1); + + smb2_generic_create_share(&io1, + NULL /* lease */, false /* dir */, + fname2, + NTCREATEX_DISP_OPEN, + smb2_util_share_access(""), + smb2_util_oplock_level("b"), + 0 /* leasekey */, 0 /* leasestate */); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (dh1 != NULL) { + smb2_util_close(tree, *dh1); + } + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_deltree(tree, dname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * do reauth with wrong credentials, + * hence triggering the error path in reauth. + * The invalid reauth deletes the session. + */ +bool test_session_reauth6(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + char *corrupted_password; + struct cli_credentials *broken_creds; + bool ok; + bool encrypted; + NTSTATUS expected; + enum credentials_use_kerberos krb_state; + + krb_state = cli_credentials_get_kerberos_state( + samba_cmdline_get_creds()); + if (krb_state == CRED_USE_KERBEROS_REQUIRED) { + torture_skip(tctx, + "Can't test failing session setup with kerberos."); + } + + encrypted = smb2cli_tcon_is_encryption_on(tree->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* + * reauthentication with invalid credentials: + */ + + broken_creds = cli_credentials_shallow_copy(mem_ctx, + samba_cmdline_get_creds()); + torture_assert(tctx, (broken_creds != NULL), "talloc error"); + + corrupted_password = talloc_asprintf(mem_ctx, "%s%s", + cli_credentials_get_password(broken_creds), + "corrupt"); + torture_assert(tctx, (corrupted_password != NULL), "talloc error"); + + ok = cli_credentials_set_password(broken_creds, corrupted_password, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_password not ok"); + + status = smb2_session_setup_spnego(tree->session, + broken_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego " + "returned unexpected status"); + + torture_comment(tctx, "did failed reauth\n"); + /* + * now verify that the invalid session reauth has closed our session + */ + + if (encrypted) { + expected = NT_STATUS_CONNECTION_DISCONNECTED; + } else { + expected = NT_STATUS_USER_SESSION_DELETED; + } + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_equal_goto(tctx, status, expected, + ret, done, "smb2_create " + "returned unexpected status"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + + +static bool test_session_expire1i(struct torture_context *tctx, + bool force_signing, + bool force_encryption) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + enum credentials_use_kerberos use_kerberos; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + size_t i; + struct timeval endtime; + bool ticket_expired = false; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + torture_assert_int_equal(tctx, + use_kerberos, + CRED_USE_KERBEROS_REQUIRED, + "please use --use-kerberos=required"); + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + if (force_signing) { + options.signing = SMB_SIGNING_REQUIRED; + } + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + if (force_encryption) { + status = smb2cli_session_encryption_on(tree->session->smbXcli); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2cli_session_encryption_on failed"); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + for (i=0; i < 2; i++) { + torture_comment(tctx, "%s: query info => OK\n", + current_timestring(tctx, true)); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "%s: query info => EXPIRED\n", + current_timestring(tctx, true)); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + /* + * the krb5 library may not handle expired creds + * well, lets start with an empty ccache. + */ + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (!force_encryption) { + smb2cli_session_require_signed_response( + tree->session->smbXcli, true); + } + + torture_comment(tctx, "%s: reauth => OK\n", + current_timestring(tctx, true)); + status = smb2_session_setup_spnego(tree->session, + credentials, + 0 /* previous_session_id */); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + + * KRB5_CLOCKSKEW + a few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, + 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + smb2cli_session_require_signed_response( + tree->session->smbXcli, false); + } + + ticket_expired = timeval_expired(&endtime); + if (ticket_expired) { + struct timeval current = timeval_current(); + double remaining_secs = timeval_elapsed2(¤t, &endtime); + remaining_secs = remaining_secs < 0.0 ? remaining_secs * -1.0 + : remaining_secs; + torture_warning( + tctx, + "The ticket already expired %.2f seconds ago. " + "You might want to increase KRB5_TICKET_LIFETIME.", + remaining_secs); + } + torture_assert(tctx, + ticket_expired == false, + "The kerberos ticket already expired"); + ZERO_STRUCT(qfinfo.access_information.out); + torture_comment(tctx, "%s: %s:%s: before smb2_getinfo_file()\n", + current_timestring(tctx, true), + __location__, __func__); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +static bool test_session_expire1n(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + false, /* force_signing */ + false); /* force_encryption */ +} + +static bool test_session_expire1s(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + true, /* force_signing */ + false); /* force_encryption */ +} + +static bool test_session_expire1e(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + true, /* force_signing */ + true); /* force_encryption */ +} + +static bool test_session_expire2i(struct torture_context *tctx, + bool force_encryption) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + const char *unc = NULL; + struct smb2_tree *tree2 = NULL; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + enum credentials_use_kerberos use_kerberos; + uint32_t caps; + char fname[256]; + struct smb2_handle dh; + struct smb2_handle dh2; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct smb2_flush flsh; + struct smb2_read rd; + const uint8_t wd = 0; + struct smb2_lock lck; + struct smb2_lock_element el; + struct smb2_ioctl ctl; + struct smb2_break oack; + struct smb2_lease_break_ack lack; + struct smb2_find fnd; + union smb_search_data *d = NULL; + unsigned int count; + struct smb2_request *req = NULL; + struct smb2_notify ntf1; + struct smb2_notify ntf2; + struct timeval endtime; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + torture_assert_int_equal(tctx, + use_kerberos, + CRED_USE_KERBEROS_REQUIRED, + "please use --use-kerberos=required"); + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + options.signing = SMB_SIGNING_REQUIRED; + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + if (force_encryption) { + status = smb2cli_session_encryption_on(tree->session->smbXcli); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2cli_session_encryption_on failed"); + } + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + status = smb2_util_roothandle(tree, &dh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_roothandle failed"); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + torture_comment(tctx, "query info => OK\n"); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + torture_comment(tctx, "lock => OK\n"); + ZERO_STRUCT(lck); + lck.in.locks = ⪙ + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = *h1; + ZERO_STRUCT(el); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el.offset = 0x0000000000000000; + el.length = 0x0000000000000001; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_lock lock failed"); + + torture_comment(tctx, "1st notify => PENDING\n"); + ZERO_STRUCT(ntf1); + ntf1.in.file.handle = dh; + ntf1.in.recursive = 0x0000; + ntf1.in.buffer_size = 128; + ntf1.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES; + ntf1.in.unknown = 0x00000000; + req = smb2_notify_send(tree, &ntf1); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(tctx->ev) != 0) { + break; + } + } + + torture_assert_goto(tctx, req->state <= SMB2_REQUEST_RECV, ret, done, + "smb2_notify finished"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "query info => EXPIRED\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + + torture_comment(tctx, "set info => EXPIRED\n"); + ZERO_STRUCT(sfinfo); + sfinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.end_of_file_info.in.file.handle = *h1; + sfinfo.end_of_file_info.in.size = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + torture_comment(tctx, "flush => EXPIRED\n"); + ZERO_STRUCT(flsh); + flsh.in.file.handle = *h1; + status = smb2_flush(tree, &flsh); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_flush " + "returned unexpected status"); + + torture_comment(tctx, "read => EXPIRED\n"); + ZERO_STRUCT(rd); + rd.in.file.handle = *h1; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tctx, &rd); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_read " + "returned unexpected status"); + + torture_comment(tctx, "write => EXPIRED\n"); + status = smb2_util_write(tree, *h1, &wd, 0, 1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_write " + "returned unexpected status"); + + torture_comment(tctx, "ioctl => EXPIRED\n"); + ZERO_STRUCT(ctl); + ctl.in.file.handle = *h1; + ctl.in.function = FSCTL_SRV_ENUM_SNAPS; + ctl.in.max_output_response = 16; + ctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + status = smb2_ioctl(tree, tctx, &ctl); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_ioctl " + "returned unexpected status"); + + torture_comment(tctx, "oplock ack => EXPIRED\n"); + ZERO_STRUCT(oack); + oack.in.file.handle = *h1; + status = smb2_break(tree, &oack); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_break " + "returned unexpected status"); + + if (caps & SMB2_CAP_LEASING) { + torture_comment(tctx, "lease ack => EXPIRED\n"); + ZERO_STRUCT(lack); + lack.in.lease.lease_version = 1; + lack.in.lease.lease_key.data[0] = 1; + lack.in.lease.lease_key.data[1] = 2; + status = smb2_lease_break_ack(tree, &lack); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_break " + "returned unexpected status"); + } + + torture_comment(tctx, "query directory => EXPIRED\n"); + ZERO_STRUCT(fnd); + fnd.in.file.handle = dh; + fnd.in.pattern = "*"; + fnd.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + fnd.in.max_response_size= 0x100; + fnd.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + status = smb2_find_level(tree, tree, &fnd, &count, &d); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_find_level " + "returned unexpected status"); + + torture_comment(tctx, "1st notify => CANCEL\n"); + smb2_cancel(req); + + torture_comment(tctx, "2nd notify => EXPIRED\n"); + ZERO_STRUCT(ntf2); + ntf2.in.file.handle = dh; + ntf2.in.recursive = 0x0000; + ntf2.in.buffer_size = 128; + ntf2.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES; + ntf2.in.unknown = 0x00000000; + status = smb2_notify(tree, tctx, &ntf2); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_notify " + "returned unexpected status"); + + torture_assert_goto(tctx, req->state > SMB2_REQUEST_RECV, ret, done, + "smb2_notify (1st) not finished"); + + status = smb2_notify_recv(req, tctx, &ntf1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, "smb2_notify cancelled" + "returned unexpected status"); + + torture_comment(tctx, "tcon => EXPIRED\n"); + tree2 = smb2_tree_init(tree->session, tctx, false); + torture_assert(tctx, tree2 != NULL, "smb2_tree_init"); + timeout_msec = tree->session->transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tree2, tctx->ev, + tree2->session->transport->conn, + timeout_msec, + tree2->session->smbXcli, + tree2->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2cli_tcon" + "returned unexpected status"); + + torture_comment(tctx, "create => EXPIRED\n"); + status = smb2_util_roothandle(tree, &dh2); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_roothandle" + "returned unexpected status"); + + torture_comment(tctx, "tdis => EXPIRED\n"); + status = smb2_tdis(tree); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2cli_tdis" + "returned unexpected status"); + + /* + * (Un)Lock, Close and Logoff are still possible + */ + + torture_comment(tctx, "1st unlock => OK\n"); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_lock unlock failed"); + + torture_comment(tctx, "2nd unlock => RANGE_NOT_LOCKED\n"); + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_RANGE_NOT_LOCKED, + ret, done, "smb2_lock 2nd unlock" + "returned unexpected status"); + + torture_comment(tctx, "lock => EXPIRED\n"); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_roothandle" + "returned unexpected status"); + + torture_comment(tctx, "close => OK\n"); + status = smb2_util_close(tree, *h1); + h1 = NULL; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close failed"); + + torture_comment(tctx, "echo without session => OK\n"); + status = smb2_keepalive(tree->session->transport); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_keepalive without session failed"); + + torture_comment(tctx, "echo with session => OK\n"); + req = smb2_keepalive_send(tree->session->transport, tree->session); + status = smb2_keepalive_recv(req); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_keepalive with session failed"); + + torture_comment(tctx, "logoff => OK\n"); + status = smb2_logoff(tree->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_logoff failed"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +static bool test_session_expire2s(struct torture_context *tctx) +{ + return test_session_expire2i(tctx, + false); /* force_encryption */ +} + +static bool test_session_expire2e(struct torture_context *tctx) +{ + return test_session_expire2i(tctx, + true); /* force_encryption */ +} + +static bool test_session_expire_disconnect(struct torture_context *tctx) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + enum credentials_use_kerberos use_kerberos; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool connected; + struct timeval endtime; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + lpcfg_smbcli_options(tctx->lp_ctx, &options); + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + smbXcli_session_set_disconnect_expired(tree->session->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + torture_comment(tctx, "query info => OK\n"); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "query info => EXPIRED\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + connected = smbXcli_conn_is_connected(tree->session->transport->conn); + torture_assert_goto(tctx, !connected, ret, done, "connected\n"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +bool test_session_bind1(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_bind1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree1, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* use the 1st connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* use the 2nd connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1 = NULL; + + /* + * Now bind the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_1, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + ret = true; +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_util_unlink(tree1, fname); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind2(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname1[256]; + char fname2[256]; + struct smb2_handle _h1f1; + struct smb2_handle *h1f1 = NULL; + struct smb2_handle _h1f2; + struct smb2_handle *h1f2 = NULL; + struct smb2_handle _h2f2; + struct smb2_handle *h2f2 = NULL; + struct smb2_create io1f1; + struct smb2_create io1f2; + struct smb2_create io2f1; + struct smb2_create io2f2; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport2 = NULL; + struct smbcli_options options3; + struct smb2_tree *tree3 = NULL; + struct smb2_transport *transport3 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session1_3 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session2_3 = NULL; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname1, sizeof(fname1), "session_bind2_1_%s.dat", + generate_random_str(tctx, 8)); + snprintf(fname2, sizeof(fname2), "session_bind2_2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + smb2_oplock_create_share(&io1f1, fname1, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + smb2_oplock_create_share(&io1f2, fname2, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + + status = smb2_create(tree1, mem_ctx, &io1f1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1f1 = io1f1.out.file.handle; + h1f1 = &_h1f1; + CHECK_CREATED(tctx, &io1f1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1f1.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + smb2cli_tcon_should_sign(tree2->smbXcli, true); + + smb2_oplock_create_share(&io2f1, fname1, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + smb2_oplock_create_share(&io2f2, fname2, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + + status = smb2_create(tree2, mem_ctx, &io2f2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h2f2 = io2f2.out.file.handle; + h2f2 = &_h2f2; + CHECK_CREATED(tctx, &io2f2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io2f2.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + options3 = transport1->options; + options3.signing = SMB_SIGNING_REQUIRED; + options3.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree3, + tctx->ev, + &options3, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport3 = tree3->session->transport; + + /* + * Create a fake session for the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + /* + * Now bind the 3rd transport connection to the 1st session + */ + session1_3 = smb2_session_channel(transport3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session1_1); + torture_assert(tctx, session1_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* + * Create a fake session for the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + /* + * Now bind the 3rd transport connection to the 2nd session + */ + session2_3 = smb2_session_channel(transport3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session2_2); + torture_assert(tctx, session2_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1f1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_getinfo_file failed"); + tree1->session = session1_3; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h2f2; + tree2->session = session2_1; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_getinfo_file failed"); + tree2->session = session2_2; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + tree2->session = session2_3; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree1->session = session1_2; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree1->session = session1_3; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + tree2->session = session2_1; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree2->session = session2_2; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree2->session = session2_3; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + smbXcli_conn_disconnect(transport3->conn, NT_STATUS_LOCAL_DISCONNECT); + smb_msleep(500); + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree1->session = session1_2; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + + tree2->session = session2_1; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree2->session = session2_2; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + smb_msleep(500); + h2f2 = NULL; + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1f2 = io1f2.out.file.handle; + h1f2 = &_h1f2; + CHECK_CREATED(tctx, &io1f2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1f2.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1f1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1f1 = NULL; + + ret = true; +done: + + smbXcli_conn_disconnect(transport3->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + + tree1->session = session1_1; + tree2->session = session2_2; + + if (h1f1 != NULL) { + smb2_util_close(tree1, *h1f1); + } + if (h1f2 != NULL) { + smb2_util_close(tree1, *h1f2); + } + if (h2f2 != NULL) { + smb2_util_close(tree2, *h2f2); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind_auth_mismatch(struct torture_context *tctx, + struct smb2_tree *tree1, + const char *testname, + struct cli_credentials *creds1, + struct cli_credentials *creds2, + bool creds2_require_ok) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session3_1 = NULL; + uint32_t caps; + bool encrypted; + bool creds2_got_ok = false; + + encrypted = smb2cli_tcon_is_encryption_on(tree1->smbXcli); + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", testname, + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree1, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + creds1, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + creds1, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* use the 1st connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* use the 2nd connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1 = NULL; + + /* + * Create a 3rd session in order to check if the invalid (creds2) + * are mapped to guest. + */ + session3_1 = smb2_session_init(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, session3_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session3_1, + creds2, + 0 /* previous_session_id */); + if (creds2_require_ok) { + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego worked"); + creds2_got_ok = true; + } else if (NT_STATUS_IS_OK(status)) { + bool authentiated = smbXcli_session_is_authenticated(session3_1->smbXcli); + torture_assert(tctx, !authentiated, "Invalid credentials allowed!"); + creds2_got_ok = true; + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + } + + /* + * Now bind the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked on invalid channel"); + + status = smb2_session_setup_spnego(session2_1, + creds2, + 0 /* previous_session_id */); + if (creds2_got_ok) { + /* + * attaching with a different user (guest or anonymous) results + * in ACCESS_DENIED. + */ + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, ret, done, + "smb2_session_setup_spnego worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + } + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked on invalid channel"); + + tree2->session = session2_2; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + if (creds2_got_ok) { + /* + * We got ACCESS_DENIED on the session bind + * with a different user, now check that + * the correct user can actually bind on + * the same connection. + */ + TALLOC_FREE(session2_1); + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_1, + creds1, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + tree2->session = session2_2; + } + + tree1->session = session1_1; + status = smb2_util_unlink(tree1, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + + tree1->session = session1_2; + status = smb2_util_unlink(tree1, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + + if (creds2_got_ok) { + /* + * With valid credentials, there's no point to test a failing + * reauth. + */ + ret = true; + goto done; + } + + /* + * Do a failing reauth the 2nd channel + */ + status = smb2_session_setup_spnego(session1_2, + creds2, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + + tree1->session = session1_1; + status = smb2_util_unlink(tree1, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport1->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked"); + } + + tree1->session = session1_2; + status = smb2_util_unlink(tree1, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport2->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked"); + } + + status = smb2_util_unlink(tree2, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport1->conn), ret, done, + "smb2_util_unlink worked"); + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport2->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + } + + ret = true; +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_util_unlink(tree1, fname); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind_invalid_auth(struct torture_context *tctx, struct smb2_tree *tree1) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct cli_credentials *invalid_credentials = NULL; + bool ret = false; + + invalid_credentials = cli_credentials_init(tctx); + torture_assert(tctx, (invalid_credentials != NULL), "talloc error"); + cli_credentials_set_username(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_domain(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_password(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_realm(invalid_credentials, NULL, CRED_SPECIFIED); + cli_credentials_set_workstation(invalid_credentials, "", CRED_UNINITIALISED); + + ret = test_session_bind_auth_mismatch(tctx, tree1, __func__, + credentials, + invalid_credentials, + false); + return ret; +} + +static bool test_session_bind_different_user(struct torture_context *tctx, struct smb2_tree *tree1) +{ + struct cli_credentials *credentials1 = samba_cmdline_get_creds(); + struct cli_credentials *credentials2 = torture_user2_credentials(tctx, tctx); + char *u1 = cli_credentials_get_unparsed_name(credentials1, tctx); + char *u2 = cli_credentials_get_unparsed_name(credentials2, tctx); + bool ret = false; + bool bval; + + torture_assert(tctx, (credentials2 != NULL), "talloc error"); + bval = cli_credentials_is_anonymous(credentials2); + if (bval) { + torture_skip(tctx, "valid user2 credentials are required"); + } + bval = strequal(u1, u2); + if (bval) { + torture_skip(tctx, "different user2 credentials are required"); + } + + ret = test_session_bind_auth_mismatch(tctx, tree1, __func__, + credentials1, + credentials2, + true); + return ret; +} + +static bool test_session_bind_negative_smbXtoX(struct torture_context *tctx, + const char *testname, + struct cli_credentials *credentials, + const struct smbcli_options *options1, + const struct smbcli_options *options2, + NTSTATUS bind_reject_status) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + bool ret = false; + struct smb2_tree *tree1 = NULL; + struct smb2_session *session1_1 = NULL; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo1; + struct smb2_tree *tree2_0 = NULL; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_2 = NULL; + uint64_t session1_id = 0; + uint16_t session1_flags = 0; + NTSTATUS deleted_status = NT_STATUS_USER_SESSION_DELETED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree1, + tctx->ev, + options1, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options1 failed"); + session1_1 = tree1->session; + session1_id = smb2cli_session_current_id(session1_1->smbXcli); + session1_flags = smb2cli_session_get_flags(session1_1->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", + testname, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + status = smb2_create(tree1, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_0, + tctx->ev, + options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options2 failed"); + transport2 = tree2_0->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, bind_reject_status, ret, done, + "smb2_session_setup_spnego failed"); + if (NT_STATUS_IS_OK(bind_reject_status)) { + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + tree1->session = session1_1; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + } + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + if (NT_STATUS_IS_OK(bind_reject_status)) { + deleted_status = NT_STATUS_ACCESS_DENIED; + bind_reject_status = NT_STATUS_ACCESS_DENIED; + } + + /* + * I guess this is not part of MultipleChannel_Negative_SMB2002, + * but we should also check the status without + * SMB2_SESSION_FLAG_BINDING. + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + session1_2->needs_bind = false; + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, deleted_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* + * ... and we should also check the status without any existing + * session keys. + */ + session1_2 = smb2_session_init(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + talloc_steal(tree2_0->session, transport2); + smb2cli_session_set_id_and_flags(session1_2->smbXcli, + session1_id, session1_flags); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, deleted_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* + * Now bind the 2nd transport connection to the 1st session (again) + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, bind_reject_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + talloc_free(tree2_0); + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + talloc_free(tree1); + + return ret; +} + +/* + * This is similar to the MultipleChannel_Negative_SMB2002 test + * from the Windows Protocol Test Suite. + * + * It demonstrates that the server needs to do lookup + * in the global session table in order to get the signing + * and error code of invalid session setups correct. + * + * See: https://bugzilla.samba.org/show_bug.cgi?id=14512 + * + * Note you can ignore tree0... + */ +static bool test_session_bind_negative_smb202(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.02 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_zero(); + options1.max_protocol = PROTOCOL_SMB2_02; + + options2 = options1; + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb210s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.max_protocol = PROTOCOL_SMB2_10; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb210d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.max_protocol = PROTOCOL_SMB2_10; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb2to3s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb2to3d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to2s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to2d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to3s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_02; + options1.max_protocol = PROTOCOL_SMB3_02; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to3d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_02; + options1.max_protocol = PROTOCOL_SMB3_02; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3encGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3encGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneCtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneCtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneHtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneHtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signC30toGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_02; + options1.signing = SMB_SIGNING_REQUIRED; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signC30toGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_02; + options1.signing = SMB_SIGNING_REQUIRED; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signH2XtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + options1.signing = SMB_SIGNING_REQUIRED; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signH2XtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + options1.signing = SMB_SIGNING_REQUIRED; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoC30s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_02; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoC30d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_02; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoH2Xs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoH2Xd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_two_logoff(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + NTSTATUS status; + bool ret = true; + struct smbcli_options transport2_options; + struct smb2_tree *tree2 = NULL; + struct smb2_session *session2 = NULL; + struct smb2_session *session1 = tree1->session; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2; + bool ok; + + /* Connect 2nd connection */ + torture_comment(tctx, "connect tree2 with the same client_guid\n"); + transport2_options = transport1->options; + ok = torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2); + torture_assert(tctx, ok, "couldn't connect tree2\n"); + transport2 = tree2->session->transport; + session2 = tree2->session; + + torture_comment(tctx, "session2: logoff\n"); + status = smb2_logoff(session2); + torture_assert_ntstatus_ok(tctx, status, "session2: logoff"); + torture_comment(tctx, "transport2: keepalive\n"); + status = smb2_keepalive(transport2); + torture_assert_ntstatus_ok(tctx, status, "transport2: keepalive"); + torture_comment(tctx, "transport2: disconnect\n"); + TALLOC_FREE(tree2); + + torture_comment(tctx, "session1: logoff\n"); + status = smb2_logoff(session1); + torture_assert_ntstatus_ok(tctx, status, "session1: logoff"); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + torture_assert_ntstatus_ok(tctx, status, "transport1: keepalive"); + torture_comment(tctx, "transport1: disconnect\n"); + TALLOC_FREE(tree1); + + return ret; +} + +static bool test_session_sign_enc(struct torture_context *tctx, + const char *testname, + struct cli_credentials *credentials1, + const struct smbcli_options *options1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + bool ret = false; + struct smb2_tree *tree1 = NULL; + char fname[256]; + struct smb2_handle rh = {{0}}; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo1; + union smb_notify notify; + struct smb2_request *req = NULL; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials1, + &tree1, + tctx->ev, + options1, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options1 failed"); + + status = smb2_util_roothandle(tree1, &rh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_roothandle failed"); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", + testname, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + status = smb2_create(tree1, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = rh; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree1, &(notify.smb2)); + WAIT_FOR_ASYNC_RESPONSE(req); + + status = smb2_cancel(req); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_cancel failed"); + + status = smb2_notify_recv(req, tctx, &(notify.smb2)); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_CANCELLED, + ret, done, + "smb2_notify_recv failed"); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + TALLOC_FREE(tree1); + + return ret; +} + +static bool test_session_signing_hmac_sha_256(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_signing_aes_128_cmac(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_signing_aes_128_gmac(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_128_ccm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_128_gcm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_256_ccm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES256_CCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_256_gcm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES256_GCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_ntlmssp_bug14932(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct cli_credentials *ntlm_creds = + cli_credentials_shallow_copy(tctx, samba_cmdline_get_creds()); + NTSTATUS status; + bool ret = true; + /* + * This is a NTLMv2_RESPONSE with the strange + * NTLMv2_CLIENT_CHALLENGE used by the net diag + * tool. + * + * As we expect an error anyway we fill the + * Response part with 0xab... + */ + static const char *netapp_magic = + "\xab\xab\xab\xab\xab\xab\xab\xab" + "\xab\xab\xab\xab\xab\xab\xab\xab" + "\x01\x01\x00\x00\x00\x00\x00\x00" + "\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f" + "\xb8\x82\x3a\xf1\xb3\xdd\x08\x15" + "\x00\x00\x00\x00\x11\xa2\x08\x81" + "\x50\x38\x22\x78\x2b\x94\x47\xfe" + "\x54\x94\x7b\xff\x17\x27\x5a\xb4" + "\xf4\x18\xba\xdc\x2c\x38\xfd\x5b" + "\xfb\x0e\xc1\x85\x1e\xcc\x92\xbb" + "\x9b\xb1\xc4\xd5\x53\x14\xff\x8c" + "\x76\x49\xf5\x45\x90\x19\xa2"; + DATA_BLOB lm_response = data_blob_talloc_zero(tctx, 24); + DATA_BLOB lm_session_key = data_blob_talloc_zero(tctx, 16); + DATA_BLOB nt_response = data_blob_const(netapp_magic, 95); + DATA_BLOB nt_session_key = data_blob_talloc_zero(tctx, 16); + + cli_credentials_set_kerberos_state(ntlm_creds, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + cli_credentials_set_ntlm_response(ntlm_creds, + &lm_response, + &lm_session_key, + &nt_response, + &nt_session_key, + CRED_SPECIFIED); + status = smb2_session_setup_spnego(tree->session, + ntlm_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, + "smb2_session_setup_spnego failed"); + + return ret; +} + +struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "session"); + + torture_suite_add_1smb2_test(suite, "reconnect1", test_session_reconnect1); + torture_suite_add_1smb2_test(suite, "reconnect2", test_session_reconnect2); + torture_suite_add_1smb2_test(suite, "reauth1", test_session_reauth1); + torture_suite_add_1smb2_test(suite, "reauth2", test_session_reauth2); + torture_suite_add_1smb2_test(suite, "reauth3", test_session_reauth3); + torture_suite_add_1smb2_test(suite, "reauth4", test_session_reauth4); + torture_suite_add_1smb2_test(suite, "reauth5", test_session_reauth5); + torture_suite_add_1smb2_test(suite, "reauth6", test_session_reauth6); + torture_suite_add_simple_test(suite, "expire1n", test_session_expire1n); + torture_suite_add_simple_test(suite, "expire1s", test_session_expire1s); + torture_suite_add_simple_test(suite, "expire1e", test_session_expire1e); + torture_suite_add_simple_test(suite, "expire2s", test_session_expire2s); + torture_suite_add_simple_test(suite, "expire2e", test_session_expire2e); + torture_suite_add_simple_test(suite, "expire_disconnect", + test_session_expire_disconnect); + torture_suite_add_1smb2_test(suite, "bind1", test_session_bind1); + torture_suite_add_1smb2_test(suite, "bind2", test_session_bind2); + torture_suite_add_1smb2_test(suite, "bind_invalid_auth", test_session_bind_invalid_auth); + torture_suite_add_1smb2_test(suite, "bind_different_user", test_session_bind_different_user); + torture_suite_add_1smb2_test(suite, "bind_negative_smb202", test_session_bind_negative_smb202); + torture_suite_add_1smb2_test(suite, "bind_negative_smb210s", test_session_bind_negative_smb210s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb210d", test_session_bind_negative_smb210d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb2to3s", test_session_bind_negative_smb2to3s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb2to3d", test_session_bind_negative_smb2to3d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to2s", test_session_bind_negative_smb3to2s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to2d", test_session_bind_negative_smb3to2d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to3s", test_session_bind_negative_smb3to3s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to3d", test_session_bind_negative_smb3to3d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3encGtoCs", test_session_bind_negative_smb3encGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3encGtoCd", test_session_bind_negative_smb3encGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoHs", test_session_bind_negative_smb3signCtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoHd", test_session_bind_negative_smb3signCtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoGs", test_session_bind_negative_smb3signCtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoGd", test_session_bind_negative_smb3signCtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoCs", test_session_bind_negative_smb3signHtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoCd", test_session_bind_negative_smb3signHtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoGs", test_session_bind_negative_smb3signHtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoGd", test_session_bind_negative_smb3signHtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoCs", test_session_bind_negative_smb3signGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoCd", test_session_bind_negative_smb3signGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoHs", test_session_bind_negative_smb3signGtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoHd", test_session_bind_negative_smb3signGtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoCs", test_session_bind_negative_smb3sneGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoCd", test_session_bind_negative_smb3sneGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoHs", test_session_bind_negative_smb3sneGtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoHd", test_session_bind_negative_smb3sneGtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneCtoGs", test_session_bind_negative_smb3sneCtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneCtoGd", test_session_bind_negative_smb3sneCtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneHtoGs", test_session_bind_negative_smb3sneHtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneHtoGd", test_session_bind_negative_smb3sneHtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signC30toGs", test_session_bind_negative_smb3signC30toGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signC30toGd", test_session_bind_negative_smb3signC30toGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signH2XtoGs", test_session_bind_negative_smb3signH2XtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signH2XtoGd", test_session_bind_negative_smb3signH2XtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoC30s", test_session_bind_negative_smb3signGtoC30s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoC30d", test_session_bind_negative_smb3signGtoC30d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoH2Xs", test_session_bind_negative_smb3signGtoH2Xs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoH2Xd", test_session_bind_negative_smb3signGtoH2Xd); + torture_suite_add_1smb2_test(suite, "two_logoff", test_session_two_logoff); + torture_suite_add_1smb2_test(suite, "signing-hmac-sha-256", test_session_signing_hmac_sha_256); + torture_suite_add_1smb2_test(suite, "signing-aes-128-cmac", test_session_signing_aes_128_cmac); + torture_suite_add_1smb2_test(suite, "signing-aes-128-gmac", test_session_signing_aes_128_gmac); + torture_suite_add_1smb2_test(suite, "encryption-aes-128-ccm", test_session_encryption_aes_128_ccm); + torture_suite_add_1smb2_test(suite, "encryption-aes-128-gcm", test_session_encryption_aes_128_gcm); + torture_suite_add_1smb2_test(suite, "encryption-aes-256-ccm", test_session_encryption_aes_256_ccm); + torture_suite_add_1smb2_test(suite, "encryption-aes-256-gcm", test_session_encryption_aes_256_gcm); + torture_suite_add_1smb2_test(suite, "ntlmssp_bug14932", test_session_ntlmssp_bug14932); + + suite->description = talloc_strdup(suite, "SMB2-SESSION tests"); + + return suite; +} + +static bool test_session_require_sign_bug15397(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *creds = NULL; + struct smbcli_options options; + struct smb2_tree *tree = NULL; + uint8_t security_mode; + NTSTATUS status; + bool ok = true; + + /* + * Setup our own connection so we can control the signing flags + */ + + creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, creds != NULL, "cli_credentials_shallow_copy"); + + options = _tree->session->transport->options; + options.client_guid = GUID_random(); + options.signing = SMB_SIGNING_IF_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + creds, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "smb2_connect failed"); + + security_mode = smb2cli_session_security_mode(tree->session->smbXcli); + + torture_assert_int_equal_goto( + tctx, + security_mode, + SMB2_NEGOTIATE_SIGNING_REQUIRED | SMB2_NEGOTIATE_SIGNING_ENABLED, + ok, + done, + "Signing not required"); + +done: + return ok; +} + +struct torture_suite *torture_smb2_session_req_sign_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "session-require-signing"); + + torture_suite_add_1smb2_test(suite, "bug15397", + test_session_require_sign_bug15397); + + suite->description = talloc_strdup(suite, "SMB2-SESSION require signing tests"); + return suite; +} diff --git a/source4/torture/smb2/setinfo.c b/source4/torture/smb2/setinfo.c new file mode 100644 index 0000000..3b01b05 --- /dev/null +++ b/source4/torture/smb2/setinfo.c @@ -0,0 +1,410 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 setinfo individual test suite + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/time.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" + +static bool find_returned_ea(union smb_fileinfo *finfo2, + const char *eaname, + const char *eavalue) +{ + unsigned int i; + unsigned int num_eas = finfo2->all_eas.out.num_eas; + struct ea_struct *eas = finfo2->all_eas.out.eas; + + for (i = 0; i < num_eas; i++) { + if (eas[i].name.s == NULL) { + continue; + } + /* Windows capitalizes returned EA names. */ + if (strcasecmp_m(eas[i].name.s, eaname)) { + continue; + } + if (eavalue == NULL && eas[i].value.length == 0) { + /* Null value, found it ! */ + return true; + } + if (eas[i].value.length == strlen(eavalue) && + memcmp(eas[i].value.data, + eavalue, + strlen(eavalue)) == 0) { + return true; + } + } + return false; +} + +#define BASEDIR "" + +#define FAIL_UNLESS(__cond) \ + do { \ + if (__cond) {} else { \ + torture_result(tctx, TORTURE_FAIL, "%s) condition violated: %s\n", \ + __location__, #__cond); \ + ret = false; goto done; \ + } \ + } while(0) + +/* basic testing of all SMB2 setinfo calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +bool torture_smb2_setinfo(struct torture_context *tctx) +{ + struct smb2_tree *tree; + bool ret = true; + struct smb2_handle handle; + char *fname; + union smb_fileinfo finfo2; + union smb_setfileinfo sfinfo; + struct security_ace ace; + struct security_descriptor *sd; + struct dom_sid *test_sid; + NTSTATUS status, status2=NT_STATUS_OK; + const char *call_name; + time_t basetime = (time(NULL) - 86400) & ~1; + int n = time(NULL) % 100; + struct ea_struct ea; + + ZERO_STRUCT(handle); + + fname = talloc_asprintf(tctx, BASEDIR "fnum_test_%d.txt", n); + + if (!torture_smb2_connection(tctx, &tree)) { + return false; + } + +#define RECREATE_FILE(fname) do { \ + smb2_util_close(tree, handle); \ + status = smb2_create_complex_file(tctx, tree, fname, &handle); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) ERROR: open of %s failed (%s)\n", \ + __location__, fname, nt_errstr(status)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define RECREATE_BOTH do { \ + RECREATE_FILE(fname); \ + } while (0) + + RECREATE_BOTH; + +#define CHECK_CALL(call, rightstatus) do { \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.handle = handle; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s (should be %s)\n", __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + goto done; \ + } \ + } while (0) + +#define CHECK1(call) \ + do { if (NT_STATUS_IS_OK(status)) { \ + finfo2.generic.level = RAW_FILEINFO_ ## call; \ + finfo2.generic.in.file.handle = handle; \ + status2 = smb2_getinfo_file(tree, tctx, &finfo2); \ + if (!NT_STATUS_IS_OK(status2)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s\n", __location__, #call, nt_errstr(status2)); \ + ret = false; \ + goto done; \ + } \ + }} while (0) + +#define CHECK_VALUE(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && finfo2.stype.out.field != value) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \ + call_name, #stype, #field, \ + (unsigned int)value, (unsigned int)finfo2.stype.out.field); \ + torture_smb2_all_info(tctx, tree, handle); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_TIME(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && nt_time_to_unix(finfo2.stype.out.field) != value) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \ + call_name, #stype, #field, \ + (unsigned int)value, \ + (unsigned int)nt_time_to_unix(finfo2.stype.out.field)); \ + torture_warning(tctx, "\t%s", timestring(tctx, value)); \ + torture_warning(tctx, "\t%s\n", nt_time_string(tctx, finfo2.stype.out.field)); \ + torture_smb2_all_info(tctx, tree, handle); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + + torture_smb2_all_info(tctx, tree, handle); + + torture_comment(tctx, "Test basic_information level\n"); + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, create_time, basetime + 100); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, access_time, basetime + 200); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, write_time, basetime + 300); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, change_time, basetime + 400); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_READONLY); + + torture_comment(tctx, "a zero time means don't change\n"); + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, create_time, basetime + 100); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, access_time, basetime + 200); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, write_time, basetime + 300); + CHECK_TIME(SMB2_ALL_INFORMATION, all_info2, change_time, basetime + 400); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_NORMAL); + + torture_comment(tctx, "change the attribute\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_HIDDEN); + + torture_comment(tctx, "zero attrib means don't change\n"); + sfinfo.basic_info.in.attrib = 0; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_HIDDEN); + + torture_comment(tctx, "can't change a file to a directory\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_INVALID_PARAMETER); + + torture_comment(tctx, "restore attribute\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_NORMAL); + + torture_comment(tctx, "Test disposition_information level\n"); + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, delete_pending, 1); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, nlink, 0); + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, delete_pending, 0); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, nlink, 1); + + torture_comment(tctx, "Test allocation_information level\n"); + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, size, 0); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, alloc_size, 4096); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, size, 0); + + torture_comment(tctx, "Test end_of_file_info level\n"); + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, size, 7); + + torture_comment(tctx, "Test position_information level\n"); + sfinfo.position_information.in.position = 123456; + CHECK_CALL(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, position, 123456); + + torture_comment(tctx, "Test mode_information level\n"); + sfinfo.mode_information.in.mode = 2; + CHECK_CALL(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2); + CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, mode, 2); + + sfinfo.mode_information.in.mode = 1; + CHECK_CALL(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + + sfinfo.mode_information.in.mode = 0; + CHECK_CALL(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + torture_comment(tctx, "Test sec_desc level\n"); + ZERO_STRUCT(finfo2); + finfo2.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + CHECK1(SEC_DESC); + sd = finfo2.query_secdesc.out.sd; + + test_sid = dom_sid_parse_talloc(tctx, SID_NT_AUTHENTICATED_USERS); + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL; + ace.trustee = *test_sid; + status = security_descriptor_dacl_add(sd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "add a new ACE to the DACL\n"); + + sfinfo.set_secdesc.in.secinfo_flags = finfo2.query_secdesc.in.secinfo_flags; + sfinfo.set_secdesc.in.sd = sd; + CHECK_CALL(SEC_DESC, NT_STATUS_OK); + FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, handle, sd)); + + torture_comment(tctx, "remove it again\n"); + + status = security_descriptor_dacl_del(sd, test_sid); + CHECK_STATUS(status, NT_STATUS_OK); + + sfinfo.set_secdesc.in.secinfo_flags = finfo2.query_secdesc.in.secinfo_flags; + sfinfo.set_secdesc.in.sd = sd; + CHECK_CALL(SEC_DESC, NT_STATUS_OK); + FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, handle, sd)); + + torture_comment(tctx, "Check zero length EA's behavior\n"); + + /* Set a new EA. */ + sfinfo.full_ea_information.in.eas.num_eas = 1; + ea.flags = 0; + ea.name.private_length = 6; + ea.name.s = "NewEA"; + ea.value = data_blob_string_const("testme"); + sfinfo.full_ea_information.in.eas.eas = &ea; + CHECK_CALL(FULL_EA_INFORMATION, NT_STATUS_OK); + + /* Does it still exist ? */ + finfo2.generic.level = RAW_FILEINFO_SMB2_ALL_EAS; + finfo2.generic.in.file.handle = handle; + finfo2.all_eas.in.continue_flags = 1; + status2 = smb2_getinfo_file(tree, tctx, &finfo2); + if (!NT_STATUS_IS_OK(status2)) { + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s\n", __location__, + "SMB2_ALL_EAS", nt_errstr(status2)); + ret = false; + goto done; + } + + /* Note on Windows EA name is returned capitalized. */ + if (!find_returned_ea(&finfo2, "NewEA", "testme")) { + torture_result(tctx, TORTURE_FAIL, "(%s) Missing EA 'NewEA'\n", __location__); + ret = false; + } + + /* Now zero it out (should delete it) */ + sfinfo.full_ea_information.in.eas.num_eas = 1; + ea.flags = 0; + ea.name.private_length = 6; + ea.name.s = "NewEA"; + ea.value = data_blob_null; + sfinfo.full_ea_information.in.eas.eas = &ea; + CHECK_CALL(FULL_EA_INFORMATION, NT_STATUS_OK); + + /* Does it still exist ? */ + finfo2.generic.level = RAW_FILEINFO_SMB2_ALL_EAS; + finfo2.generic.in.file.handle = handle; + finfo2.all_eas.in.continue_flags = 1; + status2 = smb2_getinfo_file(tree, tctx, &finfo2); + if (!NT_STATUS_IS_OK(status2)) { + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s\n", __location__, + "SMB2_ALL_EAS", nt_errstr(status2)); + ret = false; + goto done; + } + + if (find_returned_ea(&finfo2, "NewEA", NULL)) { + torture_result(tctx, TORTURE_FAIL, "(%s) EA 'NewEA' should be deleted\n", __location__); + ret = false; + } + + /* Set a zero length EA. */ + sfinfo.full_ea_information.in.eas.num_eas = 1; + ea.flags = 0; + ea.name.private_length = 6; + ea.name.s = "ZeroEA"; + ea.value = data_blob_null; + sfinfo.full_ea_information.in.eas.eas = &ea; + CHECK_CALL(FULL_EA_INFORMATION, NT_STATUS_OK); + + /* Does it still exist ? */ + finfo2.generic.level = RAW_FILEINFO_SMB2_ALL_EAS; + finfo2.generic.in.file.handle = handle; + finfo2.all_eas.in.continue_flags = 1; + status2 = smb2_getinfo_file(tree, tctx, &finfo2); + if (!NT_STATUS_IS_OK(status2)) { + torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s\n", __location__, + "SMB2_ALL_EAS", nt_errstr(status2)); + ret = false; + goto done; + } + + /* Over SMB2 ZeroEA should not exist. */ + if (!find_returned_ea(&finfo2, "EAONE", "VALUE1")) { + torture_result(tctx, TORTURE_FAIL, "(%s) Missing EA 'EAONE'\n", __location__); + ret = false; + } + if (!find_returned_ea(&finfo2, "SECONDEA", "ValueTwo")) { + torture_result(tctx, TORTURE_FAIL, "(%s) Missing EA 'SECONDEA'\n", __location__); + ret = false; + } + if (find_returned_ea(&finfo2, "ZeroEA", NULL)) { + torture_result(tctx, TORTURE_FAIL, "(%s) Found null EA 'ZeroEA'\n", __location__); + ret = false; + } + +done: + status = smb2_util_close(tree, handle); + if (NT_STATUS_IS_ERR(status)) { + torture_warning(tctx, "Failed to delete %s - %s\n", fname, nt_errstr(status)); + } + smb2_util_unlink(tree, fname); + + return ret; +} + + diff --git a/source4/torture/smb2/sharemode.c b/source4/torture/smb2/sharemode.c new file mode 100644 index 0000000..97381b5 --- /dev/null +++ b/source4/torture/smb2/sharemode.c @@ -0,0 +1,755 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 sharemodes + + Copyright (C) Christof Schmitt 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/security/security.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "lib/util/smb_strtox.h" +#include + +#define BASEDIRHOLD "sharemode_hold_test" + +struct hold_sharemode_info { + const char *sharemode; + const char *filename; + struct smb2_handle handle; +} hold_sharemode_table[] = { + { + .sharemode = "", + .filename = BASEDIRHOLD "\\N", + }, + { + .sharemode = "R", + .filename = BASEDIRHOLD "\\R", + }, + { + .sharemode = "W", + .filename = BASEDIRHOLD "\\W", + }, + { + .sharemode = "D", + .filename = BASEDIRHOLD "\\D", + }, + { + .sharemode = "RW", + .filename = BASEDIRHOLD "\\RW", + }, + { + .sharemode = "RD", + .filename = BASEDIRHOLD "\\RD", + }, + { + .sharemode = "WD", + .filename = BASEDIRHOLD "\\WD", + }, + { + .sharemode = "RWD", + .filename = BASEDIRHOLD "\\RWD", + }, +}; + +static void signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + struct torture_context *tctx = private_data; + + torture_comment(tctx, "Received signal %d\n", signum); +} + +/* + * Used for manual testing of sharemodes - especially interaction with + * other filesystems (such as NFS and local access). The scenario is + * that this test holds files open and then concurrent access to the same + * files outside of Samba can be tested. + */ +bool torture_smb2_hold_sharemode(struct torture_context *tctx) +{ + struct tevent_context *ev = tctx->ev; + struct smb2_tree *tree = NULL; + struct smb2_handle dir_handle; + struct tevent_signal *s; + NTSTATUS status; + bool ret = true; + int i; + + if (!torture_smb2_connection(tctx, &tree)) { + torture_comment(tctx, "Initializing smb2 connection failed.\n"); + return false; + } + + s = tevent_add_signal(ev, tctx, SIGINT, 0, signal_handler, tctx); + torture_assert_not_null_goto(tctx, s, ret, done, + "Error registering signal handler."); + + torture_comment(tctx, "Setting up open files with sharemodes in %s\n", + BASEDIRHOLD); + + status = torture_smb2_testdir(tree, BASEDIRHOLD, &dir_handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Error creating directory."); + + for (i = 0; i < ARRAY_SIZE(hold_sharemode_table); i++) { + struct hold_sharemode_info *info = &hold_sharemode_table[i]; + struct smb2_create create = { 0 }; + + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.alloc_size = 0; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = + smb2_util_share_access(info->sharemode); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.create_options = 0; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.security_flags = 0; + create.in.fname = info->filename; + create.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + torture_comment(tctx, "opening %s\n", info->filename); + + status = smb2_create(tree, tctx, &create); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + info->handle = create.out.file.handle; + } + + torture_comment(tctx, "Waiting for SIGINT (ctrl-c)\n"); + tevent_loop_wait(ev); + + torture_comment(tctx, "Closing and deleting files\n"); + + for (i = 0; i < ARRAY_SIZE(hold_sharemode_table); i++) { + struct hold_sharemode_info *info = &hold_sharemode_table[i]; + + union smb_setfileinfo sfinfo = { }; + + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = info->handle; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "SETINFO failed\n"); + + status = smb2_util_close(tree, info->handle); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_comment(tctx, "File %s not found, could have " + "been deleted outside of SMB\n", + info->filename); + continue; + } + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE failed\n"); +} + +done: + smb2_deltree(tree, BASEDIRHOLD); + return ret; +} + +/* + * Used for manual testing of sharemodes, especially interaction with + * file systems that can enforce sharemodes. The scenario here is that + * a file is already open outside of Samba with a sharemode and this + * can be used to test accessing the same file from Samba. + */ +bool torture_smb2_check_sharemode(struct torture_context *tctx) +{ + const char *sharemode_string, *access_string, *filename, *operation; + uint32_t sharemode, access; + struct smb2_tree *tree; + struct smb2_create create = { 0 }; + NTSTATUS status; + bool ret = true; + int error = 0; + + sharemode_string = torture_setting_string(tctx, "sharemode", "RWD"); + sharemode = smb2_util_share_access(sharemode_string); + + access_string = torture_setting_string(tctx, "access", "0xf01ff"); + access = smb_strtoul(access_string, NULL, 0, &error, SMB_STR_STANDARD); + if (error != 0) { + torture_comment(tctx, "Initializing access failed.\n"); + return false; + } + + filename = torture_setting_string(tctx, "filename", "testfile"); + operation = torture_setting_string(tctx, "operation", "WD"); + + if (!torture_smb2_connection(tctx, &tree)) { + torture_comment(tctx, "Initializing smb2 connection failed.\n"); + return false; + } + + create.in.desired_access = access; + create.in.alloc_size = 0; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = sharemode; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.create_options = 0; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.security_flags = 0; + create.in.fname = filename; + create.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE failed\n"); + + if (strchr(operation, 'R')) { + struct smb2_read read = { 0 }; + + read.in.file.handle = create.out.file.handle; + read.in.offset = 0; + read.in.length = 1; + + status = smb2_read(tree, tctx, &read); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "READ failed\n"); + } + + if (strchr(operation, 'W')) { + char buf[1]; + status = smb2_util_write(tree, create.out.file.handle, + &buf, 0, sizeof(buf)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "WRITE failed\n"); + } + + if (strchr(operation, 'D')) { + union smb_setfileinfo sfinfo = { }; + + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = create.out.file.handle; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "SETINFO failed\n"); + + status = smb2_util_close(tree, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE failed\n"); + } + +done: + return ret; +} + +struct sharemode_info { + const char *sharemode; + uint32_t access_mask; + bool expect_ok; +} sharemode_table[] = { + + /* + * Basic tests, check each permission bit against every + * possible sharemode combination. + */ + + { "R", SEC_FILE_READ_DATA, true, }, + { "R", SEC_FILE_WRITE_DATA, false, }, + { "R", SEC_FILE_APPEND_DATA, false, }, + { "R", SEC_FILE_READ_EA, true, }, + { "R", SEC_FILE_WRITE_EA, true, }, + { "R", SEC_FILE_EXECUTE, true, }, + { "R", SEC_FILE_READ_ATTRIBUTE, true, }, + { "R", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "R", SEC_STD_DELETE, false, }, + { "R", SEC_STD_READ_CONTROL, true, }, + { "R", SEC_STD_WRITE_DAC, true, }, + { "R", SEC_STD_WRITE_OWNER, true, }, + { "R", SEC_STD_SYNCHRONIZE, true, }, + + { "W", SEC_FILE_READ_DATA, false }, + { "W", SEC_FILE_WRITE_DATA, true, }, + { "W", SEC_FILE_APPEND_DATA, true, }, + { "W", SEC_FILE_READ_EA, true, }, + { "W", SEC_FILE_WRITE_EA, true, }, + { "W", SEC_FILE_EXECUTE, false, }, + { "W", SEC_FILE_READ_ATTRIBUTE, true, }, + { "W", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "W", SEC_STD_DELETE, false, }, + { "W", SEC_STD_READ_CONTROL, true, }, + { "W", SEC_STD_WRITE_DAC, true, }, + { "W", SEC_STD_WRITE_OWNER, true, }, + { "W", SEC_STD_SYNCHRONIZE, true, }, + + { "D", SEC_FILE_READ_DATA, false }, + { "D", SEC_FILE_WRITE_DATA, false }, + { "D", SEC_FILE_APPEND_DATA, false }, + { "D", SEC_FILE_READ_EA, true, }, + { "D", SEC_FILE_WRITE_EA, true, }, + { "D", SEC_FILE_EXECUTE, false, }, + { "D", SEC_FILE_READ_ATTRIBUTE, true, }, + { "D", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "D", SEC_STD_DELETE, true, }, + { "D", SEC_STD_READ_CONTROL, true, }, + { "D", SEC_STD_WRITE_DAC, true, }, + { "D", SEC_STD_WRITE_OWNER, true, }, + { "D", SEC_STD_SYNCHRONIZE, true, }, + + { "RW", SEC_FILE_READ_DATA, true, }, + { "RW", SEC_FILE_WRITE_DATA, true, }, + { "RW", SEC_FILE_APPEND_DATA, true, }, + { "RW", SEC_FILE_READ_EA, true, }, + { "RW", SEC_FILE_WRITE_EA, true, }, + { "RW", SEC_FILE_EXECUTE, true, }, + { "RW", SEC_FILE_READ_ATTRIBUTE, true, }, + { "RW", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RW", SEC_STD_DELETE, false, }, + { "RW", SEC_STD_READ_CONTROL, true, }, + { "RW", SEC_STD_WRITE_DAC, true, }, + { "RW", SEC_STD_WRITE_OWNER, true, }, + { "RW", SEC_STD_SYNCHRONIZE, true, }, + + { "RD", SEC_FILE_READ_DATA, true, }, + { "RD", SEC_FILE_WRITE_DATA, false, }, + { "RD", SEC_FILE_APPEND_DATA, false, }, + { "RD", SEC_FILE_READ_EA, true, }, + { "RD", SEC_FILE_WRITE_EA, true, }, + { "RD", SEC_FILE_EXECUTE, true, }, + { "RD", SEC_FILE_READ_ATTRIBUTE, true, }, + { "RD", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RD", SEC_STD_DELETE, true, }, + { "RD", SEC_STD_READ_CONTROL, true, }, + { "RD", SEC_STD_WRITE_DAC, true, }, + { "RD", SEC_STD_WRITE_OWNER, true, }, + { "RD", SEC_STD_SYNCHRONIZE, true, }, + + { "WD", SEC_FILE_READ_DATA, false }, + { "WD", SEC_FILE_WRITE_DATA, true, }, + { "WD", SEC_FILE_APPEND_DATA, true, }, + { "WD", SEC_FILE_READ_EA, true }, + { "WD", SEC_FILE_WRITE_EA, true, }, + { "WD", SEC_FILE_EXECUTE, false }, + { "WD", SEC_FILE_READ_ATTRIBUTE, true, }, + { "WD", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "WD", SEC_STD_DELETE, true, }, + { "WD", SEC_STD_READ_CONTROL, true, }, + { "WD", SEC_STD_WRITE_DAC, true, }, + { "WD", SEC_STD_WRITE_OWNER, true, }, + { "WD", SEC_STD_SYNCHRONIZE, true, }, + + { "RWD", SEC_FILE_READ_DATA, true }, + { "RWD", SEC_FILE_WRITE_DATA, true, }, + { "RWD", SEC_FILE_APPEND_DATA, true, }, + { "RWD", SEC_FILE_READ_EA, true }, + { "RWD", SEC_FILE_WRITE_EA, true, }, + { "RWD", SEC_FILE_EXECUTE, true, }, + { "RWD", SEC_FILE_READ_ATTRIBUTE, true, }, + { "RWD", SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RWD", SEC_STD_DELETE, true, }, + { "RWD", SEC_STD_READ_CONTROL, true, }, + { "RWD", SEC_STD_WRITE_DAC, true, }, + { "RWD", SEC_STD_WRITE_OWNER, true, }, + { "RWD", SEC_STD_SYNCHRONIZE, true, }, + + /* + * Some more interesting cases. Always request READ or WRITE + * access, as that will trigger the opening of a file + * description in Samba. This especially useful for file + * systems that enforce share modes on open file descriptors. + */ + + { "R", SEC_FILE_READ_DATA, true, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, false, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_APPEND_DATA, false, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_READ_EA, true, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_WRITE_EA, true, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_EXECUTE, true, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "R", SEC_FILE_READ_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "R", SEC_FILE_READ_DATA|SEC_STD_DELETE, false, }, + { "R", SEC_FILE_READ_DATA|SEC_STD_READ_CONTROL, true, }, + { "R", SEC_FILE_READ_DATA|SEC_STD_WRITE_DAC, true, }, + { "R", SEC_FILE_READ_DATA|SEC_STD_WRITE_OWNER, true, }, + { "R", SEC_FILE_READ_DATA|SEC_STD_SYNCHRONIZE, true, }, + + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_READ_DATA, false, }, + { "W", SEC_FILE_WRITE_DATA, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_READ_EA, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_WRITE_EA, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_EXECUTE, false, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_STD_DELETE, false, }, + { "W", SEC_FILE_WRITE_DATA|SEC_STD_READ_CONTROL, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_STD_WRITE_DAC, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_STD_WRITE_OWNER, true, }, + { "W", SEC_FILE_WRITE_DATA|SEC_STD_SYNCHRONIZE, true, }, + + { "RW", SEC_FILE_READ_DATA, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_APPEND_DATA, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_READ_EA, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_WRITE_EA, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_EXECUTE, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_STD_DELETE, false, }, + { "RW", SEC_FILE_READ_DATA|SEC_STD_READ_CONTROL, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_STD_WRITE_DAC, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_STD_WRITE_OWNER, true, }, + { "RW", SEC_FILE_READ_DATA|SEC_STD_SYNCHRONIZE, true, }, + + { "RD", SEC_FILE_READ_DATA, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, false, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_APPEND_DATA, false, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_READ_EA, true }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_EA, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_EXECUTE, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_STD_DELETE, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_STD_READ_CONTROL, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_STD_WRITE_DAC, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_STD_WRITE_OWNER, true, }, + { "RD", SEC_FILE_READ_DATA|SEC_STD_SYNCHRONIZE, true, }, + + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_READ_DATA, false }, + { "WD", SEC_FILE_WRITE_DATA, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_READ_EA, true }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_WRITE_EA, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_EXECUTE, false }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_STD_DELETE, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_STD_READ_CONTROL, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_STD_WRITE_DAC, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_STD_WRITE_OWNER, true, }, + { "WD", SEC_FILE_WRITE_DATA|SEC_STD_SYNCHRONIZE, true, }, + + { "RWD", SEC_FILE_READ_DATA, true }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_APPEND_DATA, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_READ_EA, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_EA, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_EXECUTE, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_FILE_WRITE_ATTRIBUTE, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_STD_DELETE, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_STD_READ_CONTROL, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_STD_WRITE_DAC, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_STD_WRITE_OWNER, true, }, + { "RWD", SEC_FILE_READ_DATA|SEC_STD_SYNCHRONIZE, true, }, +}; + +/* + * Test conflicting sharemodes through SMB2: First open takes a + * sharemode, second open with potentially conflicting access. + */ +static bool test_smb2_sharemode_access(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = "test_sharemode"; + NTSTATUS status; + bool ret = true; + int i; + + for (i = 0; i < ARRAY_SIZE(sharemode_table); i++) { + struct sharemode_info *info = &sharemode_table[i]; + struct smb2_create create1 = { 0 }, create2 = { 0 }; + NTSTATUS expected_status; + + torture_comment(tctx, "index %3d, sharemode %3s, " + "access mask 0x%06x\n", + i, info->sharemode, info->access_mask); + + create1.in.desired_access = SEC_RIGHTS_FILE_ALL; + create1.in.alloc_size = 0; + create1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create1.in.share_access = + smb2_util_share_access(info->sharemode); + create1.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create1.in.create_options = 0; + create1.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create1.in.fname = fname; + create1.in.security_flags = 0; + create1.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create1.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + status = smb2_create(tree1, tctx, &create1); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + create2.in.desired_access = info->access_mask; + create2.in.alloc_size = 0; + create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create2.in.create_disposition = NTCREATEX_DISP_OPEN; + create2.in.create_options = 0; + create2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create2.in.fname = fname; + create2.in.security_flags = 0; + create2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + status = smb2_create(tree2, tctx, &create2); + expected_status = info->expect_ok ? + NT_STATUS_OK : NT_STATUS_SHARING_VIOLATION; + torture_assert_ntstatus_equal_goto(tctx, status, + expected_status, ret, + done, "Unexpected status on " + "second create.\n"); + + status = smb2_util_close(tree1, create1.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to close " + "first handle.\n"); + + if (info->expect_ok) { + status = smb2_util_close(tree2, create2.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to close " + "second handle.\n"); + } + } + +done: + smb2_util_unlink(tree1, fname); + return ret; +} + +/* + * Test conflicting sharemodes through SMB2: First open file with + * different access masks, second open requests potentially conflicting + * sharemode. + */ +static bool test_smb2_access_sharemode(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = "test_sharemode"; + NTSTATUS status; + bool ret = true; + int i; + + for (i = 0; i < ARRAY_SIZE(sharemode_table); i++) { + struct sharemode_info *info = &sharemode_table[i]; + struct smb2_create create1 = { 0 }, create2 = { 0 }; + NTSTATUS expected_status; + + torture_comment(tctx, "index %3d, access mask 0x%06x, " + "sharemode %3s\n", + i, info->access_mask, info->sharemode); + + create1.in.desired_access = info->access_mask; + create1.in.alloc_size = 0; + create1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create1.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + create1.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create1.in.create_options = 0; + create1.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create1.in.fname = fname; + create1.in.security_flags = 0; + create1.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create1.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + status = smb2_create(tree1, tctx, &create1); + + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + create2.in.desired_access = SEC_RIGHTS_FILE_ALL; + create2.in.alloc_size = 0; + create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create2.in.share_access = + smb2_util_share_access(info->sharemode); + create2.in.create_disposition = NTCREATEX_DISP_OPEN; + create2.in.create_options = 0; + create2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create2.in.fname = fname; + create2.in.security_flags = 0; + create2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + create2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + + status = smb2_create(tree2, tctx, &create2); + + expected_status = info->expect_ok ? + NT_STATUS_OK : NT_STATUS_SHARING_VIOLATION; + torture_assert_ntstatus_equal_goto(tctx, status, + expected_status, ret, + done, "Unexpected status on " + "second create.\n"); + + status = smb2_util_close(tree1, create1.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to close " + "first handle.\n"); + + if (info->expect_ok) { + status = smb2_util_close(tree2, create2.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Failed to close " + "second handle.\n"); + } + } + +done: + smb2_util_unlink(tree1, fname); + return ret; +} + +/* + * Test initial stat open with share nothing doesn't trigger SHARING_VIOLTION + * errors. + */ +static bool test_smb2_bug14375(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "test_bug14375"; + struct smb2_create cr1; + struct smb2_create cr2; + struct smb2_create cr3; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(tree, fname); + + cr1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_ATTRIBUTE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_NONE, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + cr2 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + cr3 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + status = smb2_util_close(tree, cr1.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE file failed\n"); + status = smb2_util_close(tree, cr2.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE file failed\n"); + status = smb2_util_close(tree, cr3.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CLOSE file failed\n"); + + cr1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + cr2 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_ATTRIBUTE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_NONE, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + + cr3 = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, tctx, &cr3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "CREATE file failed\n"); + +done: + smb2_util_close(tree, cr1.out.file.handle); + smb2_util_close(tree, cr2.out.file.handle); + smb2_util_close(tree, cr3.out.file.handle); + smb2_util_unlink(tree, fname); + return ret; +} + +struct torture_suite *torture_smb2_sharemode_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "sharemode"); + + torture_suite_add_2smb2_test(suite, "sharemode-access", + test_smb2_sharemode_access); + torture_suite_add_2smb2_test(suite, "access-sharemode", + test_smb2_access_sharemode); + torture_suite_add_1smb2_test(suite, "bug14375", + test_smb2_bug14375); + + suite->description = talloc_strdup(suite, "SMB2-SHAREMODE tests"); + + return suite; +} diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c new file mode 100644 index 0000000..5b6477e --- /dev/null +++ b/source4/torture/smb2/smb2.c @@ -0,0 +1,230 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" + +#include "torture/smbtorture.h" +#include "torture/smb2/proto.h" +#include "../lib/util/dlinklist.h" + +static bool wrap_simple_1smb2_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smb2_tree *); + bool ret; + struct smb2_tree *tree1; + TALLOC_CTX *mem_ctx = talloc_new(torture_ctx); + + if (!torture_smb2_connection(torture_ctx, &tree1)) { + torture_fail(torture_ctx, + "Establishing SMB2 connection failed\n"); + return false; + } + + /* + * This is a trick: + * The test might close the connection. If we steal the tree context + * before that and free the parent instead of tree directly, we avoid + * a double free error. + */ + talloc_steal(mem_ctx, tree1); + + fn = test->fn; + + ret = fn(torture_ctx, tree1); + + talloc_free(mem_ctx); + + return ret; +} + +struct torture_test *torture_suite_add_1smb2_test(struct torture_suite *suite, + const char *name, + bool (*run)(struct torture_context *, + struct smb2_tree *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_1smb2_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + + +static bool wrap_simple_2smb2_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smb2_tree *, struct smb2_tree *); + bool ret = false; + + struct smb2_tree *tree1; + struct smb2_tree *tree2; + TALLOC_CTX *mem_ctx = talloc_new(torture_ctx); + + if (!torture_smb2_connection(torture_ctx, &tree1)) { + torture_fail(torture_ctx, + "Establishing SMB2 connection failed\n"); + goto done; + } + + talloc_steal(mem_ctx, tree1); + + if (!torture_smb2_connection(torture_ctx, &tree2)) { + torture_fail(torture_ctx, + "Establishing SMB2 connection failed\n"); + goto done; + } + + talloc_steal(mem_ctx, tree2); + + fn = test->fn; + + ret = fn(torture_ctx, tree1, tree2); + +done: + /* the test may already have closed some of the connections */ + talloc_free(mem_ctx); + + return ret; +} + + +struct torture_test *torture_suite_add_2smb2_test(struct torture_suite *suite, + const char *name, + bool (*run)(struct torture_context *, + struct smb2_tree *, + struct smb2_tree *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_2smb2_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + +NTSTATUS torture_smb2_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "smb2"); + torture_suite_add_simple_test(suite, "connect", torture_smb2_connect); + torture_suite_add_suite(suite, torture_smb2_scan_init(suite)); + torture_suite_add_suite(suite, torture_smb2_getinfo_init(suite)); + torture_suite_add_simple_test(suite, "setinfo", torture_smb2_setinfo); + torture_suite_add_suite(suite, torture_smb2_lock_init(suite)); + torture_suite_add_suite(suite, torture_smb2_read_init(suite)); + torture_suite_add_suite(suite, torture_smb2_aio_delay_init(suite)); + torture_suite_add_suite(suite, torture_smb2_bench_init(suite)); + torture_suite_add_suite(suite, torture_smb2_create_init(suite)); + torture_suite_add_suite(suite, torture_smb2_twrp_init(suite)); + torture_suite_add_suite(suite, torture_smb2_fileid_init(suite)); + torture_suite_add_suite(suite, torture_smb2_acls_init(suite)); + torture_suite_add_suite(suite, torture_smb2_acls_non_canonical_init(suite)); + torture_suite_add_suite(suite, torture_smb2_notify_init(suite)); + torture_suite_add_suite(suite, torture_smb2_notify_inotify_init(suite)); + torture_suite_add_suite(suite, + torture_smb2_notify_disabled_init(suite)); + torture_suite_add_suite(suite, torture_smb2_durable_open_init(suite)); + torture_suite_add_suite(suite, + torture_smb2_durable_open_disconnect_init(suite)); + torture_suite_add_suite(suite, + torture_smb2_durable_v2_open_init(suite)); + torture_suite_add_suite(suite, + torture_smb2_durable_v2_delay_init(suite)); + torture_suite_add_suite(suite, torture_smb2_dir_init(suite)); + torture_suite_add_suite(suite, torture_smb2_lease_init(suite)); + torture_suite_add_suite(suite, torture_smb2_compound_init(suite)); + torture_suite_add_suite(suite, torture_smb2_compound_find_init(suite)); + torture_suite_add_suite(suite, torture_smb2_compound_async_init(suite)); + torture_suite_add_suite(suite, torture_smb2_oplocks_init(suite)); + torture_suite_add_suite(suite, torture_smb2_kernel_oplocks_init(suite)); + torture_suite_add_suite(suite, torture_smb2_streams_init(suite)); + torture_suite_add_suite(suite, torture_smb2_ioctl_init(suite)); + torture_suite_add_simple_test(suite, "set-sparse-ioctl", + test_ioctl_set_sparse); + torture_suite_add_simple_test(suite, "zero-data-ioctl", + test_ioctl_zero_data); + torture_suite_add_simple_test(suite, "ioctl-on-stream", + test_ioctl_alternate_data_stream); + torture_suite_add_suite(suite, torture_smb2_rename_init(suite)); + torture_suite_add_suite(suite, torture_smb2_sharemode_init(suite)); + torture_suite_add_1smb2_test(suite, "hold-oplock", test_smb2_hold_oplock); + torture_suite_add_suite(suite, torture_smb2_session_init(suite)); + torture_suite_add_suite(suite, torture_smb2_session_req_sign_init(suite)); + torture_suite_add_suite(suite, torture_smb2_replay_init(suite)); + torture_suite_add_simple_test(suite, "dosmode", torture_smb2_dosmode); + torture_suite_add_simple_test(suite, "async_dosmode", torture_smb2_async_dosmode); + torture_suite_add_simple_test(suite, "maxfid", torture_smb2_maxfid); + torture_suite_add_simple_test(suite, "hold-sharemode", + torture_smb2_hold_sharemode); + torture_suite_add_simple_test(suite, "check-sharemode", + torture_smb2_check_sharemode); + torture_suite_add_suite(suite, torture_smb2_crediting_init(suite)); + + torture_suite_add_suite(suite, torture_smb2_doc_init(suite)); + torture_suite_add_suite(suite, torture_smb2_multichannel_init(suite)); + torture_suite_add_suite(suite, torture_smb2_samba3misc_init(suite)); + torture_suite_add_suite(suite, torture_smb2_timestamps_init(suite)); + torture_suite_add_suite(suite, torture_smb2_timestamp_resolution_init(suite)); + torture_suite_add_1smb2_test(suite, "openattr", torture_smb2_openattrtest); + torture_suite_add_1smb2_test(suite, "winattr", torture_smb2_winattrtest); + torture_suite_add_1smb2_test(suite, "winattr2", torture_smb2_winattr2); + torture_suite_add_1smb2_test(suite, "sdread", torture_smb2_sdreadtest); + torture_suite_add_suite(suite, torture_smb2_readwrite_init(suite)); + torture_suite_add_suite(suite, torture_smb2_max_allowed(suite)); + torture_suite_add_1smb2_test(suite, "tcon", run_tcon_test); + torture_suite_add_1smb2_test(suite, "mkdir", torture_smb2_mkdir); + torture_suite_add_suite(suite, torture_smb2_name_mangling_init(suite)); + + torture_suite_add_suite(suite, torture_smb2_charset(suite)); + torture_suite_add_1smb2_test(suite, "secleak", torture_smb2_sec_leak); + torture_suite_add_1smb2_test(suite, "session-id", run_sessidtest); + torture_suite_add_suite(suite, torture_smb2_deny_init(suite)); + torture_suite_add_suite(suite, torture_smb2_ea(suite)); + torture_suite_add_suite(suite, torture_smb2_create_no_streams_init(suite)); + + suite->description = talloc_strdup(suite, "SMB2-specific tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/smb2/streams.c b/source4/torture/smb2/streams.c new file mode 100644 index 0000000..f18048f --- /dev/null +++ b/source4/torture/smb2/streams.c @@ -0,0 +1,2424 @@ +/* + Unix SMB/CIFS implementation. + + test alternate data streams + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +#include "smb_constants.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#include "system/filesys.h" +#include "system/locale.h" +#include "lib/util/tsort.h" + +#define DNAME "teststreams" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%d - should be %d\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%llu - should be %llu\n", \ + __location__, #v, (unsigned long long)v, \ + (unsigned long long)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STR(v, correct) do { \ + bool ok; \ + if ((v) && !(correct)) { \ + ok = false; \ + } else if (!(v) && (correct)) { \ + ok = false; \ + } else if (!(v) && !(correct)) { \ + ok = true; \ + } else if (strcmp((v), (correct)) == 0) { \ + ok = true; \ + } else { \ + ok = false; \ + } \ + if (!ok) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s='%s' - " \ + "should be '%s'\n", \ + __location__, #v, (v)?(v):"NULL", \ + (correct)?(correct):"NULL"); \ + ret = false; \ + }} while (0) + + +static int qsort_string(char * const *s1, char * const *s2) +{ + return strcmp(*s1, *s2); +} + +static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2) +{ + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + +static bool check_stream(struct torture_context *tctx, + struct smb2_tree *tree, + const char *location, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + const char *value) +{ + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read r; + NTSTATUS status; + const char *full_name; + + full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = full_name; + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + if (value == NULL) { + return true; + } else { + torture_comment(tctx, "Unable to open stream %s\n", + full_name); + return false; + } + } + + handle = create.out.file.handle; + if (value == NULL) { + return true; + } + + + ZERO_STRUCT(r); + r.in.file.handle = handle; + r.in.length = strlen(value)+11; + r.in.offset = 0; + + status = smb2_read(tree, tree, &r); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) Failed to read %lu bytes from " + "stream '%s'\n", location, (long)strlen(value), full_name); + return false; + } + + if (memcmp(r.out.data.data, value, strlen(value)) != 0) { + torture_comment(tctx, "(%s) Bad data in stream\n", location); + return false; + } + + smb2_util_close(tree, handle); + return true; +} + +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + unsigned int num_exp, + const char **exp, + struct smb2_handle h) +{ + union smb_fileinfo finfo; + NTSTATUS status; + unsigned int i; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char **exp_sort; + struct stream_struct *stream_sort; + bool ret = false; + + finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION; + finfo.generic.in.file.handle = h; + + status = smb2_getinfo_file(tree, tctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n", + __location__, nt_errstr(status)); + goto fail; + } + + if (finfo.stream_info.out.num_streams != num_exp) { + torture_comment(tctx, "(%s) expected %d streams, got %d\n", + __location__, num_exp, finfo.stream_info.out.num_streams); + goto fail; + } + + if (num_exp == 0) { + ret = true; + goto fail; + } + + exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + + if (exp_sort == NULL) { + goto fail; + } + + TYPESAFE_QSORT(exp_sort, num_exp, qsort_string); + + stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + + if (stream_sort == NULL) { + goto fail; + } + + TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream); + + for (i=0; i expected[%s]\n", + __location__, fname, isprint(i)?(char)i:' ', i, + isprint(i)?"":" (not printable)", + nt_errstr(expected)); + } + CHECK_STATUS(status, expected); + + talloc_free(path); + } + +done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +/* + test case insensitive stream names +*/ +static bool test_stream_names3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_fsinfo info; + const char *fname = DNAME "\\stream_names3.txt"; + const char *sname = NULL; + const char *snamel = NULL; + const char *snameu = NULL; + const char *sdname = NULL; + const char *sdnamel = NULL; + const char *sdnameu = NULL; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle hf = {{0}}; + struct smb2_handle hs = {{0}}; + struct smb2_handle hsl = {{0}}; + struct smb2_handle hsu = {{0}}; + struct smb2_handle hsd = {{0}}; + struct smb2_handle hsdl = {{0}}; + struct smb2_handle hsdu = {{0}}; + const char *streams[] = { "::$DATA", ":StreamName:$DATA", }; + + smb2_deltree(tree, DNAME); + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(info); + info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION; + info.generic.handle = h; + status = smb2_getinfo_fs(tree, tree, &info); + CHECK_STATUS(status, NT_STATUS_OK); + if (!(info.attribute_info.out.fs_attr & FILE_CASE_SENSITIVE_SEARCH)) { + torture_skip(tctx, "No FILE_CASE_SENSITIVE_SEARCH supported"); + } + + /* + * We create the following file: + * + * teststreams\\stream_names3.txt + * + * and add a stream named 'StreamName' + * + * Then we try to open the stream using the following names: + * + * teststreams\\stream_names3.txt:StreamName + * teststreams\\stream_names3.txt:streamname + * teststreams\\stream_names3.txt:STREAMNAME + * teststreams\\stream_names3.txt:StreamName:$dAtA + * teststreams\\stream_names3.txt:streamname:$data + * teststreams\\stream_names3.txt:STREAMNAME:$DATA + */ + sname = talloc_asprintf(tctx, "%s:StreamName", fname); + torture_assert_not_null(tctx, sname, __location__); + snamel = strlower_talloc(tctx, sname); + torture_assert_not_null(tctx, snamel, __location__); + snameu = strupper_talloc(tctx, sname); + torture_assert_not_null(tctx, snameu, __location__); + + sdname = talloc_asprintf(tctx, "%s:$dAtA", sname); + torture_assert_not_null(tctx, sdname, __location__); + sdnamel = strlower_talloc(tctx, sdname); + torture_assert_not_null(tctx, sdnamel, __location__); + sdnameu = strupper_talloc(tctx, sdname); + torture_assert_not_null(tctx, sdnameu, __location__); + + torture_comment(tctx, "(%s) testing case insensitive stream names\n", + __location__); + status = torture_smb2_testfile(tree, fname, &hf); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_testfile(tree, sname, &hs); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, hs); + + torture_assert(tctx, + check_stream_list(tree, tctx, fname, + ARRAY_SIZE(streams), + streams, + hf), + "streams"); + + status = torture_smb2_open(tree, sname, SEC_RIGHTS_FILE_ALL, &hs); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, snamel, SEC_RIGHTS_FILE_ALL, &hsl); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, snameu, SEC_RIGHTS_FILE_ALL, &hsu); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdname, SEC_RIGHTS_FILE_ALL, &hsd); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdnamel, SEC_RIGHTS_FILE_ALL, &hsdl); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdnameu, SEC_RIGHTS_FILE_ALL, &hsdu); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, hsdu); + smb2_util_close(tree, hsdl); + smb2_util_close(tree, hsd); + smb2_util_close(tree, hsu); + smb2_util_close(tree, hsl); + smb2_util_close(tree, hs); + smb2_util_close(tree, hf); + smb2_util_close(tree, h); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +#define CHECK_CALL_HANDLE(call, rightstatus) do { \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.handle = h1; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s - %s (should be %s)\n", \ + __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo1.generic.in.file.handle = h1; \ + status2 = smb2_getinfo_file(tree, tctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s pathinfo - %s\n", \ + __location__, #call, nt_errstr(status)); \ + ret = false; \ + }} while (0) + +/* + test stream renames +*/ +static bool test_stream_rename(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status, status2; + union smb_open io; + const char *fname = DNAME "\\stream_rename.txt"; + const char *sname1, *sname2; + union smb_fileinfo finfo1; + union smb_setfileinfo sfinfo; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h1 = {{0}}; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, + "Second Stream"); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "(%s) testing stream renames\n", __location__); + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + + /* Create two streams. */ + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + smb2_util_close(tree, h1); + + io.smb2.in.fname = sname2; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + smb2_util_close(tree, h1); + + /* + * Open the second stream. + */ + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Now rename the second stream onto the first. + */ + + ZERO_STRUCT(sfinfo); + + sfinfo.rename_information.in.overwrite = 1; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = ":Stream One"; + CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK); +done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_stream_rename2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname1 = DNAME "\\stream_rename2.txt"; + const char *fname2 = DNAME "\\stream2_rename2.txt"; + const char *stream_name1 = ":Stream One:$DATA"; + const char *stream_name2 = ":Stream Two:$DATA"; + const char *stream_name_default = "::$DATA"; + const char *sname1; + const char *sname2; + bool ret = true; + struct smb2_handle h, h1; + union smb_setfileinfo sinfo; + + ZERO_STRUCT(h); + ZERO_STRUCT(h1); + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two"); + + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_STD_DELETE | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + + /* Open/create new stream. */ + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * Reopen the stream for SMB2 renames. + */ + io.smb2.in.fname = sname1; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Check SMB2 rename of a stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using " + ":\n", __location__); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = 1; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = stream_name1; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check SMB2 rename of an overwriting stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting " + "stream using :\n", __location__); + + /* Create second stream. */ + io.smb2.in.fname = sname2; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, io.smb2.out.file.handle); + + /* Rename the first stream onto the second. */ + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = stream_name2; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h1); + + /* + * Reopen the stream with the new name. + */ + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.fname = sname2; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Check SMB2 rename of a stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using " + ":\n", __location__); + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = sname1; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + if (!torture_setting_bool(tctx, "samba4", false)) { + /* + * Check SMB2 rename to the default stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename to default stream " + "using :\n", __location__); + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = stream_name_default; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + } + + smb2_util_close(tree, h1); + + done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname1); + status = smb2_util_unlink(tree, fname2); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool create_file_with_stream(struct torture_context *tctx, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + const char *base_fname, + const char *stream) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + + /* Create a file with a stream */ + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = stream; + + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smb2_util_close(tree, io.smb2.out.file.handle); + return ret; +} + + +/* Test how streams interact with create dispositions */ +static bool test_stream_create_disposition(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream_create_disp.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + const char *default_stream_name = "::$DATA"; + const char *stream_list[2]; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle h1 = {{0}}; + + /* clean slate .. */ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream); + + stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream); + stream_list[1] = default_stream_name; + + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + /* Open the base file with OPEN */ + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + * check create open: sanity check + */ + torture_comment(tctx, "(%s) Checking create disp: open\n", + __location__); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 2, stream_list, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite\n", + __location__); + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite_if + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n", + __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream)) + goto done; + + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create supersede + */ + torture_comment(tctx, "(%s) Checking create disp: supersede\n", + __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite_if on a stream. + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite_if on " + "stream\n", __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.smb2.in.fname = fname_stream; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 2, stream_list, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool open_stream(struct smb2_tree *tree, + struct torture_context *mem_ctx, + const char *fname, + struct smb2_handle *h_out) +{ + NTSTATUS status; + union smb_open io; + + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_ATTRIBUTE; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &(io.smb2)); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + *h_out = io.smb2.out.file.handle; + return true; +} + + +/* Test the effect of setting attributes on a stream. */ +static bool test_stream_attributes1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + bool ret = true; + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream_attr.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + struct smb2_handle h, h1; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + time_t basetime = (time(NULL) - 86400) & ~1; + + ZERO_STRUCT(h); + ZERO_STRUCT(h1); + + torture_comment(tctx, "(%s) testing attribute setting on stream\n", + __location__); + + /* clean slate .. */ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream); + + /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */ + ret = create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream); + if (!ret) { + goto done; + } + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = io.smb2.out.file.handle; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) { + torture_comment(tctx, "(%s) Incorrect attrib %x - should be " + "%x\n", __location__, + (unsigned int)finfo.basic_info.out.attrib, + (unsigned int)FILE_ATTRIBUTE_ARCHIVE); + ret = false; + goto done; + } + + smb2_util_close(tree, io.smb2.out.file.handle); + /* Now open the stream name. */ + + if (!open_stream(tree, tctx, fname_stream, &h1)) { + goto done; + } + + /* Change the time on the stream. */ + ZERO_STRUCT(sfinfo); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime); + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "(%s) %s - %s (should be %s)\n", + __location__, "SETATTR", + nt_errstr(status), nt_errstr(NT_STATUS_OK)); + ret = false; + goto done; + } + + smb2_util_close(tree, h1); + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) %s pathinfo - %s\n", + __location__, "SETATTRE", nt_errstr(status)); + ret = false; + goto done; + } + + if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) { + torture_comment(tctx, "(%s) time incorrect.\n", __location__); + ret = false; + goto done; + } + smb2_util_close(tree, h1); + + if (!open_stream(tree, tctx, fname_stream, &h1)) { + goto done; + } + + /* Changing attributes on stream */ + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "(%s) %s - %s (should be %s)\n", + __location__, "SETATTR", + nt_errstr(status), nt_errstr(NT_STATUS_OK)); + ret = false; + goto done; + } + + smb2_util_close(tree, h1); + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool check_metadata(struct torture_context *tctx, + struct smb2_tree *tree, + const char *path, + struct smb2_handle _h, + NTTIME expected_btime, + uint32_t expected_attribs) +{ + struct smb2_handle h = _h; + union smb_fileinfo getinfo; + NTSTATUS status; + bool ret = true; + + if (smb2_util_handle_empty(h)) { + struct smb2_create c; + + c = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_HIDDEN, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = path, + }; + status = smb2_create(tree, tctx, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + h = c.out.file.handle; + } + + getinfo = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = h, + }; + + status = smb2_getinfo_file(tree, tctx, &getinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_assert_u64_equal_goto(tctx, + expected_btime, + getinfo.basic_info.out.create_time, + ret, done, + "btime was updated\n"); + + torture_assert_u32_equal_goto(tctx, + expected_attribs, + getinfo.basic_info.out.attrib, + ret, done, + "btime was updated\n"); + +done: + if (smb2_util_handle_empty(_h)) { + smb2_util_close(tree, h); + } + + return ret; +} + +static bool test_stream_attributes2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + struct smb2_create c1; + struct smb2_handle h1 = {{0}}; + const char *fname = DNAME "\\test_stream_btime"; + const char *sname = DNAME "\\test_stream_btime:stream"; + union smb_fileinfo getinfo; + union smb_setfileinfo setinfo; + const char *data = "test data"; + struct timespec ts; + NTTIME btime; + uint32_t attrib = FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE; + bool ret; + + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, h1); + + torture_comment(tctx, "Let's dance!\n"); + + /* + * Step 1: create file and get creation date + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_HIDDEN, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + getinfo = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tctx, &getinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + btime = getinfo.basic_info.out.create_time; + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + /* + * Step X: write to file, assert btime was not updated + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + status = smb2_util_write(tree, h1, data, 0, strlen(data)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + ret = check_metadata(tctx, tree, NULL, h1, btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + /* + * Step X: create stream, assert creation date is the same + * as the one on the basefile + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = sname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + /* + * Step X: set btime on stream, verify basefile has the same btime. + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = sname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + setinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + }; + clock_gettime_mono(&ts); + btime = setinfo.basic_info.in.create_time = full_timespec_to_nt_time(&ts); + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + ret = check_metadata(tctx, tree, NULL, h1, btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad time on stream\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad time on basefile\n"); + + ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad time on stream\n"); + + /* + * Step X: write to stream, assert btime was not updated + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = sname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + status = smb2_util_write(tree, h1, data, 0, strlen(data)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + ret = check_metadata(tctx, tree, NULL, h1, btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + /* + * Step X: modify attributes via stream, verify it's "also" set on the + * basefile. + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = sname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + attrib = FILE_ATTRIBUTE_NORMAL; + + setinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + .basic_info.in.attrib = attrib, + }; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + /* + * Step X: modify attributes via basefile, verify it's "also" set on the + * stream. + */ + + c1 = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = attrib, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION, + .in.fname = fname, + }; + status = smb2_create(tree, tctx, &c1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = c1.out.file.handle; + + attrib = FILE_ATTRIBUTE_HIDDEN; + + setinfo = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = h1, + .basic_info.in.attrib = attrib, + }; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(h1); + + ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + + ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}}, + btime, attrib); + torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + + smb2_deltree(tree, DNAME); + + return ret; +} + +static bool test_basefile_rename_with_open_stream(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_tree *tree2 = NULL; + struct smb2_create create, create2; + struct smb2_handle h1 = {{0}}, h2 = {{0}}; + const char *fname = "test_rename_openfile"; + const char *sname = "test_rename_openfile:foo"; + const char *fname_renamed = "test_rename_openfile_renamed"; + union smb_setfileinfo sinfo; + const char *data = "test data"; + + ret = torture_smb2_connection(tctx, &tree2); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_smb2_connection failed\n"); + + torture_comment(tctx, "Creating file with stream\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_ALL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + h1 = create.out.file.handle; + + torture_comment(tctx, "Writing to stream\n"); + + status = smb2_util_write(tree, h1, data, 0, strlen(data)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + torture_comment(tctx, "Renaming base file\n"); + + ZERO_STRUCT(create2); + create2.in.desired_access = SEC_FILE_ALL; + create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create2.in.create_disposition = NTCREATEX_DISP_OPEN; + create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create2.in.fname = fname; + + status = smb2_create(tree2, tctx, &create2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + h2 = create2.out.file.handle; + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h2; + sinfo.rename_information.in.new_name = fname_renamed; + + status = smb2_setinfo_file(tree2, &sinfo); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_ACCESS_DENIED, ret, done, + "smb2_setinfo_file didn't return NT_STATUS_ACCESS_DENIED\n"); + + smb2_util_close(tree2, h2); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree2, h2); + } + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname_renamed); + + return ret; +} + +/* + basic testing of streams calls SMB2 +*/ +struct torture_suite *torture_smb2_streams_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "streams"); + + torture_suite_add_1smb2_test(suite, "dir", test_stream_dir); + torture_suite_add_1smb2_test(suite, "io", test_stream_io); + torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes); + torture_suite_add_1smb2_test(suite, "names", test_stream_names); + torture_suite_add_1smb2_test(suite, "names2", test_stream_names2); + torture_suite_add_1smb2_test(suite, "names3", test_stream_names3); + torture_suite_add_1smb2_test(suite, "rename", test_stream_rename); + torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2); + torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition); + torture_suite_add_1smb2_test(suite, "attributes1", test_stream_attributes1); + torture_suite_add_1smb2_test(suite, "attributes2", test_stream_attributes2); + torture_suite_add_1smb2_test(suite, "delete", test_stream_delete); + torture_suite_add_1smb2_test(suite, "zero-byte", test_zero_byte_stream); + torture_suite_add_1smb2_test(suite, "basefile-rename-with-open-stream", + test_basefile_rename_with_open_stream); + + suite->description = talloc_strdup(suite, "SMB2-STREAM tests"); + return suite; +} diff --git a/source4/torture/smb2/tcon.c b/source4/torture/smb2/tcon.c new file mode 100644 index 0000000..1e658af --- /dev/null +++ b/source4/torture/smb2/tcon.c @@ -0,0 +1,146 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + Copyright (C) David Mulder 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/smbtorture.h" +#include "torture/smb2/proto.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/util.h" +#include "system/filesys.h" +#include "system/time.h" +#include "libcli/resolve/resolve.h" +#include "lib/events/events.h" +#include "param/param.h" + +static void smb2cli_session_set_id(struct smbXcli_session *session, + uint64_t session_id) +{ + smb2cli_session_set_id_and_flags(session, session_id, + smb2cli_session_get_flags(session)); +} + +/** + this checks to see if a secondary tconx can use open files from an + earlier tconx + */ +bool run_tcon_test(struct torture_context *tctx, struct smb2_tree *tree) +{ + const char *fname = "tcontest.tmp"; + struct smb2_handle fnum1; + uint32_t cnum1, cnum2, cnum3; + uint64_t sessid1, sessid2; + uint8_t buf[4]; + bool ret = true; + struct smb2_tree *tree1 = NULL; + const char *host = torture_setting_string(tctx, "host", NULL); + struct smb2_create io = {0}; + NTSTATUS status; + bool ok; + + if (smb2_deltree(tree, fname) == -1) { + torture_comment(tctx, "unlink of %s failed\n", fname); + } + + io.in.fname = fname; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, tree, &io); + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, TORTURE_FAIL, "open of %s failed (%s)\n", fname, nt_errstr(status)); + return false; + } + fnum1 = io.out.file.handle; + + cnum1 = smb2cli_tcon_current_id(tree->smbXcli); + sessid1 = smb2cli_session_current_id(tree->session->smbXcli); + + memset(buf, 0, 4); /* init buf so valgrind won't complain */ + status = smb2_util_write(tree, fnum1, buf, 130, 4); + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, TORTURE_FAIL, "initial write failed (%s)\n", nt_errstr(status)); + return false; + } + + ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree1); + if (!ok) { + torture_result(tctx, TORTURE_FAIL, "%s refused 2nd tree connect\n", host); + return false; + } + + cnum2 = smb2cli_tcon_current_id(tree1->smbXcli); + cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */ + sessid2 = smb2cli_session_current_id(tree1->session->smbXcli) + 1; + + /* try a write with the wrong tid */ + smb2cli_tcon_set_id(tree1->smbXcli, cnum2); + + status = smb2_util_write(tree1, fnum1, buf, 130, 4); + if (NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with wrong TID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with wrong TID : %s\n", nt_errstr(status)); + } + + + /* try a write with an invalid tid */ + smb2cli_tcon_set_id(tree1->smbXcli, cnum3); + + status = smb2_util_write(tree1, fnum1, buf, 130, 4); + if (NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with invalid TID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with invalid TID : %s\n", nt_errstr(status)); + } + + /* try a write with an invalid session id */ + smb2cli_session_set_id(tree1->session->smbXcli, sessid2); + smb2cli_tcon_set_id(tree1->smbXcli, cnum1); + + status = smb2_util_write(tree1, fnum1, buf, 130, 4); + if (NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, "* server allows write with invalid VUID\n"); + ret = false; + } else { + torture_comment(tctx, "server fails write with invalid VUID : %s\n", nt_errstr(status)); + } + + smb2cli_session_set_id(tree1->session->smbXcli, sessid1); + smb2cli_tcon_set_id(tree1->smbXcli, cnum1); + + status = smb2_util_close(tree1, fnum1); + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, TORTURE_FAIL, "close failed (%s)\n", nt_errstr(status)); + return false; + } + + smb2cli_tcon_set_id(tree1->smbXcli, cnum2); + + smb2_util_unlink(tree1, fname); + + return ret; +} diff --git a/source4/torture/smb2/timestamps.c b/source4/torture/smb2/timestamps.c new file mode 100644 index 0000000..3d6d3d1 --- /dev/null +++ b/source4/torture/smb2/timestamps.c @@ -0,0 +1,1344 @@ +/* + Unix SMB/CIFS implementation. + + test timestamps + + Copyright (C) Ralph Boehme 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" + +#define BASEDIR "smb2-timestamps" +#define FNAME "testfile.dat" + +static bool test_close_no_attrib(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *filename = BASEDIR "/" FNAME; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + struct smb2_handle testdirh = {{0}}; + struct smb2_close c; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, testdirh); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = filename, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + c = (struct smb2_close) { + .in.file.handle = handle, + }; + + status = smb2_close(tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(handle); + + torture_assert_u64_equal_goto(tctx, c.out.create_time, NTTIME_OMIT, + ret, done, "Unexpected create time\n"); + torture_assert_u64_equal_goto(tctx, c.out.access_time, NTTIME_OMIT, + ret, done, "Unexpected access time\n"); + torture_assert_u64_equal_goto(tctx, c.out.write_time, NTTIME_OMIT, + ret, done, "Unexpected write time\n"); + torture_assert_u64_equal_goto(tctx, c.out.change_time, NTTIME_OMIT, + ret, done, "Unexpected change time\n"); + torture_assert_u64_equal_goto(tctx, c.out.size, 0, + ret, done, "Unexpected size\n"); + torture_assert_u64_equal_goto(tctx, c.out.file_attr, 0, + ret, done, "Unexpected attributes\n"); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_time_t(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + time_t t) +{ + char *filename = NULL; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + struct smb2_handle testdirh = {{0}}; + struct timespec ts = { .tv_sec = t }; + uint64_t nttime; + union smb_fileinfo gi; + union smb_setfileinfo si; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + filename = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fname); + torture_assert_not_null_goto(tctx, filename, ret, done, + "talloc_asprintf failed\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = filename, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + si = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = handle, + }; + + nttime = full_timespec_to_nt_time(&ts); + si.basic_info.in.create_time = nttime; + si.basic_info.in.write_time = nttime; + si.basic_info.in.change_time = nttime; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n", + nt_time_string(tctx, gi.basic_info.out.create_time), + nt_time_string(tctx, gi.basic_info.out.write_time), + nt_time_string(tctx, gi.basic_info.out.change_time)); + + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.change_time, + ret, done, + "Wrong change time\n"); + + find = (struct smb2_find) { + .in.file.handle = testdirh, + .in.pattern = fname, + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.change_time, + ret, done, + "Wrong change time\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = filename, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n", + nt_time_string(tctx, gi.basic_info.out.create_time), + nt_time_string(tctx, gi.basic_info.out.write_time), + nt_time_string(tctx, gi.basic_info.out.change_time)); + + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.change_time, + ret, done, + "Wrong change time\n"); + + find = (struct smb2_find) { + .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART, + .in.file.handle = testdirh, + .in.pattern = fname, + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + d[0].id_both_directory_info.change_time, + ret, done, + "Wrong change time\n"); + + status = smb2_util_close(tree, handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + ZERO_STRUCT(handle); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + if (!smb2_util_handle_empty(testdirh)) { + smb2_util_close(tree, testdirh); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_time_t_15032385535(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_15032385535.txt", + 15032385535 /* >> INT32_MAX, limit on ext */); +} + +static bool test_time_t_10000000000(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_10000000000.txt", + 10000000000 /* >> INT32_MAX */); +} + +static bool test_time_t_4294967295(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_4294967295.txt", + 4294967295 /* INT32_MAX */); +} + +static bool test_time_t_1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_1.txt", 1); +} + +static bool test_time_t_0(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_0.txt", 0); +} + +static bool test_time_t_minus_1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_-1.txt", -1); +} + +static bool test_time_t_minus_2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_-2.txt", -2); +} + +static bool test_time_t_1968(struct torture_context *tctx, + struct smb2_tree *tree) +{ + return test_time_t(tctx, tree, "test_time_t_1968.txt", + -63158400 /* 1968 */); +} + +static bool test_freeze_thaw(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *filename = BASEDIR "\\test_freeze_thaw"; + struct smb2_create cr; + struct smb2_handle handle = {{0}}; + struct smb2_handle testdirh = {{0}}; + struct timespec ts = { .tv_sec = time(NULL) }; + uint64_t nttime; + union smb_fileinfo gi; + union smb_setfileinfo si; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, + .in.fname = filename, + }; + + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + handle = cr.out.file.handle; + + si = (union smb_setfileinfo) { + .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION, + .basic_info.in.file.handle = handle, + }; + + /* + * Step 1: + * First set timestamps of testfile to current time + */ + + nttime = full_timespec_to_nt_time(&ts); + si.basic_info.in.create_time = nttime; + si.basic_info.in.write_time = nttime; + si.basic_info.in.change_time = nttime; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + /* + * Step 2: + * Verify timestamps are indeed set to the value in "nttime". + */ + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n", + nt_time_string(tctx, gi.basic_info.out.create_time), + nt_time_string(tctx, gi.basic_info.out.write_time), + nt_time_string(tctx, gi.basic_info.out.change_time)); + + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.change_time, + ret, done, + "Wrong change time\n"); + + /* + * Step 3: + * First set timestamps with NTTIME_FREEZE, must not change any + * timestamp value. + */ + + si.basic_info.in.create_time = NTTIME_FREEZE; + si.basic_info.in.write_time = NTTIME_FREEZE; + si.basic_info.in.change_time = NTTIME_FREEZE; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + /* + * Step 4: + * Verify timestamps are unmodified from step 2. + */ + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n", + nt_time_string(tctx, gi.basic_info.out.create_time), + nt_time_string(tctx, gi.basic_info.out.write_time), + nt_time_string(tctx, gi.basic_info.out.change_time)); + + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.change_time, + ret, done, + "Wrong change time\n"); + + /* + * Step 5: + * First set timestamps with NTTIME_THAW, must not change any timestamp + * value. + */ + + si.basic_info.in.create_time = NTTIME_THAW; + si.basic_info.in.write_time = NTTIME_THAW; + si.basic_info.in.change_time = NTTIME_THAW; + + status = smb2_setinfo_file(tree, &si); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed\n"); + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + /* + * Step 6: + * Verify timestamps are unmodified from step 2. + */ + + gi = (union smb_fileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + .generic.in.file.handle = handle, + }; + + status = smb2_getinfo_file(tree, tctx, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n", + nt_time_string(tctx, gi.basic_info.out.create_time), + nt_time_string(tctx, gi.basic_info.out.write_time), + nt_time_string(tctx, gi.basic_info.out.change_time)); + + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.create_time, + ret, done, + "Wrong create time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.write_time, + ret, done, + "Wrong write time\n"); + torture_assert_u64_equal_goto(tctx, + nttime, + gi.basic_info.out.change_time, + ret, done, + "Wrong change time\n"); + +done: + if (!smb2_util_handle_empty(handle)) { + smb2_util_close(tree, handle); + } + if (!smb2_util_handle_empty(testdirh)) { + smb2_util_close(tree, testdirh); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_delayed_write_vs_seteof(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + struct smb2_handle h2 = {{0}}; + NTTIME create_time; + NTTIME set_time; + union smb_fileinfo finfo; + union smb_setfileinfo setinfo; + struct smb2_close c; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Open file-handle 1\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = BASEDIR "\\" FNAME, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h1 = cr.out.file.handle; + create_time = cr.out.create_time; + sleep(1); + + torture_comment(tctx, "Write to file-handle 1\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + torture_comment(tctx, "Check writetime hasn't been updated\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + torture_assert_nttime_equal(tctx, + finfo.all_info.out.write_time, + create_time, + "Writetime != set_time (wrong!)\n"); + + torture_comment(tctx, "Setinfo EOF on file-handle 1," + " should flush pending writetime update\n"); + + setinfo = (union smb_setfileinfo) { + .generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION, + }; + setinfo.end_of_file_info.in.file.handle = h1; + setinfo.end_of_file_info.in.size = 1; /* same size! */ + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Check writetime has been updated " + "by the setinfo EOF\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + if (!(finfo.all_info.out.write_time > create_time)) { + ret = false; + torture_fail_goto(tctx, done, "setinfo EOF hasn't updated writetime\n"); + } + + torture_comment(tctx, "Open file-handle 2\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = BASEDIR "\\" FNAME, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h2 = cr.out.file.handle; + + torture_comment(tctx, "Set write time on file-handle 2\n"); + + setinfo = (union smb_setfileinfo) { + .generic.level = SMB_QFILEINFO_BASIC_INFORMATION, + }; + setinfo.generic.in.file.handle = h2; + unix_to_nt_time(&set_time, time(NULL) + 86400); + setinfo.basic_info.in.write_time = set_time; + + status = smb2_setinfo_file(tree, &setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + status = smb2_util_close(tree, h2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h2); + + torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n"); + + c = (struct smb2_close) { + .in.file.handle = h1, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h1); + + torture_assert_nttime_equal(tctx, + c.out.write_time, + set_time, + "Writetime != set_time (wrong!)\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(tree, h2); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_delayed_write_vs_flush(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + union smb_fileinfo finfo; + struct smb2_flush f; + struct smb2_close c; + NTTIME create_time; + NTTIME flush_time; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Open file-handle 1\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = BASEDIR "\\" FNAME, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h1 = cr.out.file.handle; + create_time = cr.out.create_time; + sleep(1); + + torture_comment(tctx, "Write to file-handle 1\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + torture_comment(tctx, "Check writetime hasn't been updated\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + torture_assert_nttime_equal(tctx, + finfo.all_info.out.write_time, + create_time, + "Writetime != create_time (wrong!)\n"); + + torture_comment(tctx, "Flush file, " + "should flush pending writetime update\n"); + + f = (struct smb2_flush) { + .in.file.handle = h1, + }; + + status = smb2_flush(tree, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "flush failed\n"); + + torture_comment(tctx, "Check writetime has been updated " + "by the setinfo EOF\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + flush_time = finfo.all_info.out.write_time; + if (!(flush_time > create_time)) { + ret = false; + torture_fail_goto(tctx, done, "flush hasn't updated writetime\n"); + } + + torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n"); + + c = (struct smb2_close) { + .in.file.handle = h1, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h1); + + torture_assert_nttime_equal(tctx, + c.out.write_time, + flush_time, + "writetime != flushtime (wrong!)\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_delayed_write_vs_setbasic_do(struct torture_context *tctx, + struct smb2_tree *tree, + union smb_setfileinfo *setinfo, + bool expect_update) +{ + char *path = NULL; + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + NTTIME create_time; + union smb_fileinfo finfo; + NTSTATUS status; + bool ret = true; + + torture_comment(tctx, "Create testfile\n"); + + path = talloc_asprintf(tree, BASEDIR "\\" FNAME ".%" PRIu32, + generate_random()); + torture_assert_not_null_goto(tctx, path, ret, done, "OOM\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = path, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h1 = cr.out.file.handle; + create_time = cr.out.create_time; + + torture_comment(tctx, "Write to file\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + torture_comment(tctx, "Get timestamps\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + torture_assert_nttime_equal(tctx, + finfo.all_info.out.write_time, + create_time, + "Writetime != create_time (wrong!)\n"); + + torture_comment(tctx, "Set timestamps\n"); + + setinfo->end_of_file_info.in.file.handle = h1; + status = smb2_setinfo_file(tree, setinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Check timestamps\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + if (expect_update) { + if (!(finfo.all_info.out.write_time > create_time)) { + ret = false; + torture_fail_goto(tctx, done, "setinfo basicinfo " + "hasn't updated writetime\n"); + } + } else { + if (finfo.all_info.out.write_time != create_time) { + ret = false; + torture_fail_goto(tctx, done, "setinfo basicinfo " + "hasn't updated writetime\n"); + } + } + + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h1); + + status = smb2_util_unlink(tree, path); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + +done: + TALLOC_FREE(path); + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + return ret; +} + +static bool test_delayed_write_vs_setbasic(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle h1 = {{0}}; + union smb_setfileinfo setinfo; + time_t t = time(NULL) - 86400; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + /* + * Yes, this is correct, tested against Windows 2016: even if all + * timestamp fields are 0, a pending write time is flushed. + */ + torture_comment(tctx, "Test: setting all-0 timestamps flushes?\n"); + + setinfo = (union smb_setfileinfo) { + .generic.level = RAW_SFILEINFO_BASIC_INFORMATION, + }; + ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true); + if (ret != true) { + goto done; + } + + torture_comment(tctx, "Test: setting create_time flushes?\n"); + unix_to_nt_time(&setinfo.basic_info.in.create_time, t); + ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true); + if (ret != true) { + goto done; + } + + torture_comment(tctx, "Test: setting access_time flushes?\n"); + unix_to_nt_time(&setinfo.basic_info.in.access_time, t); + ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true); + if (ret != true) { + goto done; + } + + torture_comment(tctx, "Test: setting change_time flushes?\n"); + unix_to_nt_time(&setinfo.basic_info.in.change_time, t); + ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true); + if (ret != true) { + goto done; + } + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_delayed_1write(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + union smb_fileinfo finfo; + struct smb2_close c; + NTTIME create_time; + NTTIME write_time; + NTTIME close_time; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Open file-handle 1\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = BASEDIR "\\" FNAME, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h1 = cr.out.file.handle; + create_time = cr.out.create_time; + sleep(1); + + torture_comment(tctx, "Write to file-handle 1\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + sleep(3); + + torture_comment(tctx, "Check writetime has been updated\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + write_time = finfo.all_info.out.write_time; + + if (!(write_time > create_time)) { + ret = false; + torture_fail_goto(tctx, done, + "Write-time not updated (wrong!)\n"); + } + + torture_comment(tctx, "Close file-handle 1\n"); + sleep(1); + + c = (struct smb2_close) { + .in.file.handle = h1, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h1); + close_time = c.out.write_time; + + torture_assert_nttime_equal(tctx, close_time, write_time, + "Writetime != close_time (wrong!)\n"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +static bool test_delayed_2write(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_create cr; + struct smb2_handle h1 = {{0}}; + union smb_fileinfo finfo; + struct smb2_close c; + NTTIME create_time; + NTTIME write_time; + NTTIME write_time2; + NTTIME close_time; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Open file\n"); + + cr = (struct smb2_create) { + .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = BASEDIR "\\" FNAME, + }; + status = smb2_create(tree, tctx, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h1 = cr.out.file.handle; + create_time = cr.out.create_time; + sleep(1); + + torture_comment(tctx, "Write to file\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + sleep(3); + + torture_comment(tctx, "Check writetime has been updated\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + write_time = finfo.all_info.out.write_time; + + if (!(write_time > create_time)) { + ret = false; + torture_fail_goto(tctx, done, + "Write-time not updated (wrong!)\n"); + } + + torture_comment(tctx, "Write a second time\n"); + + status = smb2_util_write(tree, h1, "s", 0, 1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + sleep(3); + + torture_comment(tctx, "Check writetime has NOT been updated\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + write_time2 = finfo.all_info.out.write_time; + + torture_assert_nttime_equal(tctx, write_time2, write_time, + "second write updated write-time (wrong!)\n"); + + torture_comment(tctx, "Close file-handle 1\n"); + sleep(2); + + torture_comment(tctx, "Check writetime has been updated\n"); + + c = (struct smb2_close) { + .in.file.handle = h1, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h1); + close_time = c.out.write_time; + + if (!(close_time > write_time)) { + ret = false; + torture_fail_goto(tctx, done, + "Write-time not updated (wrong!)\n"); + } + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + basic testing of SMB2 timestamps +*/ +struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "timestamps"); + + torture_suite_add_1smb2_test(suite, "test_close_not_attrib", test_close_no_attrib); + torture_suite_add_1smb2_test(suite, "time_t_15032385535", test_time_t_15032385535); + torture_suite_add_1smb2_test(suite, "time_t_10000000000", test_time_t_10000000000); + torture_suite_add_1smb2_test(suite, "time_t_4294967295", test_time_t_4294967295); + torture_suite_add_1smb2_test(suite, "time_t_1", test_time_t_1); + torture_suite_add_1smb2_test(suite, "time_t_0", test_time_t_0); + torture_suite_add_1smb2_test(suite, "time_t_-1", test_time_t_minus_1); + torture_suite_add_1smb2_test(suite, "time_t_-2", test_time_t_minus_2); + torture_suite_add_1smb2_test(suite, "time_t_1968", test_time_t_1968); + torture_suite_add_1smb2_test(suite, "freeze-thaw", test_freeze_thaw); + + /* + * Testing of delayed write-time updates + */ + torture_suite_add_1smb2_test(suite, "delayed-write-vs-seteof", test_delayed_write_vs_seteof); + torture_suite_add_1smb2_test(suite, "delayed-write-vs-flush", test_delayed_write_vs_flush); + torture_suite_add_1smb2_test(suite, "delayed-write-vs-setbasic", test_delayed_write_vs_setbasic); + torture_suite_add_1smb2_test(suite, "delayed-1write", test_delayed_1write); + torture_suite_add_1smb2_test(suite, "delayed-2write", test_delayed_2write); + + suite->description = talloc_strdup(suite, "SMB2 timestamp tests"); + + return suite; +} + +/* + * This test shows that Windows has a timestamp resolution of ~15ms. When so + * when a smaller amount of time than that has passed it's not necessarily + * detectable on a Windows 2019 and newer who implement immediate timestamp + * updates. + * + * Note that this test relies on a low latency SMB connection. Even with a low + * latency connection of eg 1m there's a chance of 1/15 that the first part of + * the test expecting no timestamp change fails as the writetime is updated. + * + * Due to this timing dependency this test is skipped in Samba CI, but it is + * preserved here for future SMB2 timestamps behaviour archealogists. + * + * See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html + */ +static bool test_timestamp_resolution1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + union smb_fileinfo finfo1; + const char *fname = BASEDIR "\\" FNAME; + struct smb2_create cr; + struct smb2_handle h = {{0}}; + struct smb2_close cl; + NTSTATUS status; + bool ret = true; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + status = smb2_util_close(tree, h ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + + torture_comment(tctx, "Write without delay, expect no " + "write-time change\n"); + + smb2_generic_create(&cr, NULL, false, fname, + NTCREATEX_DISP_CREATE, + smb2_util_oplock_level(""), 0, 0); + status = smb2_create(tree, tree, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h = cr.out.file.handle; + + finfo1 = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h, + }; + status = smb2_getinfo_file(tree, tree, &finfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + status = smb2_util_write(tree, h, "123456789", 0, 9); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + cl = (struct smb2_close) { + .in.file.handle = h, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h); + + torture_comment(tctx, "Initial: %s\nClose: %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time), + nt_time_string(tctx, cl.out.write_time)); + + torture_assert_u64_equal_goto(tctx, + finfo1.basic_info.out.write_time, + cl.out.write_time, + ret, done, + "Write time changed (wrong!)\n"); + + torture_comment(tctx, "Write with 20 ms delay, expect " + "write-time change\n"); + + smb2_generic_create(&cr, NULL, false, fname, + NTCREATEX_DISP_OPEN, + smb2_util_oplock_level(""), 0, 0); + status = smb2_create(tree, tree, &cr); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "create failed\n"); + h = cr.out.file.handle; + + finfo1 = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION, + .generic.in.file.handle = h, + }; + status = smb2_getinfo_file(tree, tree, &finfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "getinfo failed\n"); + + smb_msleep(20); + + status = smb2_util_write(tree, h, "123456789", 0, 9); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + cl = (struct smb2_close) { + .in.file.handle = h, + .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION, + }; + + status = smb2_close(tree, &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "close failed\n"); + ZERO_STRUCT(h); + + torture_comment(tctx, "Initial: %s\nClose: %s\n", + nt_time_string(tctx, finfo1.basic_info.out.write_time), + nt_time_string(tctx, cl.out.write_time)); + + torture_assert_u64_not_equal_goto( + tctx, + finfo1.basic_info.out.write_time, + cl.out.write_time, + ret, done, + "Write time did not change (wrong!)\n"); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* + basic testing of SMB2 timestamps +*/ +struct torture_suite *torture_smb2_timestamp_resolution_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "timestamp_resolution"); + + torture_suite_add_1smb2_test(suite, "resolution1", test_timestamp_resolution1); + + suite->description = talloc_strdup(suite, "SMB2 timestamp tests"); + + return suite; +} diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c new file mode 100644 index 0000000..233f589 --- /dev/null +++ b/source4/torture/smb2/util.c @@ -0,0 +1,1045 @@ +/* + Unix SMB/CIFS implementation. + + helper functions for SMB2 test suite + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/security/security_descriptor.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/cmdline/cmdline.h" +#include "system/time.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include "lib/util/tevent_ntstatus.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "source4/torture/util.h" +#include "libcli/security/dom_sid.h" +#include "librpc/gen_ndr/lsa.h" +#include "libcli/util/clilsa.h" + + +/* + write to a file on SMB2 +*/ +NTSTATUS smb2_util_write(struct smb2_tree *tree, + struct smb2_handle handle, + const void *buf, off_t offset, size_t size) +{ + struct smb2_write w; + + ZERO_STRUCT(w); + w.in.file.handle = handle; + w.in.offset = offset; + w.in.data = data_blob_const(buf, size); + + return smb2_write(tree, &w); +} + +/* + create a complex file/dir using the SMB2 protocol +*/ +static NTSTATUS smb2_create_complex(struct torture_context *tctx, + struct smb2_tree *tree, + const char *fname, + struct smb2_handle *handle, + bool dir) +{ + TALLOC_CTX *tmp_ctx = talloc_new(tree); + char buf[7] = "abc"; + struct smb2_create io; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smb2_util_unlink(tree, fname); + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = fname; + if (dir) { + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + } + + /* it seems vista is now fussier about alignment? */ + if (strchr(fname, ':') == NULL) { + /* setup some EAs */ + io.in.eas.num_eas = 2; + io.in.eas.eas = talloc_array(tmp_ctx, struct ea_struct, 2); + io.in.eas.eas[0].flags = 0; + io.in.eas.eas[0].name.s = "EAONE"; + io.in.eas.eas[0].value = data_blob_talloc(tmp_ctx, "VALUE1", 6); + io.in.eas.eas[1].flags = 0; + io.in.eas.eas[1].name.s = "SECONDEA"; + io.in.eas.eas[1].value = data_blob_talloc(tmp_ctx, "ValueTwo", 8); + } + + status = smb2_create(tree, tmp_ctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)) { + torture_comment( + tctx, "EAs not supported, creating: %s\n", fname); + io.in.eas.num_eas = 0; + status = smb2_create(tree, tmp_ctx, &io); + } + + talloc_free(tmp_ctx); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.file.handle; + + if (!dir) { + status = smb2_util_write(tree, *handle, buf, 0, sizeof(buf)); + NT_STATUS_NOT_OK_RETURN(status); + } + + /* make sure all the timestamps aren't the same, and are also + in different DST zones*/ + setfile.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + setfile.generic.in.file.handle = *handle; + + unix_to_nt_time(&setfile.basic_info.in.create_time, t + 9*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.access_time, t + 6*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.write_time, t + 3*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.change_time, t + 1*30*24*60*60); + setfile.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + + status = smb2_setinfo_file(tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to setup file times - %s\n", nt_errstr(status)); + return status; + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + fileinfo.generic.in.file.handle = *handle; + + status = smb2_getinfo_file(tree, tree, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to query file times - %s\n", nt_errstr(status)); + return status; + + } + +#define CHECK_TIME(field) do {\ + if (setfile.basic_info.in.field != fileinfo.all_info2.out.field) { \ + torture_comment(tctx, "(%s) " #field " not setup correctly: %s(%llu) => %s(%llu)\n", \ + __location__, \ + nt_time_string(tree, setfile.basic_info.in.field), \ + (unsigned long long)setfile.basic_info.in.field, \ + nt_time_string(tree, fileinfo.basic_info.out.field), \ + (unsigned long long)fileinfo.basic_info.out.field); \ + status = NT_STATUS_INVALID_PARAMETER; \ + } \ +} while (0) + + CHECK_TIME(create_time); + CHECK_TIME(access_time); + CHECK_TIME(write_time); + CHECK_TIME(change_time); + + return status; +} + +/* + create a complex file using the SMB2 protocol +*/ +NTSTATUS smb2_create_complex_file(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + return smb2_create_complex(tctx, tree, fname, handle, false); +} + +/* + create a complex dir using the SMB2 protocol +*/ +NTSTATUS smb2_create_complex_dir(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + return smb2_create_complex(tctx, tree, fname, handle, true); +} + +/* + show lots of information about a file +*/ +void torture_smb2_all_info(struct torture_context *tctx, + struct smb2_tree *tree, struct smb2_handle handle) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + union smb_fileinfo io; + + io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + io.generic.in.file.handle = handle; + + status = smb2_getinfo_file(tree, tmp_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("getinfo failed - %s\n", nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + torture_comment(tctx, "all_info for '%s'\n", io.all_info2.out.fname.s); + torture_comment(tctx, "\tcreate_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.create_time)); + torture_comment(tctx, "\taccess_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.access_time)); + torture_comment(tctx, "\twrite_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.write_time)); + torture_comment(tctx, "\tchange_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.change_time)); + torture_comment(tctx, "\tattrib: 0x%x\n", io.all_info2.out.attrib); + torture_comment(tctx, "\tunknown1: 0x%x\n", io.all_info2.out.unknown1); + torture_comment(tctx, "\talloc_size: %llu\n", (long long)io.all_info2.out.alloc_size); + torture_comment(tctx, "\tsize: %llu\n", (long long)io.all_info2.out.size); + torture_comment(tctx, "\tnlink: %u\n", io.all_info2.out.nlink); + torture_comment(tctx, "\tdelete_pending: %u\n", io.all_info2.out.delete_pending); + torture_comment(tctx, "\tdirectory: %u\n", io.all_info2.out.directory); + torture_comment(tctx, "\tfile_id: %llu\n", (long long)io.all_info2.out.file_id); + torture_comment(tctx, "\tea_size: %u\n", io.all_info2.out.ea_size); + torture_comment(tctx, "\taccess_mask: 0x%08x\n", io.all_info2.out.access_mask); + torture_comment(tctx, "\tposition: 0x%llx\n", (long long)io.all_info2.out.position); + torture_comment(tctx, "\tmode: 0x%llx\n", (long long)io.all_info2.out.mode); + + /* short name, if any */ + io.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + if (NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "\tshort name: '%s'\n", io.alt_name_info.out.fname.s); + } + + /* the EAs, if any */ + io.generic.level = RAW_FILEINFO_SMB2_ALL_EAS; + status = smb2_getinfo_file(tree, tmp_ctx, &io); + if (NT_STATUS_IS_OK(status)) { + int i; + for (i=0;itransport->options.request_timeout * 1000; + + subreq = smb2cli_tcon_send(tree, tctx->ev, + session->transport->conn, + timeout_msec, + session->smbXcli, + tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, "smb2cli_tcon_recv"); + + *_tree = tree; + + return true; +} + +/** + * do a smb2 session setup (without a tree connect) + */ +bool torture_smb2_session_setup(struct torture_context *tctx, + struct smb2_transport *transport, + uint64_t previous_session_id, + TALLOC_CTX *mem_ctx, + struct smb2_session **_session) +{ + NTSTATUS status; + struct smb2_session *session; + + session = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + mem_ctx); + + if (session == NULL) { + return false; + } + + status = smb2_session_setup_spnego(session, + samba_cmdline_get_creds(), + previous_session_id); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "session setup failed: %s\n", nt_errstr(status)); + talloc_free(session); + return false; + } + + *_session = session; + + return true; +} + +/* + open a smb2 connection +*/ +bool torture_smb2_connection_ext(struct torture_context *tctx, + uint64_t previous_session_id, + const struct smbcli_options *options, + struct smb2_tree **tree) +{ + NTSTATUS status; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + const char *p = torture_setting_string(tctx, "unclist", NULL); + TALLOC_CTX *mem_ctx = NULL; + bool ok; + + if (p != NULL) { + char *host2 = NULL; + char *share2 = NULL; + + mem_ctx = talloc_new(tctx); + if (mem_ctx == NULL) { + return false; + } + + ok = torture_get_conn_index(tctx->conn_index++, mem_ctx, tctx, + &host2, &share2); + if (!ok) { + TALLOC_FREE(mem_ctx); + return false; + } + + host = host2; + share = share2; + } + + status = smb2_connect_ext(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + NULL, /* existing_conn */ + previous_session_id, + tree, + tctx->ev, + options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to connect to SMB2 share \\\\%s\\%s - %s\n", + host, share, nt_errstr(status)); + TALLOC_FREE(mem_ctx); + return false; + } + + TALLOC_FREE(mem_ctx); + return true; +} + +bool torture_smb2_connection(struct torture_context *tctx, struct smb2_tree **tree) +{ + bool ret; + struct smbcli_options options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + ret = torture_smb2_connection_ext(tctx, 0, &options, tree); + + return ret; +} + +/** + * SMB2 connect with share from soption + **/ +bool torture_smb2_con_share(struct torture_context *tctx, + const char *share, + struct smb2_tree **tree) +{ + struct smbcli_options options; + NTSTATUS status; + const char *host = torture_setting_string(tctx, "host", NULL); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + samba_cmdline_get_creds(), + tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to connect to SMB2 share \\\\%s\\%s - %s\n", + host, share, nt_errstr(status)); + return false; + } + return true; +} + +/** + * SMB2 connect with share from soption + **/ +bool torture_smb2_con_sopt(struct torture_context *tctx, + const char *soption, + struct smb2_tree **tree) +{ + const char *share = torture_setting_string(tctx, soption, NULL); + + if (share == NULL) { + torture_comment(tctx, "No share for option %s\n", soption); + return false; + } + + return torture_smb2_con_share(tctx, share, tree); +} + +/* + create and return a handle to a test file + with a specific access mask +*/ +NTSTATUS torture_smb2_testfile_access(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle, + uint32_t desired_access) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = desired_access; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = fname; + + status = smb2_create(tree, tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + +/* + create and return a handle to a test file +*/ +NTSTATUS torture_smb2_testfile(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + return torture_smb2_testfile_access(tree, fname, handle, + SEC_RIGHTS_FILE_ALL); +} + +/* + create and return a handle to a test file + with a specific access mask +*/ +NTSTATUS torture_smb2_open(struct smb2_tree *tree, + const char *fname, + uint32_t desired_access, + struct smb2_handle *handle) +{ + struct smb2_create io; + NTSTATUS status; + + io = (struct smb2_create) { + .in.fname = fname, + .in.desired_access = desired_access, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + }; + + status = smb2_create(tree, tree, &io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + +/* + create and return a handle to a test directory + with specific desired access +*/ +NTSTATUS torture_smb2_testdir_access(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle, + uint32_t desired_access) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = desired_access; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.fname = fname; + + status = smb2_create(tree, tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + +/* + create and return a handle to a test directory +*/ +NTSTATUS torture_smb2_testdir(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + return torture_smb2_testdir_access(tree, fname, handle, + SEC_RIGHTS_DIR_ALL); +} + +/* + create a simple file using the SMB2 protocol +*/ +NTSTATUS smb2_create_simple_file(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + char buf[7] = "abc"; + NTSTATUS status; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testfile_access(tree, + fname, handle, + SEC_FLAG_MAXIMUM_ALLOWED); + NT_STATUS_NOT_OK_RETURN(status); + + status = smb2_util_write(tree, *handle, buf, 0, sizeof(buf)); + NT_STATUS_NOT_OK_RETURN(status); + + return NT_STATUS_OK; +} + +/* + create a simple file using SMB2. +*/ +NTSTATUS torture_setup_simple_file(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname) +{ + struct smb2_handle handle; + NTSTATUS status = smb2_create_simple_file(tctx, tree, fname, &handle); + NT_STATUS_NOT_OK_RETURN(status); + return smb2_util_close(tree, handle); +} + +/* + create a complex file using SMB2, to make it easier to + find fields in SMB2 getinfo levels +*/ +NTSTATUS torture_setup_complex_file(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname) +{ + struct smb2_handle handle; + NTSTATUS status = smb2_create_complex_file(tctx, tree, fname, &handle); + NT_STATUS_NOT_OK_RETURN(status); + return smb2_util_close(tree, handle); +} + + +/* + create a complex dir using SMB2, to make it easier to + find fields in SMB2 getinfo levels +*/ +NTSTATUS torture_setup_complex_dir(struct torture_context *tctx, + struct smb2_tree *tree, const char *fname) +{ + struct smb2_handle handle; + NTSTATUS status = smb2_create_complex_dir(tctx, tree, fname, &handle); + NT_STATUS_NOT_OK_RETURN(status); + return smb2_util_close(tree, handle); +} + + +/* + return a handle to the root of the share +*/ +NTSTATUS smb2_util_roothandle(struct smb2_tree *tree, struct smb2_handle *handle) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_DIR_READ_ATTRIBUTE | SEC_DIR_LIST; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = NTCREATEX_OPTIONS_ASYNC_ALERT; + io.in.fname = ""; + + status = smb2_create(tree, tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + +/* Comparable to torture_setup_dir, but for SMB2. */ +bool smb2_util_setup_dir(struct torture_context *tctx, struct smb2_tree *tree, + const char *dname) +{ + NTSTATUS status; + + /* XXX: smb_raw_exit equivalent? + smb_raw_exit(cli->session); */ + if (smb2_deltree(tree, dname) == -1) { + torture_result(tctx, TORTURE_ERROR, "Unable to deltree when setting up %s.\n", dname); + return false; + } + + status = smb2_util_mkdir(tree, dname); + if (NT_STATUS_IS_ERR(status)) { + torture_result(tctx, TORTURE_ERROR, "Unable to mkdir when setting up %s - %s\n", dname, + nt_errstr(status)); + return false; + } + + return true; +} + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +/* + * Helper function to verify a security descriptor, by querying + * and comparing against the passed in sd. + */ +bool smb2_util_verify_sd(TALLOC_CTX *tctx, struct smb2_tree *tree, + struct smb2_handle handle, struct security_descriptor *sd) +{ + NTSTATUS status; + bool ret = true; + union smb_fileinfo q = {}; + + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!security_acl_equal( + q.query_secdesc.out.sd->dacl, sd->dacl)) { + torture_warning(tctx, "%s: security descriptors don't match!\n", + __location__); + torture_warning(tctx, "got:\n"); + NDR_PRINT_DEBUG(security_descriptor, + q.query_secdesc.out.sd); + torture_warning(tctx, "expected:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); + ret = false; + } + + done: + return ret; +} + +/* + * Helper function to verify attributes, by querying + * and comparing against the passed in attrib. + */ +bool smb2_util_verify_attrib(TALLOC_CTX *tctx, struct smb2_tree *tree, + struct smb2_handle handle, uint32_t attrib) +{ + NTSTATUS status; + bool ret = true; + union smb_fileinfo q = {}; + + q.standard.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + q.standard.in.file.handle = handle; + status = smb2_getinfo_file(tree, tctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + + q.all_info2.out.attrib &= ~(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NONINDEXED); + + if (q.all_info2.out.attrib != attrib) { + torture_warning(tctx, "%s: attributes don't match! " + "got %x, expected %x\n", __location__, + (uint32_t)q.standard.out.attrib, + (uint32_t)attrib); + ret = false; + } + + done: + return ret; +} + + +uint32_t smb2_util_lease_state(const char *ls) +{ + uint32_t val = 0; + int i; + + for (i = 0; i < strlen(ls); i++) { + switch (ls[i]) { + case 'R': + val |= SMB2_LEASE_READ; + break; + case 'H': + val |= SMB2_LEASE_HANDLE; + break; + case 'W': + val |= SMB2_LEASE_WRITE; + break; + } + } + + return val; +} + +char *smb2_util_lease_state_string(TALLOC_CTX *mem_ctx, uint32_t ls) +{ + return talloc_asprintf(mem_ctx, "0x%0x (%s%s%s)", + (unsigned)ls, + ls & SMB2_LEASE_READ ? "R": "", + ls & SMB2_LEASE_HANDLE ? "H": "", + ls & SMB2_LEASE_WRITE ? "W": ""); +} + +uint32_t smb2_util_share_access(const char *sharemode) +{ + uint32_t val = NTCREATEX_SHARE_ACCESS_NONE; /* 0 */ + int i; + + for (i = 0; i < strlen(sharemode); i++) { + switch(sharemode[i]) { + case 'R': + val |= NTCREATEX_SHARE_ACCESS_READ; + break; + case 'W': + val |= NTCREATEX_SHARE_ACCESS_WRITE; + break; + case 'D': + val |= NTCREATEX_SHARE_ACCESS_DELETE; + break; + } + } + + return val; +} + +uint8_t smb2_util_oplock_level(const char *op) +{ + uint8_t val = SMB2_OPLOCK_LEVEL_NONE; + int i; + + for (i = 0; i < strlen(op); i++) { + switch (op[i]) { + case 's': + return SMB2_OPLOCK_LEVEL_II; + case 'x': + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + case 'b': + return SMB2_OPLOCK_LEVEL_BATCH; + default: + continue; + } + } + + return val; +} + +/** + * Helper functions to fill a smb2_create struct for several + * open scenarios. + */ +void smb2_generic_create_share(struct smb2_create *io, struct smb2_lease *ls, + bool dir, const char *name, uint32_t disposition, + uint32_t share_access, + uint8_t oplock, uint64_t leasekey, + uint32_t leasestate) +{ + ZERO_STRUCT(*io); + io->in.security_flags = 0x00; + io->in.oplock_level = oplock; + io->in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + io->in.create_flags = 0x00000000; + io->in.reserved = 0x00000000; + io->in.desired_access = SEC_RIGHTS_FILE_ALL; + io->in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io->in.share_access = share_access; + io->in.create_disposition = disposition; + io->in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io->in.fname = name; + + if (dir) { + io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io->in.create_disposition = NTCREATEX_DISP_CREATE; + } + + if (ls) { + ZERO_STRUCTPN(ls); + ls->lease_key.data[0] = leasekey; + ls->lease_key.data[1] = ~leasekey; + ls->lease_state = leasestate; + io->in.lease_request = ls; + } +} + +void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls, + bool dir, const char *name, uint32_t disposition, + uint8_t oplock, uint64_t leasekey, + uint32_t leasestate) +{ + smb2_generic_create_share(io, ls, dir, name, disposition, + smb2_util_share_access("RWD"), + oplock, + leasekey, leasestate); +} + +void smb2_lease_create_share(struct smb2_create *io, struct smb2_lease *ls, + bool dir, const char *name, uint32_t share_access, + uint64_t leasekey, uint32_t leasestate) +{ + smb2_generic_create_share(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF, + share_access, SMB2_OPLOCK_LEVEL_LEASE, + leasekey, leasestate); +} + +void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls, + bool dir, const char *name, uint64_t leasekey, + uint32_t leasestate) +{ + smb2_lease_create_share(io, ls, dir, name, + smb2_util_share_access("RWD"), + leasekey, leasestate); +} + +void smb2_lease_v2_create_share(struct smb2_create *io, + struct smb2_lease *ls, + bool dir, + const char *name, + uint32_t share_access, + uint64_t leasekey, + const uint64_t *parentleasekey, + uint32_t leasestate, + uint16_t lease_epoch) +{ + smb2_generic_create_share(io, NULL, dir, name, NTCREATEX_DISP_OPEN_IF, + share_access, SMB2_OPLOCK_LEVEL_LEASE, 0, 0); + + if (ls) { + ZERO_STRUCT(*ls); + ls->lease_key.data[0] = leasekey; + ls->lease_key.data[1] = ~leasekey; + ls->lease_state = leasestate; + if (parentleasekey != NULL) { + ls->lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET; + ls->parent_lease_key.data[0] = *parentleasekey; + ls->parent_lease_key.data[1] = ~(*parentleasekey); + } + ls->lease_epoch = lease_epoch; + io->in.lease_request_v2 = ls; + } +} + +void smb2_lease_v2_create(struct smb2_create *io, + struct smb2_lease *ls, + bool dir, + const char *name, + uint64_t leasekey, + const uint64_t *parentleasekey, + uint32_t leasestate, + uint16_t lease_epoch) +{ + smb2_lease_v2_create_share(io, ls, dir, name, + smb2_util_share_access("RWD"), + leasekey, parentleasekey, + leasestate, lease_epoch); +} + + +void smb2_oplock_create_share(struct smb2_create *io, const char *name, + uint32_t share_access, uint8_t oplock) +{ + smb2_generic_create_share(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF, + share_access, oplock, 0, 0); +} +void smb2_oplock_create(struct smb2_create *io, const char *name, uint8_t oplock) +{ + smb2_oplock_create_share(io, name, smb2_util_share_access("RWD"), + oplock); +} + +/* + a wrapper around smblsa_sid_check_privilege, that tries to take + account of the fact that the lsa privileges calls don't expand + group memberships, using an explicit check for administrator. There + must be a better way ... + */ +NTSTATUS torture_smb2_check_privilege(struct smb2_tree *tree, + const char *sid_str, + const char *privilege) +{ + struct dom_sid *sid = NULL; + TALLOC_CTX *tmp_ctx = NULL; + uint32_t rid; + NTSTATUS status; + + tmp_ctx = talloc_new(tree); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + sid = dom_sid_parse_talloc(tmp_ctx, sid_str); + if (sid == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_INVALID_SID; + } + + status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(tmp_ctx); + return status; + } + + if (rid == DOMAIN_RID_ADMINISTRATOR) { + /* assume the administrator has them all */ + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; + } + + talloc_free(tmp_ctx); + + return smb2lsa_sid_check_privilege(tree, sid_str, privilege); +} diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build new file mode 100644 index 0000000..533639c --- /dev/null +++ b/source4/torture/smb2/wscript_build @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +bld.SAMBA_MODULE('TORTURE_SMB2', + source=''' + acls.c + attr.c + block.c + bench.c + charset.c + compound.c + connect.c + create.c + credits.c + delete-on-close.c + deny.c + dir.c + dosmode.c + durable_open.c + durable_v2_open.c + ea.c + getinfo.c + ioctl.c + lease.c + lease_break_handler.c + lock.c + max_allowed.c + mangle.c + maxfid.c + maxwrite.c + mkdir.c + multichannel.c + oplock_break_handler.c + notify.c + notify_disabled.c + oplock.c + read.c + read_write.c + rename.c + replay.c + scan.c + secleak.c + session.c + sessid.c + setinfo.c + sharemode.c + smb2.c + streams.c + samba3misc.c + tcon.c + timestamps.c + util.c + ''', + subsystem='smbtorture', + deps='LIBCLI_SMB2 torture NDR_IOCTL CMDLINE_S4', + internal_module=True, + autoproto='proto.h', + init_function='torture_smb2_init' + ) + diff --git a/source4/torture/smbtorture.c b/source4/torture/smbtorture.c new file mode 100644 index 0000000..d9e90ea --- /dev/null +++ b/source4/torture/smbtorture.c @@ -0,0 +1,770 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006-2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "system/time.h" +#include "system/wait.h" +#include "system/filesys.h" +#include "system/readline.h" +#include "../libcli/smbreadline/smbreadline.h" +#include "libcli/libcli.h" +#include "lib/events/events.h" + +#include "torture/smbtorture.h" +#include "librpc/rpc/dcerpc.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" +#include "lib/util/samba_modules.h" + +#ifdef HAVE_READLINE_HISTORY_H +#include +#endif + +static int use_fullname; + +static char *prefix_name(TALLOC_CTX *mem_ctx, const char *prefix, const char *name) +{ + if (prefix == NULL) + return talloc_strdup(mem_ctx, name); + else + return talloc_asprintf(mem_ctx, "%s.%s", prefix, name); +} + +static void print_test_list(const struct torture_suite *suite, const char *prefix, const char *expr) +{ + struct torture_suite *o; + struct torture_tcase *t; + struct torture_test *p; + + for (o = suite->children; o; o = o->next) { + char *name = prefix_name(NULL, prefix, o->name); + print_test_list(o, name, expr); + talloc_free(name); + } + + for (t = suite->testcases; t; t = t->next) { + for (p = t->tests; p; p = p->next) { + char *name = talloc_asprintf(NULL, "%s.%s.%s", prefix, t->name, p->name); + if (strncmp(name, expr, strlen(expr)) == 0) { + printf("%s\n", name); + } + talloc_free(name); + } + } +} + +static bool run_matching(struct torture_context *torture, + const char *prefix, + const char *expr, + const char **restricted, + struct torture_suite *suite, + bool *matched) +{ + bool ret = true; + struct torture_suite *o; + struct torture_tcase *t; + struct torture_test *p; + + for (o = suite->children; o; o = o->next) { + char *name = NULL; + name = prefix_name(torture, prefix, o->name); + if (gen_fnmatch(expr, name) == 0) { + *matched = true; + reload_charcnv(torture->lp_ctx); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, prefix); + } + ret &= torture_run_suite_restricted(torture, o, restricted); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, NULL); + } + /* + * torture_run_suite_restricted() already implements + * recursion, so we're done with this child suite. + */ + continue; + } + ret &= run_matching(torture, name, expr, restricted, o, matched); + } + + for (t = suite->testcases; t; t = t->next) { + char *tname = prefix_name(torture, prefix, t->name); + if (gen_fnmatch(expr, tname) == 0) { + *matched = true; + reload_charcnv(torture->lp_ctx); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, prefix); + } + ret &= torture_run_tcase_restricted(torture, t, restricted); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, NULL); + } + /* + * torture_run_tcase_restricted() already implements + * recursion, so we're done for this tcase. + */ + continue; + } + for (p = t->tests; p; p = p->next) { + char *pname = prefix_name(torture, tname, p->name); + if (gen_fnmatch(expr, pname) == 0) { + *matched = true; + reload_charcnv(torture->lp_ctx); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, + tname); + } + ret &= torture_run_test_restricted(torture, t, p, restricted); + if (use_fullname == 1) { + torture_subunit_prefix_reset(torture, + NULL); + } + } + } + } + + return ret; +} + +#define MAX_COLS 80 /* FIXME: Determine this at run-time */ + +/**************************************************************************** +run a specified test or "ALL" +****************************************************************************/ +bool torture_run_named_tests(struct torture_context *torture, const char *name, + const char **restricted) +{ + bool ret = true; + bool matched = false; + struct torture_suite *o; + + torture_ui_report_time(torture); + + if (strequal(name, "ALL")) { + if (restricted != NULL) { + printf("--load-list and ALL are incompatible\n"); + return false; + } + for (o = torture_root->children; o; o = o->next) { + ret &= torture_run_suite(torture, o); + } + return ret; + } + + ret = run_matching(torture, NULL, name, restricted, torture_root, &matched); + + if (!matched) { + printf("Unknown torture operation '%s'\n", name); + return false; + } + + return ret; +} + +bool torture_parse_target(TALLOC_CTX *ctx, + struct loadparm_context *lp_ctx, + const char *target) +{ + char *host = NULL, *share = NULL; + struct dcerpc_binding *binding_struct; + NTSTATUS status; + + /* see if its a RPC transport specifier */ + if (!smbcli_parse_unc(target, NULL, &host, &share)) { + const char *h; + + status = dcerpc_parse_binding(ctx, target, &binding_struct); + if (NT_STATUS_IS_ERR(status)) { + d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", target); + return false; + } + + h = dcerpc_binding_get_string_option(binding_struct, "host"); + host = discard_const_p(char, h); + if (host != NULL) { + lpcfg_set_cmdline(lp_ctx, "torture:host", host); + } + + if (lpcfg_parm_string(lp_ctx, NULL, "torture", "share") == NULL) + lpcfg_set_cmdline(lp_ctx, "torture:share", "IPC$"); + lpcfg_set_cmdline(lp_ctx, "torture:binding", target); + } else { + lpcfg_set_cmdline(lp_ctx, "torture:host", host); + lpcfg_set_cmdline(lp_ctx, "torture:share", share); + lpcfg_set_cmdline(lp_ctx, "torture:binding", host); + } + + return true; +} + +static void parse_dns(struct loadparm_context *lp_ctx, const char *dns) +{ + char *userdn, *basedn, *secret; + char *p, *d; + + /* retrieve the userdn */ + p = strchr_m(dns, '#'); + if (!p) { + lpcfg_set_cmdline(lp_ctx, "torture:ldap_userdn", ""); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_basedn", ""); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_secret", ""); + return; + } + userdn = strndup(dns, p - dns); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn); + + /* retrieve the basedn */ + d = p + 1; + p = strchr_m(d, '#'); + if (!p) { + lpcfg_set_cmdline(lp_ctx, "torture:ldap_basedn", ""); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_secret", ""); + return; + } + basedn = strndup(d, p - d); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn); + + /* retrieve the secret */ + p = p + 1; + if (!p) { + lpcfg_set_cmdline(lp_ctx, "torture:ldap_secret", ""); + return; + } + secret = strdup(p); + lpcfg_set_cmdline(lp_ctx, "torture:ldap_secret", secret); + + printf ("%s - %s - %s\n", userdn, basedn, secret); + +} + +/* Print the full test list, formatted into separate labelled test + * groups. + */ +static void print_structured_testsuite_list(void) +{ + struct torture_suite *o; + struct torture_suite *s; + struct torture_tcase *t; + int i; + + if (torture_root == NULL) { + printf("NO TESTS LOADED\n"); + return; + } + + for (o = torture_root->children; o; o = o->next) { + printf("\n%s (%s):\n ", o->description, o->name); + + i = 0; + for (s = o->children; s; s = s->next) { + if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) { + printf("\n "); + i = 0; + } + i+=printf("%s.%s ", o->name, s->name); + } + + for (t = o->testcases; t; t = t->next) { + if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) { + printf("\n "); + i = 0; + } + i+=printf("%s.%s ", o->name, t->name); + } + + if (i) printf("\n"); + } + + printf("\nThe default test is ALL.\n"); +} + +static void print_testsuite_list(void) +{ + struct torture_suite *o; + struct torture_suite *s; + struct torture_tcase *t; + + if (torture_root == NULL) + return; + + for (o = torture_root->children; o; o = o->next) { + for (s = o->children; s; s = s->next) { + printf("%s.%s\n", o->name, s->name); + } + + for (t = o->testcases; t; t = t->next) { + printf("%s.%s\n", o->name, t->name); + } + } +} + +void torture_print_testsuites(bool structured) +{ + if (structured) { + print_structured_testsuite_list(); + } else { + print_testsuite_list(); + } +} + +static void usage(poptContext pc) +{ + poptPrintUsage(pc, stdout, 0); + printf("\n"); + + printf("The binding format is:\n\n"); + + printf(" TRANSPORT:host[flags]\n\n"); + + printf(" where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n"); + printf(" or ncalrpc for local connections.\n\n"); + + printf(" 'host' is an IP or hostname or netbios name. If the binding string\n"); + printf(" identifies the server side of an endpoint, 'host' may be an empty\n"); + printf(" string.\n\n"); + + printf(" 'flags' can include a SMB pipe name if using the ncacn_np transport or\n"); + printf(" a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n"); + printf(" will be auto-determined.\n\n"); + + printf(" other recognised flags are:\n\n"); + + printf(" sign : enable ntlmssp signing\n"); + printf(" seal : enable ntlmssp sealing\n"); + printf(" connect : enable rpc connect level auth (auth, but no sign or seal)\n"); + printf(" validate: enable the NDR validator\n"); + printf(" print: enable debugging of the packets\n"); + printf(" bigendian: use bigendian RPC\n"); + printf(" padcheck: check reply data for non-zero pad bytes\n\n"); + + printf(" For example, these all connect to the samr pipe:\n\n"); + + printf(" ncacn_np:myserver\n"); + printf(" ncacn_np:myserver[samr]\n"); + printf(" ncacn_np:myserver[\\pipe\\samr]\n"); + printf(" ncacn_np:myserver[/pipe/samr]\n"); + printf(" ncacn_np:myserver[samr,sign,print]\n"); + printf(" ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n"); + printf(" ncacn_np:myserver[/pipe/samr,seal,validate]\n"); + printf(" ncacn_np:\n"); + printf(" ncacn_np:[/pipe/samr]\n\n"); + + printf(" ncacn_ip_tcp:myserver\n"); + printf(" ncacn_ip_tcp:myserver[1024]\n"); + printf(" ncacn_ip_tcp:myserver[1024,sign,seal]\n\n"); + + printf(" ncalrpc:\n\n"); + + printf("The UNC format is:\n\n"); + + printf(" //server/share\n\n"); + + printf("Tests are:"); + + print_structured_testsuite_list(); + +} + +_NORETURN_ static void max_runtime_handler(int sig) +{ + DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n")); + exit(1); +} + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc, const char *argv[]) +{ + int opt, i; + bool correct = true; + int max_runtime=0; + int argc_new; + struct torture_context *torture; + struct torture_results *results; + const struct torture_ui_ops *ui_ops; + char **argv_new; + poptContext pc; + static const char *target = "other"; + NTSTATUS status; + int shell = false; + static const char *ui_ops_name = "subunit"; + const char *basedir = NULL; + char *outputdir; + const char *extra_module = NULL; + static int list_tests = 0, list_testsuites = 0; + int num_extra_users = 0; + const char **restricted = NULL; + int num_restricted = -1; + const char *load_list = NULL; + enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST, + OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS, + OPT_EXTRA_USER,}; + TALLOC_CTX *mem_ctx = NULL; + struct loadparm_context *lp_ctx = NULL; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + {"fullname", 0, POPT_ARG_NONE, &use_fullname, 0, + "use full name for the test", NULL }, + {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL }, + {"smb-ports", 'p', POPT_ARG_STRING, NULL, OPT_SMB_PORTS, "SMB ports", NULL}, + {"basedir", 0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" }, + {"seed", 0, POPT_ARG_INT, &torture_seed, 0, "Seed to use for randomizer", NULL}, + {"num-progs", 0, POPT_ARG_INT, NULL, OPT_NUMPROGS, "num progs", NULL}, + {"num-ops", 0, POPT_ARG_INT, &torture_numops, 0, "num ops", NULL}, + {"entries", 0, POPT_ARG_INT, &torture_entries, 0, "entries", NULL}, + {"loadfile", 0, POPT_ARG_STRING, NULL, OPT_LOADFILE, "NBench load file to use", NULL}, + {"list-suites", 0, POPT_ARG_NONE, &list_testsuites, 0, "List available testsuites and exit", NULL }, + {"list", 0, POPT_ARG_NONE, &list_tests, 0, "List available tests in specified suites and exit", NULL }, + {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, + {"timelimit", 't', POPT_ARG_INT, NULL, OPT_TIMELIMIT, "Set time limit (in seconds)", NULL}, + {"failures", 'f', POPT_ARG_INT, &torture_failures, 0, "failures", NULL}, + {"parse-dns", 'D', POPT_ARG_STRING, NULL, OPT_DNS, "parse-dns", NULL}, + {"dangerous", 'X', POPT_ARG_NONE, NULL, OPT_DANGEROUS, + "run dangerous tests (eg. wiping out password database)", NULL}, + {"load-module", 0, POPT_ARG_STRING, &extra_module, 0, "load tests from DSO file", "SOFILE"}, + {"shell", 0, POPT_ARG_NONE, &shell, true, "Run shell", NULL}, + {"target", 'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL}, + {"async", 'a', POPT_ARG_NONE, NULL, OPT_ASYNC, + "run async tests", NULL}, + {"num-async", 0, POPT_ARG_INT, &torture_numasync, 0, + "number of simultaneous async requests", NULL}, + {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, + "set maximum time for smbtorture to live", "seconds"}, + {"extra-user", 0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER, + "extra user credentials", NULL}, + {"load-list", 0, POPT_ARG_STRING, &load_list, 0, + "load a test id list from a text file", NULL}, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_LEGACY_S4 + POPT_TABLEEND + }; + + setlinebuf(stdout); + + mem_ctx = talloc_named_const(NULL, 0, "torture_ctx"); + if (mem_ctx == NULL) { + printf("Unable to allocate torture_ctx\n"); + exit(1); + } + + printf("smbtorture %s\n", samba_version_string()); + + /* we are never interested in SIGPIPE */ + BlockSignals(true, SIGPIPE); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Unable to init cmdline parser\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed cmdline parser\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + poptSetOtherOptionHelp(pc, "| TEST1 TEST2 ..."); + + lp_ctx = samba_cmdline_get_lp_ctx(); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_LOADFILE: + lpcfg_set_cmdline(lp_ctx, "torture:loadfile", poptGetOptArg(pc)); + break; + case OPT_UNCLIST: + lpcfg_set_cmdline(lp_ctx, "torture:unclist", poptGetOptArg(pc)); + break; + case OPT_TIMELIMIT: + lpcfg_set_cmdline(lp_ctx, "torture:timelimit", poptGetOptArg(pc)); + break; + case OPT_NUMPROGS: + lpcfg_set_cmdline(lp_ctx, "torture:nprocs", poptGetOptArg(pc)); + break; + case OPT_DNS: + parse_dns(lp_ctx, poptGetOptArg(pc)); + break; + case OPT_DANGEROUS: + lpcfg_set_cmdline(lp_ctx, "torture:dangerous", "Yes"); + break; + case OPT_ASYNC: + lpcfg_set_cmdline(lp_ctx, "torture:async", "Yes"); + break; + case OPT_SMB_PORTS: + lpcfg_set_cmdline(lp_ctx, "smb ports", poptGetOptArg(pc)); + break; + case OPT_EXTRA_USER: + { + char *option = talloc_asprintf(mem_ctx, + "torture:extra_user%u", + ++num_extra_users); + const char *value = poptGetOptArg(pc); + if (option == NULL) { + printf("talloc fail\n"); + talloc_free(mem_ctx); + exit(1); + } + lpcfg_set_cmdline(lp_ctx, option, value); + talloc_free(option); + } + break; + default: + if (opt < 0) { + printf("Invalid command line option %s (%d)\n", + poptBadOption(pc, 0), + opt); + talloc_free(mem_ctx); + exit(1); + } + } + } + + if (load_list != NULL) { + char **r; + r = file_lines_load(load_list, &num_restricted, 0, mem_ctx); + restricted = discard_const_p(const char *, r); + if (restricted == NULL) { + printf("Unable to read load list file '%s'\n", load_list); + talloc_free(mem_ctx); + exit(1); + } + } + + if (strcmp(target, "samba3") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:samba3", "true"); + lpcfg_set_cmdline(lp_ctx, "torture:resume_key_support", "false"); + } else if (strcmp(target, "samba4") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:samba4", "true"); + } else if (strcmp(target, "samba4-ntvfs") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:samba4", "true"); + lpcfg_set_cmdline(lp_ctx, "torture:samba4-ntvfs", "true"); + } else if (strcmp(target, "winxp") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:winxp", "true"); + } else if (strcmp(target, "w2k3") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:w2k3", "true"); + } else if (strcmp(target, "w2k8") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:w2k8", "true"); + lpcfg_set_cmdline(lp_ctx, + "torture:invalid_lock_range_support", "false"); + } else if (strcmp(target, "w2k12") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:w2k12", "true"); + } else if (strcmp(target, "win7") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:win7", "true"); + lpcfg_set_cmdline(lp_ctx, "torture:resume_key_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:rewind_support", "false"); + + /* RAW-SEARCH for fails for inexplicable reasons against win7 */ + lpcfg_set_cmdline(lp_ctx, "torture:search_ea_support", "false"); + + lpcfg_set_cmdline(lp_ctx, "torture:hide_on_access_denied", + "true"); + } else if (strcmp(target, "onefs") == 0) { + lpcfg_set_cmdline(lp_ctx, "torture:onefs", "true"); + lpcfg_set_cmdline(lp_ctx, "torture:openx_deny_dos_support", + "false"); + lpcfg_set_cmdline(lp_ctx, "torture:range_not_locked_on_file_close", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:sacl_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:ea_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:smbexit_pdu_support", + "false"); + lpcfg_set_cmdline(lp_ctx, "torture:smblock_pdu_support", + "false"); + lpcfg_set_cmdline(lp_ctx, "torture:2_step_break_to_none", + "true"); + lpcfg_set_cmdline(lp_ctx, "torture:deny_dos_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:deny_fcb_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:read_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:writeclose_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:resume_key_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:rewind_support", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:raw_search_search", "false"); + lpcfg_set_cmdline(lp_ctx, "torture:search_ea_size", "false"); + } + + if (max_runtime) { + /* this will only work if nobody else uses alarm(), + which means it won't work for some tests, but we + can't use the event context method we use for smbd + as so many tests create their own event + context. This will at least catch most cases. */ + signal(SIGALRM, max_runtime_handler); + alarm(max_runtime); + } + + if (extra_module != NULL) { + init_module_fn fn = load_module(poptGetOptArg(pc), false, NULL); + + if (fn == NULL) + d_printf("Unable to load module from %s\n", poptGetOptArg(pc)); + else { + status = fn(mem_ctx); + if (NT_STATUS_IS_ERR(status)) { + d_printf("Error initializing module %s: %s\n", + poptGetOptArg(pc), nt_errstr(status)); + } + } + } else { + torture_init(mem_ctx); + } + + if (list_testsuites) { + print_testsuite_list(); + poptFreeContext(pc); + talloc_free(mem_ctx); + return 0; + } + + argv_new = discard_const_p(char *, poptGetArgs(pc)); + + argc_new = argc; + for (i=0; ioutputdir = mkdtemp(outputdir); + if (!torture->outputdir) { + perror("Failed to make temp output dir"); + poptFreeContext(pc); + talloc_free(mem_ctx); + return 1; + } + + torture->lp_ctx = lp_ctx; + + gensec_init(); + + if (shell) { + /* In shell mode, just ignore any remaining test names. */ + torture_shell(torture); + } else { + + /* At this point, we should just have a target string, + * followed by a series of test names. Unless we are in + * shell mode, in which case we don't need anything more. + */ + + if (argc_new < 3) { + printf("You must specify a test to run, or 'ALL'\n"); + usage(pc); + torture->results->returncode = 1; + } else if (!torture_parse_target(torture, + lp_ctx, argv_new[1])) { + /* Take the target name or binding. */ + usage(pc); + torture->results->returncode = 1; + } else { + for (i=2;iresults->returncode && correct) { + poptFreeContext(pc); + talloc_free(mem_ctx); + return(0); + } else { + poptFreeContext(pc); + talloc_free(mem_ctx); + return(1); + } +} diff --git a/source4/torture/smbtorture.h b/source4/torture/smbtorture.h new file mode 100644 index 0000000..3a72e29 --- /dev/null +++ b/source4/torture/smbtorture.h @@ -0,0 +1,146 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __SMBTORTURE_H__ +#define __SMBTORTURE_H__ + +#include "../lib/torture/torture.h" + +struct smbcli_state; + +extern struct torture_suite *torture_root; + +extern int torture_entries; +extern int torture_seed; +extern int torture_numops; +extern int torture_failures; +extern int torture_numasync; + +struct torture_test; +int torture_init(TALLOC_CTX *); +bool torture_register_suite(TALLOC_CTX *, struct torture_suite *suite); +void torture_shell(struct torture_context *tctx); +void torture_print_testsuites(bool structured); +bool torture_run_named_tests(struct torture_context *torture, const char *name, + const char **restricted); +bool torture_parse_target(TALLOC_CTX *ctx, + struct loadparm_context *lp_ctx, + const char *target); + +/* Server Functionality Support */ + +/* Not all SMB server implementations support every aspect of the protocol. + * To allow smbtorture to provide useful data when run against these servers we + * define support parameters here, that will cause some tests to be skipped or + * the correctness checking of some tests to be conditional. + * + * The idea is that different server implementations can be specified on the + * command line such as "--target=win7" which will define the list of server + * parameters that are not supported. This is mostly a black list of + * unsupported features with the default expectation being that all features are + * supported. + * + * Because we use parametric options we do not need to define these parameters + * anywhere, we just define the meaning of each here.*/ + +/* torture:invalid_lock_range_support + * + * This parameter specifies whether the server will return + * STATUS_INVALID_LOCK_RANGE in response to a LockingAndX request where the + * combined offset and range overflow the 63-bit boundary. On Windows servers + * before Win7, this request would return STATUS_OK, but the actual lock + * behavior was undefined. */ + +/* torture:openx_deny_dos_support + * + * This parameter specifies whether the server supports the DENY_DOS open mode + * of the SMBOpenX PDU. */ + +/* torture:range_not_locked_on_file_close + * + * When a byte range lock is pending, and the file which is being locked is + * closed, Windows servers return the error NT_STATUS_RANGE_NOT_LOCKED. This + * is strange, as this error is meant to be returned only for unlock requests. + * When true, torture will expect the Windows behavior, otherwise it will + * expect the more logical NT_STATUS_LOCK_NOT_GRANTED. + */ + +/* torture:sacl_support + * + * This parameter specifies whether the server supports the setting and + * retrieval of System Access Control Lists. This includes whether the server + * supports the use of the SEC_FLAG_SYSTEM_SECURITY bit in the open access + * mask.*/ + +/* torture:smbexit_pdu_support + * + * This parameter specifies whether the server supports the SMBExit (0x11) PDU. */ + +/* torture:smblock_pdu_support + * + * This parameter specifies whether the server supports the SMBLock (0x0C) PDU. */ + +/* torture:2_step_break_to_none + * + * If true this parameter tests servers that break from level 1 to none in two + * steps rather than 1. + */ + +/* torture:resume_key_support + * + * Server supports resuming search via key. + */ + +/* torture:rewind_support + * + * Server supports rewinding during search. + */ + +/* torture:ea_support + * + * Server supports OS/2 style EAs. + */ + +/* torture:search_ea_support + * + * Server supports RAW_SEARCH_DATA_EA_LIST - Torture currently + * does not interact correctly with win7, this flag disables + * the appropriate test. + */ + +/* torture:hide_on_acess_denied + * + * Some servers (win7) choose to hide files when certain access has been + * denied. When true, torture will expect NT_STATUS_OBJECT_NAME_NOT_FOUND + * rather than NT_STATUS_ACCESS_DENIED when trying to open one of these files. + */ + +/* torture:raw_search_search + * + * Server supports RAW_SEARCH_SEARCH level. + */ + +/* torture:search_ea_size + * + * Server supports RAW_SEARCH_DATA_EA_SIZE - This flag disables + * the appropriate test. + */ + +#endif /* __SMBTORTURE_H__ */ diff --git a/source4/torture/tests/test_gentest.sh b/source4/torture/tests/test_gentest.sh new file mode 100755 index 0000000..ad88bd4 --- /dev/null +++ b/source4/torture/tests/test_gentest.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# Blackbox tests for gentest +# Copyright (C) 2008 Andrew Tridgell +# based on test_smbclient.sh + +if [ $# -lt 4 ]; then + cat <$PREFIX/gentest.ignore +all_info.out.fname +internal_information.out.file_id +EOF + +testit "gentest" $VALGRIND $gentest //$SERVER/test1 //$SERVER/test2 --seed=1 --seedsfile=$PREFIX/gentest_seeds.dat --num-ops=100 --ignore=$PREFIX/gentest.ignore -W "$DOMAIN" --user1="$USERNAME%$PASSWORD" --user2="$USERNAME%$PASSWORD" "$@" || failed=$(expr $failed + 1) + +rm -f $PREFIX/gentest.ignore + +exit $failed diff --git a/source4/torture/tests/test_locktest.sh b/source4/torture/tests/test_locktest.sh new file mode 100755 index 0000000..2342d7a --- /dev/null +++ b/source4/torture/tests/test_locktest.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# Blackbox tests for locktest +# Copyright (C) 2008 Andrew Tridgell +# based on test_smbclient.sh + +if [ $# -lt 5 ]; then + cat <. +*/ + +#include "includes.h" +#include "system/time.h" +#include "param/param.h" +#include "torture/smbtorture.h" +#include "lib/util/samba_modules.h" + +_PUBLIC_ int torture_numops=10; +_PUBLIC_ int torture_entries=1000; +_PUBLIC_ int torture_failures=1; +_PUBLIC_ int torture_seed=0; +_PUBLIC_ int torture_numasync=100; + +struct torture_suite *torture_root = NULL; + +bool torture_register_suite(TALLOC_CTX *mem_ctx, struct torture_suite *suite) +{ + if (!suite) + return true; + + if (torture_root == NULL) + torture_root = talloc_zero(mem_ctx, struct torture_suite); + + return torture_suite_add_suite(torture_root, suite); +} + +_PUBLIC_ int torture_init(TALLOC_CTX *mem_ctx) +{ +#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *); + STATIC_smbtorture_MODULES_PROTO; + init_module_fn static_init[] = { STATIC_smbtorture_MODULES }; + init_module_fn *shared_init = load_samba_modules(mem_ctx, "smbtorture"); + + run_init_functions(mem_ctx, static_init); + run_init_functions(mem_ctx, shared_init); + + talloc_free(shared_init); + + return 0; +} diff --git a/source4/torture/unix/unix.c b/source4/torture/unix/unix.c new file mode 100644 index 0000000..8ee3d8d --- /dev/null +++ b/source4/torture/unix/unix.c @@ -0,0 +1,40 @@ +/* + UNIX Extensions test registration. + + Copyright (C) 2007 James Peach + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/unix/proto.h" + +NTSTATUS torture_unix_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "unix"); + + suite->description = + talloc_strdup(suite, "CIFS UNIX extensions tests"); + + torture_suite_add_simple_test(suite, + "whoami", torture_unix_whoami); + torture_suite_add_simple_test(suite, + "info2", unix_torture_unix_info2); + + return (torture_register_suite(ctx, suite)) ? NT_STATUS_OK + : NT_STATUS_UNSUCCESSFUL; + +} diff --git a/source4/torture/unix/unix_info2.c b/source4/torture/unix/unix_info2.c new file mode 100644 index 0000000..cf3ea37 --- /dev/null +++ b/source4/torture/unix/unix_info2.c @@ -0,0 +1,503 @@ +/* + Test the SMB_QUERY_FILE_UNIX_INFO2 Unix extension. + + Copyright (C) 2007 James Peach + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "torture/util.h" +#include "torture/unix/proto.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct unix_info2 { + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + NTTIME create_time; + uint32_t file_flags; + uint32_t flags_mask; +}; + +static struct smbcli_state *connect_to_server(struct torture_context *tctx) +{ + NTSTATUS status; + struct smbcli_state *cli; + + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct smbcli_options options; + struct smbcli_session_options session_options; + struct smb_trans2 tp; + uint16_t setup; + uint8_t data[12]; + uint8_t params[4]; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + status = smbcli_full_connection(tctx, &cli, host, + lpcfg_smb_ports(tctx->lp_ctx), + share, NULL, lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "failed to connect to //%s/%s: %s\n", + host, share, nt_errstr(status)); + torture_result(tctx, TORTURE_FAIL, "Failed to connect to server"); + return NULL; + } + + /* Setup POSIX on the server. */ + SSVAL(data, 0, CIFS_UNIX_MAJOR_VERSION); + SSVAL(data, 2, CIFS_UNIX_MINOR_VERSION); + SBVAL(data,4,((uint64_t)( + CIFS_UNIX_POSIX_ACLS_CAP| + CIFS_UNIX_POSIX_PATHNAMES_CAP| + CIFS_UNIX_FCNTL_LOCKS_CAP| + CIFS_UNIX_EXTATTR_CAP| + CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP))); + setup = TRANSACT2_SETFSINFO; + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 0; + tp.in.max_data = 0; + tp.in.setup = &setup; + tp.in.trans_name = NULL; + SSVAL(params, 0, 0); + SSVAL(params, 2, SMB_SET_CIFS_UNIX_INFO); + tp.in.params = data_blob_talloc(tctx, params, 4); + tp.in.data = data_blob_talloc(tctx, data, 12); + + status = smb_raw_trans2(cli->tree, tctx, &tp); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK, + "doing SMB_SET_CIFS_UNIX_INFO"); + + return cli; +} + +static bool check_unix_info2(struct torture_context *torture, + struct unix_info2 *info2) +{ + printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n", + (unsigned long long)info2->create_time, + info2->file_flags, info2->flags_mask); + + if (info2->file_flags == 0) { + return true; + } + + /* If we have any file_flags set, they must be within the range + * defined by flags_mask. + */ + if ((info2->flags_mask & info2->file_flags) == 0) { + torture_result(torture, TORTURE_FAIL, + __location__": UNIX_INFO2 flags field 0x%08x, " + "does not match mask 0x%08x\n", + info2->file_flags, info2->flags_mask); + } + + return true; +} + +static NTSTATUS set_path_info2(void *mem_ctx, + struct smbcli_state *cli, + const char *fname, + struct unix_info2 *info2) +{ + union smb_setfileinfo sfinfo; + + ZERO_STRUCT(sfinfo.basic_info.in); + sfinfo.generic.level = RAW_SFILEINFO_UNIX_INFO2; + sfinfo.generic.in.file.path = fname; + + sfinfo.unix_info2.in.end_of_file = info2->end_of_file; + sfinfo.unix_info2.in.num_bytes = info2->num_bytes; + sfinfo.unix_info2.in.status_change_time = info2->status_change_time; + sfinfo.unix_info2.in.access_time = info2->access_time; + sfinfo.unix_info2.in.change_time = info2->change_time; + sfinfo.unix_info2.in.uid = info2->uid; + sfinfo.unix_info2.in.gid = info2->gid; + sfinfo.unix_info2.in.file_type = info2->file_type; + sfinfo.unix_info2.in.dev_major = info2->dev_major; + sfinfo.unix_info2.in.dev_minor = info2->dev_minor; + sfinfo.unix_info2.in.unique_id = info2->unique_id; + sfinfo.unix_info2.in.permissions = info2->permissions; + sfinfo.unix_info2.in.nlink = info2->nlink; + sfinfo.unix_info2.in.create_time = info2->create_time; + sfinfo.unix_info2.in.file_flags = info2->file_flags; + sfinfo.unix_info2.in.flags_mask = info2->flags_mask; + + return smb_raw_setpathinfo(cli->tree, &sfinfo); +} + +static bool query_file_path_info2(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + int fnum, + const char *fname, + struct unix_info2 *info2) +{ + NTSTATUS result; + union smb_fileinfo finfo; + + finfo.generic.level = RAW_FILEINFO_UNIX_INFO2; + + if (fname) { + finfo.generic.in.file.path = fname; + result = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + } else { + finfo.generic.in.file.fnum = fnum; + result = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + } + + torture_assert_ntstatus_equal(torture, result, NT_STATUS_OK, + smbcli_errstr(cli->tree)); + + info2->end_of_file = finfo.unix_info2.out.end_of_file; + info2->num_bytes = finfo.unix_info2.out.num_bytes; + info2->status_change_time = finfo.unix_info2.out.status_change_time; + info2->access_time = finfo.unix_info2.out.access_time; + info2->change_time = finfo.unix_info2.out.change_time; + info2->uid = finfo.unix_info2.out.uid; + info2->gid = finfo.unix_info2.out.gid; + info2->file_type = finfo.unix_info2.out.file_type; + info2->dev_major = finfo.unix_info2.out.dev_major; + info2->dev_minor = finfo.unix_info2.out.dev_minor; + info2->unique_id = finfo.unix_info2.out.unique_id; + info2->permissions = finfo.unix_info2.out.permissions; + info2->nlink = finfo.unix_info2.out.nlink; + info2->create_time = finfo.unix_info2.out.create_time; + info2->file_flags = finfo.unix_info2.out.file_flags; + info2->flags_mask = finfo.unix_info2.out.flags_mask; + + if (!check_unix_info2(torture, info2)) { + return false; + } + + return true; +} + +static bool query_file_info2(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + int fnum, + struct unix_info2 *info2) +{ + return query_file_path_info2(mem_ctx, torture, cli, + fnum, NULL, info2); +} + +static bool query_path_info2(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + const char *fname, + struct unix_info2 *info2) +{ + return query_file_path_info2(mem_ctx, torture, cli, + -1, fname, info2); +} + +static bool search_callback(void *private_data, const union smb_search_data *fdata) +{ + struct unix_info2 *info2 = (struct unix_info2 *)private_data; + + info2->end_of_file = fdata->unix_info2.end_of_file; + info2->num_bytes = fdata->unix_info2.num_bytes; + info2->status_change_time = fdata->unix_info2.status_change_time; + info2->access_time = fdata->unix_info2.access_time; + info2->change_time = fdata->unix_info2.change_time; + info2->uid = fdata->unix_info2.uid; + info2->gid = fdata->unix_info2.gid; + info2->file_type = fdata->unix_info2.file_type; + info2->dev_major = fdata->unix_info2.dev_major; + info2->dev_minor = fdata->unix_info2.dev_minor; + info2->unique_id = fdata->unix_info2.unique_id; + info2->permissions = fdata->unix_info2.permissions; + info2->nlink = fdata->unix_info2.nlink; + info2->create_time = fdata->unix_info2.create_time; + info2->file_flags = fdata->unix_info2.file_flags; + info2->flags_mask = fdata->unix_info2.flags_mask; + + return true; +} + +static bool find_single_info2(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + const char *fname, + struct unix_info2 *info2) +{ + union smb_search_first search; + NTSTATUS status; + + /* Set up a new search for a single item, not using resume keys. */ + ZERO_STRUCT(search); + search.t2ffirst.level = RAW_SEARCH_TRANS2; + search.t2ffirst.data_level = SMB_FIND_UNIX_INFO2; + search.t2ffirst.in.max_count = 1; + search.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + search.t2ffirst.in.pattern = fname; + + status = smb_raw_search_first(cli->tree, mem_ctx, + &search, info2, search_callback); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + smbcli_errstr(cli->tree)); + + torture_assert_int_equal(torture, search.t2ffirst.out.count, 1, + "expected exactly one result"); + /* + * In smbd directory listings using POSIX extensions + * always treat the search pathname as a wildcard, + * so don't expect end_of_search to be set here. Wildcard + * searches always need a findnext to end the search. + */ + torture_assert_int_equal(torture, search.t2ffirst.out.end_of_search, 0, + "expected end_of_search to be false"); + + return check_unix_info2(torture, info2); +} + +#define ASSERT_FLAGS_MATCH(info2, expected) \ + if ((info2)->file_flags != (1 << i)) { \ + torture_result(torture, TORTURE_FAIL, \ + __location__": INFO2 flags field was 0x%08x, "\ + "expected 0x%08x\n",\ + (info2)->file_flags, expected); \ + } + +static void set_no_metadata_change(struct unix_info2 *info2) +{ + info2->uid = SMB_UID_NO_CHANGE; + info2->gid = SMB_GID_NO_CHANGE; + info2->permissions = SMB_MODE_NO_CHANGE; + + info2->end_of_file = + ((uint64_t)SMB_SIZE_NO_CHANGE_HI << 32) | SMB_SIZE_NO_CHANGE_LO; + + info2->status_change_time = + info2->access_time = + info2->change_time = + info2->create_time = + ((uint64_t)SMB_SIZE_NO_CHANGE_HI << 32) | SMB_SIZE_NO_CHANGE_LO; +} + +static bool verify_setinfo_flags(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + const char *fname) +{ + struct unix_info2 info2; + uint32_t smb_fmask; + int i; + + bool ret = true; + NTSTATUS status; + + if (!query_path_info2(mem_ctx, torture, cli, fname, &info2)) { + return false; + } + + smb_fmask = info2.flags_mask; + + /* For each possible flag, ask to set exactly 1 flag, making sure + * that flag is in our requested mask. + */ + for (i = 0; i < 32; ++i) { + info2.file_flags = ((uint32_t)1 << i); + info2.flags_mask = smb_fmask | info2.file_flags; + + set_no_metadata_change(&info2); + status = set_path_info2(mem_ctx, cli, fname, &info2); + + if (info2.file_flags & smb_fmask) { + torture_assert_ntstatus_equal(torture, + status, NT_STATUS_OK, + "setting valid UNIX_INFO2 flag"); + + if (!query_path_info2(mem_ctx, torture, cli, + fname, &info2)) { + return false; + } + + ASSERT_FLAGS_MATCH(&info2, 1 << i); + + + } else { + /* We tried to set a flag the server doesn't + * understand. + */ + torture_assert_ntstatus_equal(torture, + status, NT_STATUS_INVALID_PARAMETER, + "setting invalid UNIX_INFO2 flag"); + } + } + + /* Make sure that a zero flags field does nothing. */ + set_no_metadata_change(&info2); + info2.file_flags = 0xFFFFFFFF; + info2.flags_mask = 0; + status = set_path_info2(mem_ctx, cli, fname, &info2); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "setting empty flags mask"); + + return ret; + +} + +static int create_file(struct smbcli_state *cli, const char * fname) +{ + + return smbcli_nt_create_full(cli->tree, fname, 0, + SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, + 0, 0); +} + +static bool match_info2(struct torture_context *torture, + const struct unix_info2 *pinfo, + const struct unix_info2 *finfo) +{ + printf("checking results match\n"); + + torture_assert_u64_equal(torture, finfo->end_of_file, 0, + "end_of_file should be 0"); + torture_assert_u64_equal(torture, finfo->num_bytes, 0, + "num_bytes should be 0"); + + torture_assert_u64_equal(torture, finfo->end_of_file, + pinfo->end_of_file, "end_of_file mismatch"); + torture_assert_u64_equal(torture, finfo->num_bytes, pinfo->num_bytes, + "num_bytes mismatch"); + + /* Don't match access_time. */ + + torture_assert_u64_equal(torture, finfo->status_change_time, + pinfo->status_change_time, + "status_change_time mismatch"); + torture_assert_u64_equal(torture, finfo->change_time, + pinfo->change_time, "change_time mismatch"); + + torture_assert_u64_equal(torture, finfo->uid, pinfo->uid, + "UID mismatch"); + torture_assert_u64_equal(torture, finfo->gid, pinfo->gid, + "GID mismatch"); + torture_assert_int_equal(torture, finfo->file_type, pinfo->file_type, + "file_type mismatch"); + torture_assert_u64_equal(torture, finfo->dev_major, pinfo->dev_major, + "dev_major mismatch"); + torture_assert_u64_equal(torture, finfo->dev_minor, pinfo->dev_minor, + "dev_minor mismatch"); + torture_assert_u64_equal(torture, finfo->unique_id, pinfo->unique_id, + "unique_id mismatch"); + torture_assert_u64_equal(torture, finfo->permissions, + pinfo->permissions, "permissions mismatch"); + torture_assert_u64_equal(torture, finfo->nlink, pinfo->nlink, + "nlink mismatch"); + torture_assert_u64_equal(torture, finfo->create_time, pinfo->create_time, + "create_time mismatch"); + + return true; +} + + +#define FILENAME "\\smb_unix_info2.txt" + +bool unix_torture_unix_info2(struct torture_context *torture) +{ + void *mem_ctx; + struct smbcli_state *cli; + int fnum; + + struct unix_info2 pinfo, finfo; + + mem_ctx = talloc_init("smb_query_unix_info2"); + torture_assert(torture, mem_ctx != NULL, "out of memory"); + + if (!(cli = connect_to_server(torture))) { + talloc_free(mem_ctx); + return false; + } + + smbcli_unlink(cli->tree, FILENAME); + + fnum = create_file(cli, FILENAME); + torture_assert(torture, fnum != -1, smbcli_errstr(cli->tree)); + + printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n"); + if (!query_file_info2(mem_ctx, torture, cli, fnum, &finfo)) { + goto fail; + } + + printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n"); + if (!query_path_info2(mem_ctx, torture, cli, FILENAME, &pinfo)) { + goto fail; + } + + if (!match_info2(torture, &pinfo, &finfo)) { + goto fail; + } + + printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n"); + if (!find_single_info2(mem_ctx, torture, cli, FILENAME, &pinfo)) { + goto fail; + } + + if (!match_info2(torture, &pinfo, &finfo)) { + goto fail; + } + + /* XXX: should repeat this test with SetFileInfo. */ + printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n"); + if (!verify_setinfo_flags(mem_ctx, torture, cli, FILENAME)) { + goto fail; + } + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, FILENAME); + torture_close_connection(cli); + talloc_free(mem_ctx); + return true; + +fail: + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, FILENAME); + torture_close_connection(cli); + talloc_free(mem_ctx); + return false; + +} + +/* vim: set sts=8 sw=8 : */ diff --git a/source4/torture/unix/whoami.c b/source4/torture/unix/whoami.c new file mode 100644 index 0000000..28b1f87 --- /dev/null +++ b/source4/torture/unix/whoami.c @@ -0,0 +1,433 @@ +/* + Test the SMB_WHOAMI Unix extension. + + Copyright (C) 2007 James Peach + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/raw/raw_proto.h" +#include "torture/torture.h" +#include "torture/unix/proto.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include +#include "lib/util/util_ldb.h" +#include "ldb_wrap.h" +#include "dsdb/samdb/samdb.h" +#include "../libcli/security/security.h" + +#undef strcasecmp + +/* Size (in bytes) of the required fields in the SMBwhoami response. */ +#define WHOAMI_REQUIRED_SIZE 40 + +/* + SMBWhoami - Query the user mapping performed by the server for the + connected tree. This is a subcommand of the TRANS2_QFSINFO. + + Returns: + 4 bytes unsigned - mapping flags (smb_whoami_flags) + 4 bytes unsigned - flags mask + + 8 bytes unsigned - primary UID + 8 bytes unsigned - primary GID + 4 bytes unsigned - number of supplementary GIDs + 4 bytes unsigned - number of SIDs + 4 bytes unsigned - SID list byte count + 4 bytes - pad / reserved (must be zero) + + 8 bytes unsigned[] - list of GIDs (may be empty) + struct dom_sid[] - list of SIDs (may be empty) +*/ + +struct smb_whoami +{ + uint32_t mapping_flags; + uint32_t mapping_mask; + uint64_t server_uid; + uint64_t server_gid; + uint32_t num_gids; + uint32_t num_sids; + uint32_t num_sid_bytes; + uint32_t reserved; /* Must be zero */ + uint64_t * gid_list; + struct dom_sid ** sid_list; +}; + +static struct smbcli_state *connect_to_server(struct torture_context *tctx, + struct cli_credentials *creds) +{ + NTSTATUS status; + struct smbcli_state *cli; + + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + status = smbcli_full_connection(tctx, &cli, host, + lpcfg_smb_ports(tctx->lp_ctx), + share, NULL, lpcfg_socket_options(tctx->lp_ctx), + creds, lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, + "FATAL: Failed to connect to //%s/%s " + "with %s - %s\n", + host, + share, + cli_credentials_get_username(creds), + nt_errstr(status)); + return NULL; + } + + return cli; +} + +static bool whoami_sid_parse(void *mem_ctx, + struct torture_context *torture, + DATA_BLOB *data, size_t *offset, + struct dom_sid **psid) +{ + size_t remain = data->length - *offset; + int i; + + *psid = talloc_zero(mem_ctx, struct dom_sid); + torture_assert(torture, *psid != NULL, "out of memory"); + + torture_assert(torture, remain >= 8, + "invalid SID format"); + + (*psid)->sid_rev_num = CVAL(data->data, *offset); + (*psid)->num_auths = CVAL(data->data, *offset + 1); + memcpy((*psid)->id_auth, data->data + *offset + 2, 6); + + (*offset) += 8; + remain = data->length - *offset; + + torture_assert(torture, remain >= ((*psid)->num_auths * 4), + "invalid sub_auth byte count"); + torture_assert(torture, (*psid)->num_auths >= 0, + "invalid sub_auth value"); + torture_assert(torture, (*psid)->num_auths <= 15, + "invalid sub_auth value"); + + for (i = 0; i < (*psid)->num_auths; i++) { + (*psid)->sub_auths[i] = IVAL(data->data, *offset); + (*offset) += 4; + } + + return true; +} + +static bool smb_raw_query_posix_whoami(void *mem_ctx, + struct torture_context *torture, + struct smbcli_state *cli, + struct smb_whoami *whoami, + unsigned max_data) +{ + struct smb_trans2 tp; + NTSTATUS status; + size_t offset; + int i; + + uint16_t setup = TRANSACT2_QFSINFO; + uint16_t info_level; + + ZERO_STRUCTP(whoami); + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 10; + tp.in.max_data = (uint16_t)max_data; + tp.in.setup = &setup; + tp.in.trans_name = NULL; + SSVAL(&info_level, 0, SMB_QFS_POSIX_WHOAMI); + tp.in.params = data_blob_talloc(mem_ctx, &info_level, 2); + tp.in.data = data_blob_talloc(mem_ctx, NULL, 0); + + status = smb_raw_trans2(cli->tree, mem_ctx, &tp); + torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK, + "doing SMB_QFS_POSIX_WHOAMI"); + + /* Make sure we got back all the required fields. */ + torture_assert(torture, tp.out.params.length == 0, + "trans2 params should be empty"); + torture_assert(torture, tp.out.data.length >= WHOAMI_REQUIRED_SIZE, + "checking for required response fields"); + + whoami->mapping_flags = IVAL(tp.out.data.data, 0); + whoami->mapping_mask = IVAL(tp.out.data.data, 4); + whoami->server_uid = BVAL(tp.out.data.data, 8); + whoami->server_gid = BVAL(tp.out.data.data, 16); + whoami->num_gids = IVAL(tp.out.data.data, 24); + whoami->num_sids = IVAL(tp.out.data.data, 28); + whoami->num_sid_bytes = IVAL(tp.out.data.data, 32); + whoami->reserved = IVAL(tp.out.data.data, 36); + + /* The GID list and SID list are optional, depending on the count + * and length fields. + */ + if (whoami->num_sids != 0) { + torture_assert(torture, whoami->num_sid_bytes != 0, + "SID count does not match byte count"); + } + + printf("\tmapping_flags=0x%08x mapping_mask=0x%08x\n", + whoami->mapping_flags, whoami->mapping_mask); + printf("\tserver UID=%llu GID=%llu\n", + (unsigned long long)whoami->server_uid, (unsigned long long)whoami->server_gid); + printf("\t%u GIDs, %u SIDs, %u SID bytes\n", + whoami->num_gids, whoami->num_sids, + whoami->num_sid_bytes); + + offset = WHOAMI_REQUIRED_SIZE; + + torture_assert_int_equal(torture, whoami->reserved, 0, + "invalid reserved field"); + + if (tp.out.data.length == offset) { + /* No SIDs or GIDs returned */ + torture_assert_int_equal(torture, whoami->num_gids, 0, + "invalid GID count"); + torture_assert_int_equal(torture, whoami->num_sids, 0, + "invalid SID count"); + torture_assert_int_equal(torture, whoami->num_sid_bytes, 0, + "invalid SID byte count"); + return true; + } + + if (whoami->num_gids != 0) { + int remain = tp.out.data.length - offset; + int gid_bytes = whoami->num_gids * 8; + + if (whoami->num_sids == 0) { + torture_assert_int_equal(torture, remain, gid_bytes, + "GID count does not match data length"); + } else { + torture_assert(torture, remain > gid_bytes, + "invalid GID count"); + } + + whoami->gid_list = talloc_array(mem_ctx, uint64_t, whoami->num_gids); + torture_assert(torture, whoami->gid_list != NULL, "out of memory"); + + torture_comment(torture, "\tGIDs:\n"); + + for (i = 0; i < whoami->num_gids; ++i) { + whoami->gid_list[i] = BVAL(tp.out.data.data, offset); + offset += 8; + torture_comment(torture, "\t\t%u\n", (unsigned int)whoami->gid_list[i]); + } + } + + /* Check if there should be data left for the SID list. */ + if (tp.out.data.length == offset) { + torture_assert_int_equal(torture, whoami->num_sids, 0, + "invalid SID count"); + return true; + } + + /* All the remaining bytes must be the SID list. */ + torture_assert_int_equal(torture, + whoami->num_sid_bytes, (tp.out.data.length - offset), + "invalid SID byte count"); + + if (whoami->num_sids != 0) { + + whoami->sid_list = talloc_array(mem_ctx, struct dom_sid *, + whoami->num_sids); + torture_assert(torture, whoami->sid_list != NULL, + "out of memory"); + + torture_comment(torture, "\tSIDs:\n"); + + for (i = 0; i < whoami->num_sids; ++i) { + if (!whoami_sid_parse(mem_ctx, torture, + &tp.out.data, &offset, + &whoami->sid_list[i])) { + return false; + } + + torture_comment(torture, "\t\t%s\n", + dom_sid_string(torture, whoami->sid_list[i])); + } + } + + /* We should be at the end of the response now. */ + torture_assert_int_equal(torture, tp.out.data.length, offset, + "trailing garbage bytes"); + + return true; +} + +static bool test_against_ldap(struct torture_context *torture, struct ldb_context *ldb, bool is_dc, + struct smb_whoami *whoami) +{ + struct ldb_message *msg; + struct ldb_message_element *el; + + const char *attrs[] = { "tokenGroups", NULL }; + int i; + + torture_assert_int_equal(torture, dsdb_search_one(ldb, torture, &msg, NULL, LDB_SCOPE_BASE, attrs, 0, NULL), LDB_SUCCESS, "searching for tokenGroups"); + el = ldb_msg_find_element(msg, "tokenGroups"); + torture_assert(torture, el, "obtaining tokenGroups"); + torture_assert(torture, el->num_values > 0, "Number of SIDs from LDAP needs to be more than 0"); + torture_assert(torture, whoami->num_sids > 0, "Number of SIDs from LDAP needs to be more than 0"); + + if (is_dc) { + torture_assert_int_equal(torture, el->num_values, whoami->num_sids, "Number of SIDs from LDAP and number of SIDs from CIFS does not match!"); + + for (i = 0; i < el->num_values; i++) { + struct dom_sid *sid = talloc(torture, struct dom_sid); + ssize_t ret; + torture_assert(torture, sid != NULL, "talloc failed"); + + ret = sid_parse(el->values[i].data, + el->values[i].length, sid); + torture_assert(torture, + ret != -1, + "sid parse failed"); + torture_assert_str_equal(torture, dom_sid_string(sid, sid), dom_sid_string(sid, whoami->sid_list[i]), "SID from LDAP and SID from CIFS does not match!"); + talloc_free(sid); + } + } else { + unsigned int num_domain_sids_dc = 0, num_domain_sids_member = 0; + struct dom_sid *user_sid = talloc(torture, struct dom_sid); + struct dom_sid *dom_sid = talloc(torture, struct dom_sid); + struct dom_sid *dc_sids = talloc_array(torture, struct dom_sid, el->num_values); + struct dom_sid *member_sids = talloc_array(torture, struct dom_sid, whoami->num_sids); + ssize_t ret; + torture_assert(torture, user_sid != NULL, "talloc failed"); + ret = sid_parse(el->values[0].data, + el->values[0].length, + user_sid); + torture_assert(torture, + ret != -1, + "sid parse failed"); + torture_assert_ntstatus_equal(torture, dom_sid_split_rid(torture, user_sid, &dom_sid, NULL), NT_STATUS_OK, "failed to split domain SID from user SID"); + for (i = 0; i < el->num_values; i++) { + struct dom_sid *sid = talloc(dc_sids, struct dom_sid); + torture_assert(torture, sid != NULL, "talloc failed"); + + ret = sid_parse(el->values[i].data, + el->values[i].length, + sid); + torture_assert(torture, + ret != -1, + "sid parse failed"); + if (dom_sid_in_domain(dom_sid, sid)) { + dc_sids[num_domain_sids_dc] = *sid; + num_domain_sids_dc++; + } + talloc_free(sid); + } + + for (i = 0; i < whoami->num_sids; i++) { + if (dom_sid_in_domain(dom_sid, whoami->sid_list[i])) { + member_sids[num_domain_sids_member] = *whoami->sid_list[i]; + num_domain_sids_member++; + } + } + + torture_assert_int_equal(torture, num_domain_sids_dc, num_domain_sids_member, "Number of Domain SIDs from LDAP DC and number of SIDs from CIFS member does not match!"); + for (i = 0; i < num_domain_sids_dc; i++) { + torture_assert_str_equal(torture, dom_sid_string(dc_sids, &dc_sids[i]), dom_sid_string(member_sids, &member_sids[i]), "Domain SID from LDAP DC and SID from CIFS member server does not match!"); + } + talloc_free(dc_sids); + talloc_free(member_sids); + } + return true; +} + +bool torture_unix_whoami(struct torture_context *torture) +{ + struct smbcli_state *cli; + struct smb_whoami whoami; + bool ret = false; + struct ldb_context *ldb; + const char *addc, *host; + + cli = connect_to_server(torture, samba_cmdline_get_creds()); + torture_assert(torture, cli, "connecting to server with authenticated credentials"); + + /* Test basic authenticated mapping. */ + torture_assert_goto(torture, smb_raw_query_posix_whoami(torture, torture, + cli, &whoami, 0xFFFF), ret, fail, + "calling SMB_QFS_POSIX_WHOAMI on an authenticated connection"); + + /* Check that our anonymous login mapped us to guest on the server, but + * only if the server supports this. + */ + if (whoami.mapping_mask & SMB_WHOAMI_GUEST) { + bool guest = whoami.mapping_flags & SMB_WHOAMI_GUEST; + torture_comment(torture, "checking whether we were logged in as guest... %s\n", + guest ? "YES" : "NO"); + torture_assert(torture, + cli_credentials_is_anonymous( + samba_cmdline_get_creds()) == guest, + "login did not credentials map to guest"); + } else { + torture_comment(torture, "server does not support SMB_WHOAMI_GUEST flag\n"); + } + + addc = torture_setting_string(torture, "addc", NULL); + host = torture_setting_string(torture, "host", NULL); + + if (addc) { + ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, talloc_asprintf(torture, "ldap://%s", addc), + NULL, samba_cmdline_get_creds(), 0); + torture_assert(torture, ldb, "ldb connect failed"); + + /* We skip this testing if we could not contact the LDAP server */ + if (!test_against_ldap(torture, ldb, strcasecmp(addc, host) == 0, &whoami)) { + goto fail; + } + } + + /* Test that the server drops the UID and GID list. */ + torture_assert_goto(torture, smb_raw_query_posix_whoami(torture, torture, + cli, &whoami, 0x40), ret, fail, + "calling SMB_QFS_POSIX_WHOAMI with a small buffer\n"); + + torture_assert_int_equal(torture, whoami.num_gids, 0, + "invalid GID count"); + torture_assert_int_equal(torture, whoami.num_sids, 0, + "invalid SID count"); + torture_assert_int_equal(torture, whoami.num_sid_bytes, 0, + "invalid SID bytes count"); + + smbcli_tdis(cli); + + return true; +fail: + + smbcli_tdis(cli); + return ret; +} + +/* vim: set sts=8 sw=8 : */ diff --git a/source4/torture/util.h b/source4/torture/util.h new file mode 100644 index 0000000..385ee15 --- /dev/null +++ b/source4/torture/util.h @@ -0,0 +1,121 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TORTURE_UTIL_H_ +#define _TORTURE_UTIL_H_ + +#include "lib/torture/torture.h" + +struct smbcli_state; +struct smbcli_tree; +struct cli_credentials; + +/** + * Useful target macros for handling server bugs in torture tests. + */ +#define TARGET_IS_WINXP(_tctx) (torture_setting_bool(_tctx, "winxp", false)) +#define TARGET_IS_W2K3(_tctx) (torture_setting_bool(_tctx, "w2k3", false)) +#define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false)) +#define TARGET_IS_W2K12(_tctx) (torture_setting_bool(_tctx, "w2k12", false)) +#define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false)) +#define TARGET_IS_SAMBA3(_tctx) (torture_setting_bool(_tctx, "samba3", false)) +#define TARGET_IS_SAMBA4(_tctx) (torture_setting_bool(_tctx, "samba4", false)) + +/** + setup a directory ready for a test +*/ +_PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname); +NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum); + +/** + sometimes we need a fairly complex file to work with, so we can test + all possible attributes. +*/ +_PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname); +int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname); + +/** + check that a wire string matches the flags specified + not 100% accurate, but close enough for testing +*/ +bool wire_bad_flags(struct smb_wire_string *str, int flags, + struct smbcli_transport *transport); +void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo); +void torture_all_info(struct smbcli_tree *tree, const char *fname); +bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib); +NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum); +NTSTATUS torture_check_ea(struct smbcli_state *cli, + const char *fname, const char *eaname, const char *value); +_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx, + struct smbcli_state **c, + struct torture_context *tctx, + const char *hostname, + const char *sharename, + struct tevent_context *ev); +_PUBLIC_ bool torture_get_conn_index(int conn_index, + TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + char **host, char **share); +_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c, + int conn_index, + struct torture_context *tctx, + struct tevent_context *ev); +_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index); +_PUBLIC_ bool torture_close_connection(struct smbcli_state *c); +_PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, + uint8_t eclass, uint32_t ecode, NTSTATUS nterr); +double torture_create_procs(struct torture_context *tctx, + bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result); +_PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + int i)); +_PUBLIC_ struct torture_test *torture_suite_add_2smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + struct smbcli_state *)); +_PUBLIC_ struct torture_test *torture_suite_add_1smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, struct smbcli_state *)); +NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx, + struct smbcli_session *session, + const char *sharename, + struct smbcli_tree **res); + + +NTSTATUS torture_check_privilege(struct smbcli_state *cli, + const char *sid_str, + const char *privilege); + +/* + * Use this to pass a 2nd user: + * + * --option='torture:user2name=user2' + * --option='torture:user2domain=domain2' + * --option='torture:user2password=password2' + */ +struct cli_credentials *torture_user2_credentials(struct torture_context *tctx, + TALLOC_CTX *mem_ctx); + +#endif /* _TORTURE_UTIL_H_ */ diff --git a/source4/torture/util_smb.c b/source4/torture/util_smb.c new file mode 100644 index 0000000..6924164 --- /dev/null +++ b/source4/torture/util_smb.c @@ -0,0 +1,1020 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester utility functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Jelmer Vernooij 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "../libcli/smb/smb_constants.h" +#include "libcli/libcli.h" +#include "system/filesys.h" +#include "system/shmem.h" +#include "system/wait.h" +#include "system/time.h" +#include "torture/torture.h" +#include "../lib/util/dlinklist.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "libcli/security/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/util/clilsa.h" +#include "torture/util.h" +#include "libcli/smb/smbXcli_base.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" + +/** + setup a directory ready for a test +*/ +_PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname) +{ + smb_raw_exit(cli->session); + if (smbcli_deltree(cli->tree, dname) == -1 || + NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) { + printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree)); + return false; + } + return true; +} + +/* + create a directory, returning a handle to it +*/ +NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum) +{ + NTSTATUS status; + union smb_open io; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named_const(tree, 0, "create_directory_handle"); + + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = dname; + + status = smb_raw_open(tree, mem_ctx, &io); + talloc_free(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + *fnum = io.ntcreatex.out.file.fnum; + } + + return status; +} + + +/** + sometimes we need a fairly complex file to work with, so we can test + all possible attributes. +*/ +_PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname) +{ + int fnum; + char buf[7] = "abc"; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smbcli_unlink(cli->tree, fname); + fnum = smbcli_nt_create_full(cli->tree, fname, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + if (fnum == -1) return -1; + + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + + if (strchr(fname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + } + + /* make sure all the timestamps aren't the same */ + ZERO_STRUCT(setfile); + setfile.generic.level = RAW_SFILEINFO_BASIC_INFO; + setfile.generic.in.file.fnum = fnum; + + unix_to_nt_time(&setfile.basic_info.in.create_time, + t + 9*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.access_time, + t + 6*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.write_time, + t + 3*30*24*60*60); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO; + fileinfo.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + + +/* + sometimes we need a fairly complex directory to work with, so we can test + all possible attributes. +*/ +int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname) +{ + int fnum; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smbcli_deltree(cli->tree, dname); + fnum = smbcli_nt_create_full(cli->tree, dname, 0, + SEC_RIGHTS_DIR_ALL, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DIRECTORY, 0); + if (fnum == -1) return -1; + + if (strchr(dname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.in.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + } + + /* make sure all the timestamps aren't the same */ + ZERO_STRUCT(setfile); + setfile.generic.level = RAW_SFILEINFO_BASIC_INFO; + setfile.generic.in.file.fnum = fnum; + + unix_to_nt_time(&setfile.basic_info.in.create_time, + t + 9*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.access_time, + t + 6*30*24*60*60); + unix_to_nt_time(&setfile.basic_info.in.write_time, + t + 3*30*24*60*60); + + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO; + fileinfo.generic.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + +/** + check that a wire string matches the flags specified + not 100% accurate, but close enough for testing +*/ +bool wire_bad_flags(struct smb_wire_string *str, int flags, + struct smbcli_transport *transport) +{ + bool server_unicode; + int len; + if (!str || !str->s) return true; + len = strlen(str->s); + if (flags & STR_TERMINATE) len++; + + server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false; + if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) { + server_unicode = false; + } + + if ((flags & STR_UNICODE) || server_unicode) { + len *= 2; + } else if (flags & STR_TERMINATE_ASCII) { + len++; + } + if (str->private_length != len) { + printf("Expected wire_length %d but got %d for '%s'\n", + len, str->private_length, str->s); + return true; + } + return false; +} + +/* + dump a all_info QFILEINFO structure +*/ +void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo) +{ + d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time)); + d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time)); + d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time)); + d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time)); + d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib); + d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size); + d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size); + d_printf("\tnlink: %u\n", finfo->all_info.out.nlink); + d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending); + d_printf("\tdirectory: %u\n", finfo->all_info.out.directory); + d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size); + d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s); +} + +/* + dump file info by name +*/ +void torture_all_info(struct smbcli_tree *tree, const char *fname) +{ + TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname); + union smb_fileinfo finfo; + NTSTATUS status; + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s - %s\n", fname, nt_errstr(status)); + return; + } + + d_printf("%s:\n", fname); + dump_all_info(mem_ctx, &finfo); + talloc_free(mem_ctx); +} + + +/* + set a attribute on a file +*/ +bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib) +{ + union smb_setfileinfo sfinfo; + NTSTATUS status; + + ZERO_STRUCT(sfinfo.basic_info.in); + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.basic_info.in.file.path = fname; + sfinfo.basic_info.in.attrib = attrib; + status = smb_raw_setpathinfo(tree, &sfinfo); + return NT_STATUS_IS_OK(status); +} + + +/* + set a file descriptor as sparse +*/ +NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum) +{ + union smb_ioctl nt; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + nt.ntioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_SET_SPARSE; + nt.ntioctl.in.file.fnum = fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = 0; + nt.ntioctl.in.blob = data_blob(NULL, 0); + + status = smb_raw_ioctl(tree, mem_ctx, &nt); + + talloc_free(mem_ctx); + + return status; +} + +/* + check that an EA has the right value +*/ +NTSTATUS torture_check_ea(struct smbcli_state *cli, + const char *fname, const char *eaname, const char *value) +{ + union smb_fileinfo info; + NTSTATUS status; + struct ea_name ea; + TALLOC_CTX *mem_ctx = talloc_new(cli); + + info.ea_list.level = RAW_FILEINFO_EA_LIST; + info.ea_list.in.file.path = fname; + info.ea_list.in.num_names = 1; + info.ea_list.in.ea_names = &ea; + + ea.name.s = eaname; + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &info); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + if (info.ea_list.out.num_eas != 1) { + printf("Expected 1 ea in ea_list\n"); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + + if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) { + printf("Expected ea '%s' not '%s' in ea_list\n", + eaname, info.ea_list.out.eas[0].name.s); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + + if (value == NULL) { + if (info.ea_list.out.eas[0].value.length != 0) { + printf("Expected zero length ea for %s\n", eaname); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + if (strlen(value) == info.ea_list.out.eas[0].value.length && + memcmp(value, info.ea_list.out.eas[0].value.data, + info.ea_list.out.eas[0].value.length) == 0) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + printf("Expected value '%s' not '%*.*s' for ea %s\n", + value, + (int)info.ea_list.out.eas[0].value.length, + (int)info.ea_list.out.eas[0].value.length, + info.ea_list.out.eas[0].value.data, + eaname); + + talloc_free(mem_ctx); + + return NT_STATUS_EA_CORRUPT_ERROR; +} + +_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx, + struct smbcli_state **c, + struct torture_context *tctx, + const char *hostname, + const char *sharename, + struct tevent_context *ev) +{ + NTSTATUS status; + + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true); + options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true); + + status = smbcli_full_connection(mem_ctx, c, hostname, + lpcfg_smb_ports(tctx->lp_ctx), + sharename, NULL, + lpcfg_socket_options(tctx->lp_ctx), + samba_cmdline_get_creds(), + lpcfg_resolve_context(tctx->lp_ctx), + ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open connection - %s\n", nt_errstr(status)); + return false; + } + + return true; +} + +_PUBLIC_ bool torture_get_conn_index(int conn_index, + TALLOC_CTX *mem_ctx, + struct torture_context *tctx, + char **host, char **share) +{ + char **unc_list = NULL; + int num_unc_names = 0; + const char *p; + + (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL)); + (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL)); + + p = torture_setting_string(tctx, "unclist", NULL); + if (!p) { + return true; + } + + unc_list = file_lines_load(p, &num_unc_names, 0, NULL); + if (!unc_list || num_unc_names <= 0) { + DEBUG(0,("Failed to load unc names list from '%s'\n", p)); + return false; + } + + p = unc_list[conn_index % num_unc_names]; + if (p[0] != '/' && p[0] != '\\') { + /* allow UNC lists of hosts */ + (*host) = talloc_strdup(mem_ctx, p); + } else if (!smbcli_parse_unc(p, mem_ctx, host, share)) { + DEBUG(0, ("Failed to parse UNC name %s\n", + unc_list[conn_index % num_unc_names])); + return false; + } + + talloc_free(unc_list); + return true; +} + + + +_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c, + int conn_index, + struct torture_context *tctx, + struct tevent_context *ev) +{ + char *host, *share; + bool ret; + + if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) { + return false; + } + + ret = torture_open_connection_share(NULL, c, tctx, host, share, ev); + talloc_free(host); + talloc_free(share); + + return ret; +} + +_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index) +{ + return torture_open_connection_ev(c, conn_index, tctx, tctx->ev); +} + + + +_PUBLIC_ bool torture_close_connection(struct smbcli_state *c) +{ + bool ret = true; + if (!c) return true; + if (NT_STATUS_IS_ERR(smbcli_tdis(c))) { + printf("tdis failed (%s)\n", smbcli_errstr(c->tree)); + ret = false; + } + talloc_free(c); + return ret; +} + + +/* check if the server produced the expected error code */ +_PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, + uint8_t eclass, uint32_t ecode, NTSTATUS nterr) +{ + NTSTATUS status; + + status = smbcli_nt_error(c->tree); + if (NT_STATUS_IS_DOS(status)) { + int classnum, num; + classnum = NT_STATUS_DOS_CLASS(status); + num = NT_STATUS_DOS_CODE(status); + if (eclass != classnum || ecode != num) { + printf("unexpected error code %s\n", nt_errstr(status)); + printf(" expected %s or %s (at %s)\n", + nt_errstr(NT_STATUS_DOS(eclass, ecode)), + nt_errstr(nterr), location); + return false; + } + } else { + if (!NT_STATUS_EQUAL(nterr, status)) { + printf("unexpected error code %s\n", nt_errstr(status)); + printf(" expected %s (at %s)\n", nt_errstr(nterr), location); + return false; + } + } + + return true; +} + +static struct smbcli_state *current_cli; +static int procnum; /* records process count number when forking */ + +static void sigcont(int sig) +{ +} + +struct child_status { + pid_t pid; + bool start; + enum torture_result result; + char reason[1024]; +}; + +double torture_create_procs(struct torture_context *tctx, + bool (*fn)(struct torture_context *, struct smbcli_state *, int), + bool *result) +{ + int status; + size_t i; + struct child_status *child_status; + size_t synccount; + size_t tries = 8; + size_t torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + double start_time_limit = 10 + (torture_nprocs * 1.5); + struct timeval tv; + + *result = true; + + synccount = 0; + + signal(SIGCONT, sigcont); + + child_status = (struct child_status *)anonymous_shared_allocate( + sizeof(struct child_status)*torture_nprocs); + if (child_status == NULL) { + printf("Failed to setup shared memory\n"); + return -1; + } + + for (i = 0; i < torture_nprocs; i++) { + ZERO_STRUCT(child_status[i]); + } + + tv = timeval_current(); + + for (i=0;ilp_ctx, "netbios name", myname); + free(myname); + + + while (1) { + if (torture_open_connection(¤t_cli, tctx, i)) { + break; + } + if (tries-- == 0) { + printf("pid %d failed to start\n", (int)getpid()); + _exit(1); + } + smb_msleep(100); + } + + child_status[i].pid = getpid(); + + pause(); + + if (!child_status[i].start) { + child_status[i].result = TORTURE_ERROR; + printf("Child %zu failed to start!\n", i); + _exit(1); + } + + ok = fn(tctx, current_cli, i); + if (!ok) { + if (tctx->last_result == TORTURE_OK) { + torture_result(tctx, TORTURE_ERROR, + "unknown error: missing " + "torture_result call?\n"); + } + + child_status[i].result = tctx->last_result; + + if (strlen(tctx->last_reason) > 1023) { + /* note: reason already contains \n */ + torture_comment(tctx, + "child %zu (pid %u) failed: %s", + i, + (unsigned)child_status[i].pid, + tctx->last_reason); + } + + snprintf(child_status[i].reason, + 1024, "child %zu (pid %u) failed: %s", + i, (unsigned)child_status[i].pid, + tctx->last_reason); + /* ensure proper "\n\0" termination: */ + if (child_status[i].reason[1022] != '\0') { + child_status[i].reason[1022] = '\n'; + child_status[i].reason[1023] = '\0'; + } + } + _exit(0); + } + } + + do { + synccount = 0; + for (i=0;ifn; + bool result; + + torture_create_procs(torture, fn, &result); + + return result; +} + +_PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + int i)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_smb_multi_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; + +} + +static bool wrap_simple_2smb_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smbcli_state *, + struct smbcli_state *); + bool ret = true; + + struct smbcli_state *cli1 = NULL, *cli2 = NULL; + + torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection"); + torture_assert_goto(torture_ctx, torture_open_connection(&cli2, torture_ctx, 1), ret, fail, "Failed to open connection"); + + fn = test->fn; + + ret = fn(torture_ctx, cli1, cli2); +fail: + talloc_free(cli1); + talloc_free(cli2); + + return ret; +} + + + +_PUBLIC_ struct torture_test *torture_suite_add_2smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, + struct smbcli_state *, + struct smbcli_state *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_2smb_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; + +} + +static bool wrap_simple_1smb_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smbcli_state *); + bool ret = true; + + struct smbcli_state *cli1 = NULL; + + torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection"); + + fn = test->fn; + + ret = fn(torture_ctx, cli1); +fail: + talloc_free(cli1); + + return ret; +} + +_PUBLIC_ struct torture_test *torture_suite_add_1smb_test( + struct torture_suite *suite, + const char *name, + bool (*run) (struct torture_context *, struct smbcli_state *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_simple_1smb_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + + +NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx, + struct smbcli_session *session, + const char *sharename, + struct smbcli_tree **res) +{ + union smb_tcon tcon; + struct smbcli_tree *result; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + + if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) { + return NT_STATUS_NO_MEMORY; + } + + result = smbcli_tree_init(session, tmp_ctx, false); + if (result == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE; + tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES; + + /* Ignore share mode security here */ + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = "?????"; + + status = smb_raw_tcon(result, tmp_ctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + + result->tid = tcon.tconx.out.tid; + + if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) { + smb1cli_session_protect_session_key(result->session->smbXcli); + } + + *res = talloc_steal(mem_ctx, result); + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* + a wrapper around smblsa_sid_check_privilege, that tries to take + account of the fact that the lsa privileges calls don't expand + group memberships, using an explicit check for administrator. There + must be a better way ... + */ +NTSTATUS torture_check_privilege(struct smbcli_state *cli, + const char *sid_str, + const char *privilege) +{ + struct dom_sid *sid; + TALLOC_CTX *tmp_ctx = talloc_new(cli); + uint32_t rid; + NTSTATUS status; + + sid = dom_sid_parse_talloc(tmp_ctx, sid_str); + if (sid == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_INVALID_SID; + } + + status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(tmp_ctx); + return status; + } + + if (rid == DOMAIN_RID_ADMINISTRATOR) { + /* assume the administrator has them all */ + return NT_STATUS_OK; + } + + talloc_free(tmp_ctx); + + return smblsa_sid_check_privilege(cli, sid_str, privilege); +} + +/* + * Use this to pass a 2nd user: + * + * --option='torture:user2name=user2' + * --option='torture:user2domain=domain2' + * --option='torture:user2password=password2' + */ +struct cli_credentials *torture_user2_credentials(struct torture_context *tctx, + TALLOC_CTX *mem_ctx) +{ + struct cli_credentials *credentials1 = samba_cmdline_get_creds(); + const char *user1domain = cli_credentials_get_domain(credentials1); + const char *user2name = torture_setting_string(tctx, "user2name", NULL); + const char *user2domain = torture_setting_string(tctx, "user2domain", user1domain); + const char *user2password = torture_setting_string(tctx, "user2password", NULL); + struct cli_credentials *credentials2 = NULL; + + credentials2 = cli_credentials_shallow_copy(mem_ctx, credentials1); + if (credentials2 == NULL) { + torture_comment(tctx, + "%s: cli_credentials_shallow_copy() failed\n", + __func__); + return NULL; + } + if (user2name != NULL) { + torture_comment(tctx, + "Using " + "'torture:user2name'='%s' " + "'torture:user2domain'='%s' " + "'torture:user2password'='REDACTED'", + user2name, + user2domain); + cli_credentials_set_username(credentials2, user2name, CRED_SPECIFIED); + cli_credentials_set_domain(credentials2, user2domain, CRED_SPECIFIED); + cli_credentials_set_password(credentials2, user2password, CRED_SPECIFIED); + } else { + torture_comment(tctx, + "Fallback to anonymous for " + "'torture:user2name'=NULL " + "'torture:user2domain'='%s' " + "'torture:user2password'='REDACTED'", + user2domain); + cli_credentials_set_anonymous(credentials2); + } + + return credentials2; +} diff --git a/source4/torture/vfs/acl_xattr.c b/source4/torture/vfs/acl_xattr.c new file mode 100644 index 0000000..1deb2b3 --- /dev/null +++ b/source4/torture/vfs/acl_xattr.c @@ -0,0 +1,281 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Ralph Boehme 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/vfs/proto.h" +#include "libcli/resolve/resolve.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "lib/param/param.h" + +#define BASEDIR "smb2-testsd" + +#define CHECK_SECURITY_DESCRIPTOR(_sd1, _sd2) do { \ + if (!security_descriptor_equal(_sd1, _sd2)) { \ + torture_warning(tctx, "security descriptors don't match!\n"); \ + torture_warning(tctx, "got:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd1); \ + torture_warning(tctx, "expected:\n"); \ + NDR_PRINT_DEBUG(security_descriptor, _sd2); \ + torture_result(tctx, TORTURE_FAIL, \ + "%s: security descriptors don't match!\n", \ + __location__); \ + ret = false; \ + } \ +} while (0) + +static bool test_default_acl_posix(struct torture_context *tctx, + struct smb2_tree *tree_unused) +{ + struct smb2_tree *tree = NULL; + NTSTATUS status; + bool ok; + bool ret = true; + const char *dname = BASEDIR "\\testdir"; + const char *fname = BASEDIR "\\testdir\\testfile"; + struct smb2_handle fhandle = {{0}}; + struct smb2_handle dhandle = {{0}}; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd = NULL; + struct security_descriptor *exp_sd = NULL; + char *owner_sid = NULL; + char *group_sid = NULL; + + ok = torture_smb2_con_share(tctx, "acl_xattr_ign_sysacl_posix", &tree); + torture_assert_goto(tctx, ok == true, ret, done, + "Unable to connect to 'acl_xattr_ign_sysacl_posix'\n"); + + ok = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ok == true, ret, done, "Unable to setup testdir\n"); + + ZERO_STRUCT(dhandle); + status = torture_smb2_testdir(tree, dname, &dhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir\n"); + + torture_comment(tctx, "Get the original sd\n"); + + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = dhandle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file\n"); + + sd = q.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd->owner_sid); + group_sid = dom_sid_string(tctx, sd->group_sid); + torture_comment(tctx, "owner [%s] group [%s]\n", owner_sid, group_sid); + + torture_comment(tctx, "Set ACL with no inheritable ACE\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_DIR_ALL, + 0, + NULL); + + ZERO_STRUCT(set); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = dhandle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_setinfo_file\n"); + + TALLOC_FREE(sd); + smb2_util_close(tree, dhandle); + + torture_comment(tctx, "Create file\n"); + + ZERO_STRUCT(fhandle); + status = torture_smb2_testfile(tree, fname, &fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_complex_file\n"); + + torture_comment(tctx, "Query file SD\n"); + + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = fhandle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file\n"); + sd = q.query_secdesc.out.sd; + + smb2_util_close(tree, fhandle); + ZERO_STRUCT(fhandle); + + torture_comment(tctx, "Checking actual file SD against expected SD\n"); + + exp_sd = security_descriptor_dacl_create( + tctx, 0, owner_sid, group_sid, + owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_RIGHTS_FILE_ALL, 0, + group_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, FILE_GENERIC_READ|FILE_GENERIC_WRITE|FILE_GENERIC_EXECUTE, 0, + SID_WORLD, SEC_ACE_TYPE_ACCESS_ALLOWED, FILE_GENERIC_READ|FILE_GENERIC_WRITE|FILE_GENERIC_EXECUTE, 0, + SID_NT_SYSTEM, SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_RIGHTS_FILE_ALL, 0, + NULL); + + CHECK_SECURITY_DESCRIPTOR(sd, exp_sd); + +done: + if (!smb2_util_handle_empty(fhandle)) { + smb2_util_close(tree, fhandle); + } + if (!smb2_util_handle_empty(dhandle)) { + smb2_util_close(tree, dhandle); + } + if (tree != NULL) { + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + } + + return ret; +} + +static bool test_default_acl_win(struct torture_context *tctx, + struct smb2_tree *tree_unused) +{ + struct smb2_tree *tree = NULL; + NTSTATUS status; + bool ok; + bool ret = true; + const char *dname = BASEDIR "\\testdir"; + const char *fname = BASEDIR "\\testdir\\testfile"; + struct smb2_handle fhandle = {{0}}; + struct smb2_handle dhandle = {{0}}; + union smb_fileinfo q; + union smb_setfileinfo set; + struct security_descriptor *sd = NULL; + struct security_descriptor *exp_sd = NULL; + char *owner_sid = NULL; + char *group_sid = NULL; + + ok = torture_smb2_con_share(tctx, "acl_xattr_ign_sysacl_windows", &tree); + torture_assert_goto(tctx, ok == true, ret, done, + "Unable to connect to 'acl_xattr_ign_sysacl_windows'\n"); + + ok = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ok == true, ret, done, "Unable to setup testdir\n"); + + ZERO_STRUCT(dhandle); + status = torture_smb2_testdir(tree, dname, &dhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir\n"); + + torture_comment(tctx, "Get the original sd\n"); + + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = dhandle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file\n"); + + sd = q.query_secdesc.out.sd; + owner_sid = dom_sid_string(tctx, sd->owner_sid); + group_sid = dom_sid_string(tctx, sd->group_sid); + torture_comment(tctx, "owner [%s] group [%s]\n", owner_sid, group_sid); + + torture_comment(tctx, "Set ACL with no inheritable ACE\n"); + + sd = security_descriptor_dacl_create(tctx, + 0, NULL, NULL, + owner_sid, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_DIR_ALL, + 0, + NULL); + + ZERO_STRUCT(set); + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = dhandle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = sd; + status = smb2_setinfo_file(tree, &set); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_setinfo_file\n"); + + TALLOC_FREE(sd); + smb2_util_close(tree, dhandle); + + torture_comment(tctx, "Create file\n"); + + ZERO_STRUCT(fhandle); + status = torture_smb2_testfile(tree, fname, &fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_complex_file\n"); + + torture_comment(tctx, "Query file SD\n"); + + ZERO_STRUCT(q); + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = fhandle; + q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP; + status = smb2_getinfo_file(tree, tctx, &q); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file\n"); + sd = q.query_secdesc.out.sd; + + smb2_util_close(tree, fhandle); + ZERO_STRUCT(fhandle); + + torture_comment(tctx, "Checking actual file SD against expected SD\n"); + + exp_sd = security_descriptor_dacl_create( + tctx, 0, owner_sid, group_sid, + owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_RIGHTS_FILE_ALL, 0, + SID_NT_SYSTEM, SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_RIGHTS_FILE_ALL, 0, + NULL); + + CHECK_SECURITY_DESCRIPTOR(sd, exp_sd); + +done: + if (!smb2_util_handle_empty(fhandle)) { + smb2_util_close(tree, fhandle); + } + if (!smb2_util_handle_empty(dhandle)) { + smb2_util_close(tree, dhandle); + } + if (tree != NULL) { + smb2_deltree(tree, BASEDIR); + smb2_tdis(tree); + } + + return ret; +} + +/* + basic testing of vfs_acl_xattr +*/ +struct torture_suite *torture_acl_xattr(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "acl_xattr"); + + torture_suite_add_1smb2_test(suite, "default-acl-style-posix", test_default_acl_posix); + torture_suite_add_1smb2_test(suite, "default-acl-style-windows", test_default_acl_win); + + suite->description = talloc_strdup(suite, "vfs_acl_xattr tests"); + + return suite; +} diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c new file mode 100644 index 0000000..b9cab0c --- /dev/null +++ b/source4/torture/vfs/fruit.c @@ -0,0 +1,8839 @@ +/* + Unix SMB/CIFS implementation. + + vfs_fruit tests + + Copyright (C) Ralph Boehme 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smb2_create_ctx.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include "MacExtensions.h" +#include "lib/util/tsort.h" + +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "torture/vfs/proto.h" +#include "librpc/gen_ndr/ndr_ioctl.h" +#include "libcli/security/dom_sid.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "libcli/security/secace.h" +#include "libcli/security/security_descriptor.h" + +#define BASEDIR "vfs_fruit_dir" +#define FNAME_CC_SRC "testfsctl.dat" +#define FNAME_CC_DST "testfsctl2.dat" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%u - should be %u\n", \ + __location__, #v, (unsigned)v, (unsigned)correct); \ + ret = false; \ + goto done; \ + }} while (0) + +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + int num_exp, + const char **exp, + bool is_dir); + +static int qsort_string(char * const *s1, char * const *s2) +{ + return strcmp(*s1, *s2); +} + +static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2) +{ + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + +/* + * REVIEW: + * This is hokey, but what else can we do? + */ +#if defined(HAVE_ATTROPEN) || defined(FREEBSD) +#define AFPINFO_EA_NETATALK "org.netatalk.Metadata" +#define AFPRESOURCE_EA_NETATALK "org.netatalk.ResourceFork" +#else +#define AFPINFO_EA_NETATALK "user.org.netatalk.Metadata" +#define AFPRESOURCE_EA_NETATALK "user.org.netatalk.ResourceFork" +#endif + +/* +The metadata xattr char buf below contains the following attributes: + +------------------------------------------------------------------------------- +Entry ID : 00000008 : File Dates Info +Offset : 00000162 : 354 +Length : 00000010 : 16 + +-DATE------: : (GMT) : (Local) +create : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014 +modify : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014 +backup : 80000000 : Unknown or Initial +access : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 1B 44 21 69 1B 44 21 69 80 00 00 00 1B 44 21 69 : .D!i.D!i.....D!i + +------------------------------------------------------------------------------- +Entry ID : 00000009 : Finder Info +Offset : 0000007A : 122 +Length : 00000020 : 32 + +-FInfo-----: +Type : 42415252 : BARR +Creator : 464F4F4F : FOOO +isAlias : 0 +Invisible : 1 +hasBundle : 0 +nameLocked : 0 +Stationery : 0 +CustomIcon : 0 +Reserved : 0 +Inited : 0 +NoINITS : 0 +Shared : 0 +SwitchLaunc: 0 +Hidden Ext : 0 +color : 000 : none +isOnDesk : 0 +Location v : 0000 : 0 +Location h : 0000 : 0 +Fldr : 0000 : .. + +-FXInfo----: +Rsvd|IconID: 0000 : 0 +Rsvd : 0000 : .. +Rsvd : 0000 : .. +Rsvd : 0000 : .. +AreInvalid : 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +CustomBadge: 0 +ObjctIsBusy: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +RoutingInfo: 0 +unknown bit: 0 +unknown bit: 0 +Rsvd|commnt: 0000 : 0 +PutAway : 00000000 : 0 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 42 41 52 52 46 4F 4F 4F 40 00 00 00 00 00 00 00 : BARRFOOO@....... +00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + +------------------------------------------------------------------------------- +Entry ID : 0000000E : AFP File Info +Offset : 00000172 : 370 +Length : 00000004 : 4 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 00 00 01 A1 : .... + */ + +char metadata_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x7a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x01, 0x72, 0x00, 0x00, + 0x00, 0x04, 0x80, 0x44, 0x45, 0x56, 0x00, 0x00, + 0x01, 0x76, 0x00, 0x00, 0x00, 0x08, 0x80, 0x49, + 0x4e, 0x4f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00, + 0x00, 0x08, 0x80, 0x53, 0x59, 0x4e, 0x00, 0x00, + 0x01, 0x86, 0x00, 0x00, 0x00, 0x08, 0x80, 0x53, + 0x56, 0x7e, 0x00, 0x00, 0x01, 0x8e, 0x00, 0x00, + 0x00, 0x04, 0x42, 0x41, 0x52, 0x52, 0x46, 0x4f, + 0x4f, 0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1b, 0x44, 0x21, 0x69, 0x1b, 0x44, + 0x21, 0x69, 0x80, 0x00, 0x00, 0x00, 0x1b, 0x44, + 0x21, 0x69, 0x00, 0x00, 0x01, 0xa1, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xe3, + 0x86, 0x53, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x01, + 0x00, 0x00 +}; + +/* +The buf below contains the following AppleDouble encoded data: + +------------------------------------------------------------------------------- +MagicNumber: 00051607 : AppleDouble +Version : 00020000 : Version 2 +Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X +Num. of ent: 0002 : 2 + +------------------------------------------------------------------------------- +Entry ID : 00000009 : Finder Info +Offset : 00000032 : 50 +Length : 00000EB0 : 3760 + +-FInfo-----: +Type : 54455354 : TEST +Creator : 534C4F57 : SLOW +isAlias : 0 +Invisible : 0 +hasBundle : 0 +nameLocked : 0 +Stationery : 0 +CustomIcon : 0 +Reserved : 0 +Inited : 0 +NoINITS : 0 +Shared : 0 +SwitchLaunc: 0 +Hidden Ext : 0 +color : 100 : blue +isOnDesk : 0 +Location v : 0000 : 0 +Location h : 0000 : 0 +Fldr : 0000 : .. + +-FXInfo----: +Rsvd|IconID: 0000 : 0 +Rsvd : 0000 : .. +Rsvd : 0000 : .. +Rsvd : 0000 : .. +AreInvalid : 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +CustomBadge: 0 +ObjctIsBusy: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +RoutingInfo: 0 +unknown bit: 0 +unknown bit: 0 +Rsvd|commnt: 0000 : 0 +PutAway : 00000000 : 0 + +-EA--------: +pad : 0000 : .. +magic : 41545452 : ATTR +debug_tag : 53D4580C : 1406425100 +total_size : 00000EE2 : 3810 +data_start : 000000BC : 188 +data_length: 0000005E : 94 +reserved[0]: 00000000 : .... +reserved[1]: 00000000 : .... +reserved[2]: 00000000 : .... +flags : 0000 : .. +num_attrs : 0002 : 2 +-EA ENTRY--: +offset : 000000BC : 188 +length : 0000005B : 91 +flags : 0000 : .. +namelen : 24 : 36 +-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 61 : com.apple.metada +00000010 : 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 54 : ta:_kMDItemUserT +00000020 : 61 67 73 00 : ags. +-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 62 70 6C 69 73 74 30 30 A5 01 02 03 04 05 54 74 : bplist00......Tt +00000010 : 65 73 74 66 00 47 00 72 00 FC 00 6E 00 0A 00 32 : estf.G.r...n...2 +00000020 : 56 4C 69 6C 61 0A 33 56 47 65 6C 62 0A 35 56 42 : VLila.3VGelb.5VB +00000030 : 6C 61 75 0A 34 08 0E 13 20 27 2E 00 00 00 00 00 : lau.4... '...... +00000040 : 00 01 01 00 00 00 00 00 00 00 06 00 00 00 00 00 : ................ +00000050 : 00 00 00 00 00 00 00 00 00 00 35 : ..........5 +-EA ENTRY--: +offset : 00000117 : 279 +length : 00000003 : 3 +flags : 0000 : .. +namelen : 08 : 8 +-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 66 6F 6F 3A 62 61 72 00 : foo:bar. +-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 62 61 7A : baz + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 54 45 53 54 53 4C 4F 57 00 08 00 00 00 00 00 00 : TESTSLOW........ +00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000020 : 00 00 41 54 54 52 53 D4 58 0C 00 00 0E E2 00 00 : ..ATTRS.X....... +00000030 : 00 BC 00 00 00 5E 00 00 00 00 00 00 00 00 00 00 : .....^.......... +00000040 : 00 00 00 00 00 02 00 00 00 BC 00 00 00 5B 00 00 : .............[.. +00000050 : 24 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 : $com.apple.metad +00000060 : 61 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 : ata:_kMDItemUser +00000070 : 54 61 67 73 00 00 00 00 01 17 00 00 00 03 00 00 : Tags............ +00000080 : 08 66 6F 6F 3A 62 61 72 00 66 62 70 6C 69 73 74 : .foo:bar.fbplist +00000090 : 30 30 A5 01 02 03 04 05 54 74 65 73 74 66 00 47 : 00......Ttestf.G +000000A0 : 00 72 00 FC 00 6E 00 0A 00 32 56 4C 69 6C 61 0A : .r...n...2VLila. +000000B0 : 33 56 47 65 6C 62 0A 35 56 42 6C 61 75 0A 34 08 : 3VGelb.5VBlau.4. +000000C0 : 0E 13 20 27 2E 00 00 00 00 00 00 01 01 00 00 00 : .. '............ +000000D0 : 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 35 62 61 7A 00 00 00 00 00 00 00 00 : ....5baz........ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +... all zeroes ... +00000EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + +------------------------------------------------------------------------------- +Entry ID : 00000002 : Resource Fork +Offset : 00000EE2 : 3810 +Length : 0000011E : 286 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000010 : 54 68 69 73 20 72 65 73 6F 75 72 63 65 20 66 6F : This resource fo +00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally +00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. +00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. + +It was created with: +$ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"' +*/ +static char osx_adouble_w_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x0e, 0xb0, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x0e, 0xe2, 0x00, 0x00, + 0x01, 0x1e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x4c, + 0x4f, 0x57, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x53, 0xd4, 0x58, 0x0c, 0x00, 0x00, 0x0e, 0xe2, + 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5b, + 0x00, 0x00, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5f, 0x6b, + 0x4d, 0x44, 0x49, 0x74, 0x65, 0x6d, 0x55, 0x73, + 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x08, 0x66, 0x6f, 0x6f, 0x3a, 0x62, + 0x61, 0x72, 0x00, 0x66, 0x62, 0x70, 0x6c, 0x69, + 0x73, 0x74, 0x30, 0x30, 0xa5, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x54, 0x74, 0x65, 0x73, 0x74, 0x66, + 0x00, 0x47, 0x00, 0x72, 0x00, 0xfc, 0x00, 0x6e, + 0x00, 0x0a, 0x00, 0x32, 0x56, 0x4c, 0x69, 0x6c, + 0x61, 0x0a, 0x33, 0x56, 0x47, 0x65, 0x6c, 0x62, + 0x0a, 0x35, 0x56, 0x42, 0x6c, 0x61, 0x75, 0x0a, + 0x34, 0x08, 0x0e, 0x13, 0x20, 0x27, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x62, + 0x61, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff +}; + +/* + * The buf below contains the following AppleDouble encoded data: + * + * ------------------------------------------------------------------------------- + * MagicNumber: 00051607 : AppleDouble + * Version : 00020000 : Version 2 + * Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X + * Num. of ent: 0002 : 2 + * + * ------------------------------------------------------------------------------- + * Entry ID : 00000002 : Resource Fork + * Offset : 00000052 : 82 + * Length : 0000011E : 286 + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ + * 00000010 : F0 F1 F2 F3 F5 F5 F6 F7 F8 F9 FA FB FC FD FE FF : ................ + * 00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally + * 00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. + * 00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ + * 00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. + * + * Entry ID : 00000009 : Finder Info + * Offset : 00000032 : 50 + * Length : 00000020 : 32 + * + * -NOTE------: cannot detect whether FInfo or DInfo. assume FInfo. + * + * -FInfo-----: + * Type : 57415645 : WAVE + * Creator : 5054756C : PTul + * isAlias : 0 + * Invisible : 0 + * hasBundle : 0 + * nameLocked : 0 + * Stationery : 0 + * CustomIcon : 0 + * Reserved : 0 + * Inited : 0 + * NoINITS : 0 + * Shared : 0 + * SwitchLaunc: 0 + * Hidden Ext : 0 + * color : 000 : none + * isOnDesk : 0 + * Location v : 0000 : 0 + * Location h : 0000 : 0 + * Fldr : 0000 : .. + * + * -FXInfo----: + * Rsvd|IconID: 0000 : 0 + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * AreInvalid : 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * CustomBadge: 0 + * ObjctIsBusy: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * RoutingInfo: 0 + * unknown bit: 0 + * unknown bit: 0 + * Rsvd|commnt: 0000 : 0 + * PutAway : 00000000 : 0 + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 57 41 56 45 50 54 75 6C 00 00 00 00 00 00 00 00 : WAVEPTul........ + * 00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * * + * It was created with: + * $ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"' + */ +static char osx_adouble_without_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x52, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, + 0x00, 0x20, 0x57, 0x41, 0x56, 0x45, 0x50, 0x54, + 0x75, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + 0xfe, 0xff, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff +}; + +/* +The buf below contains the following AppleDouble encoded data: + +------------------------------------------------------------------------------- +MagicNumber: 00051607 : AppleDouble +Version : 00020000 : Version 2 +Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X +Num. of ent: 0002 : 2 + +------------------------------------------------------------------------------- +Entry ID : 00000009 : Finder Info +Offset : 00000032 : 50 +Length : 00000EB0 : 3760 + +-FInfo-----: +Type : 54455354 : TEST +Creator : 534C4F57 : SLOW +isAlias : 0 +Invisible : 0 +hasBundle : 0 +nameLocked : 0 +Stationery : 0 +CustomIcon : 0 +Reserved : 0 +Inited : 0 +NoINITS : 0 +Shared : 0 +SwitchLaunc: 0 +Hidden Ext : 0 +color : 100 : blue +isOnDesk : 0 +Location v : 0000 : 0 +Location h : 0000 : 0 +Fldr : 0000 : .. + +-FXInfo----: +Rsvd|IconID: 0000 : 0 +Rsvd : 0000 : .. +Rsvd : 0000 : .. +Rsvd : 0000 : .. +AreInvalid : 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +CustomBadge: 0 +ObjctIsBusy: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +RoutingInfo: 0 +unknown bit: 0 +unknown bit: 0 +Rsvd|commnt: 0000 : 0 +PutAway : 00000000 : 0 + +-EA--------: +pad : 0000 : .. +magic : 41545452 : ATTR +debug_tag : 53D4580C : 1406425100 +total_size : 00000EE2 : 3810 +data_start : 000000BC : 188 +data_length: 0000005E : 94 +reserved[0]: 00000000 : .... +reserved[1]: 00000000 : .... +reserved[2]: 00000000 : .... +flags : 0000 : .. +num_attrs : 0002 : 2 +-EA ENTRY--: +offset : 000000BC : 188 +length : 0000005B : 91 +flags : 0000 : .. +namelen : 24 : 36 +-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 61 : com.apple.metada +00000010 : 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 54 : ta:_kMDItemUserT +00000020 : 61 67 73 00 : ags. +-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 62 70 6C 69 73 74 30 30 A5 01 02 03 04 05 54 74 : bplist00......Tt +00000010 : 65 73 74 66 00 47 00 72 00 FC 00 6E 00 0A 00 32 : estf.G.r...n...2 +00000020 : 56 4C 69 6C 61 0A 33 56 47 65 6C 62 0A 35 56 42 : VLila.3VGelb.5VB +00000030 : 6C 61 75 0A 34 08 0E 13 20 27 2E 00 00 00 00 00 : lau.4... '...... +00000040 : 00 01 01 00 00 00 00 00 00 00 06 00 00 00 00 00 : ................ +00000050 : 00 00 00 00 00 00 00 00 00 00 35 : ..........5 +-EA ENTRY--: +offset : 00000117 : 279 +length : 00000003 : 3 +flags : 0000 : .. +namelen : 08 : 8 +-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 66 6F 6F 3A 62 61 72 00 : foo:bar. +-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 62 61 7A : baz + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 54 45 53 54 53 4C 4F 57 00 08 00 00 00 00 00 00 : TESTSLOW........ +00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000020 : 00 00 41 54 54 52 53 D4 58 0C 00 00 0E E2 00 00 : ..ATTRS.X....... +00000030 : 00 BC 00 00 00 5E 00 00 00 00 00 00 00 00 00 00 : .....^.......... +00000040 : 00 00 00 00 00 02 00 00 00 BC 00 00 00 5B 00 00 : .............[.. +00000050 : 24 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 : $com.apple.metad +00000060 : 61 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 : ata:_kMDItemUser +00000070 : 54 61 67 73 00 00 00 00 01 17 00 00 00 03 00 00 : Tags............ +00000080 : 08 66 6F 6F 3A 62 61 72 00 66 62 70 6C 69 73 74 : .foo:bar.fbplist +00000090 : 30 30 A5 01 02 03 04 05 54 74 65 73 74 66 00 47 : 00......Ttestf.G +000000A0 : 00 72 00 FC 00 6E 00 0A 00 32 56 4C 69 6C 61 0A : .r...n...2VLila. +000000B0 : 33 56 47 65 6C 62 0A 35 56 42 6C 61 75 0A 34 08 : 3VGelb.5VBlau.4. +000000C0 : 0E 13 20 27 2E 00 00 00 00 00 00 01 01 00 00 00 : .. '............ +000000D0 : 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 35 62 61 7A 00 00 00 00 00 00 00 00 : ....5baz........ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +... all zeroes ... +00000EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + +------------------------------------------------------------------------------- +Entry ID : 00000002 : Resource Fork +Offset : 00000EE2 : 3810 +Length : 0000011E : 286 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000010 : F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF : This resource fo +00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally +00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. +00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. + +It was created with: +$ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"' +*/ +static char osx_adouble_non_empty_rfork_w_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x0e, 0xb0, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x0e, 0xe2, 0x00, 0x00, + 0x01, 0x1e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x4c, + 0x4f, 0x57, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x53, 0xd4, 0x58, 0x0c, 0x00, 0x00, 0x0e, 0xe2, + 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5b, + 0x00, 0x00, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5f, 0x6b, + 0x4d, 0x44, 0x49, 0x74, 0x65, 0x6d, 0x55, 0x73, + 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x08, 0x66, 0x6f, 0x6f, 0x3a, 0x62, + 0x61, 0x72, 0x00, 0x66, 0x62, 0x70, 0x6c, 0x69, + 0x73, 0x74, 0x30, 0x30, 0xa5, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x54, 0x74, 0x65, 0x73, 0x74, 0x66, + 0x00, 0x47, 0x00, 0x72, 0x00, 0xfc, 0x00, 0x6e, + 0x00, 0x0a, 0x00, 0x32, 0x56, 0x4c, 0x69, 0x6c, + 0x61, 0x0a, 0x33, 0x56, 0x47, 0x65, 0x6c, 0x62, + 0x0a, 0x35, 0x56, 0x42, 0x6c, 0x61, 0x75, 0x0a, + 0x34, 0x08, 0x0e, 0x13, 0x20, 0x27, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x62, + 0x61, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + 0xfe, 0xff, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff +}; + +/** + * talloc and intialize an AfpInfo + **/ +static AfpInfo *torture_afpinfo_new(TALLOC_CTX *mem_ctx) +{ + AfpInfo *info; + + info = talloc_zero(mem_ctx, AfpInfo); + if (info == NULL) { + return NULL; + } + + info->afpi_Signature = AFP_Signature; + info->afpi_Version = AFP_Version; + info->afpi_BackupTime = AFP_BackupTime; + + return info; +} + +/** + * Pack AfpInfo into a talloced buffer + **/ +static char *torture_afpinfo_pack(TALLOC_CTX *mem_ctx, + AfpInfo *info) +{ + char *buf; + + buf = talloc_zero_array(mem_ctx, char, AFP_INFO_SIZE); + if (buf == NULL) { + return NULL; + } + + RSIVAL(buf, 0, info->afpi_Signature); + RSIVAL(buf, 4, info->afpi_Version); + RSIVAL(buf, 12, info->afpi_BackupTime); + memcpy(buf + 16, info->afpi_FinderInfo, sizeof(info->afpi_FinderInfo)); + + return buf; +} + +/** + * Unpack AfpInfo + **/ +#if 0 +static void torture_afpinfo_unpack(AfpInfo *info, char *data) +{ + info->afpi_Signature = RIVAL(data, 0); + info->afpi_Version = RIVAL(data, 4); + info->afpi_BackupTime = RIVAL(data, 12); + memcpy(info->afpi_FinderInfo, (const char *)data + 16, + sizeof(info->afpi_FinderInfo)); +} +#endif + +static bool torture_write_afpinfo(struct smb2_tree *tree, + struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *fname, + AfpInfo *info) +{ + struct smb2_handle handle; + struct smb2_create io; + NTSTATUS status; + const char *full_name; + char *infobuf; + bool ret = true; + + full_name = talloc_asprintf(mem_ctx, "%s%s", fname, AFPINFO_STREAM_NAME); + if (full_name == NULL) { + torture_comment(tctx, "talloc_asprintf error\n"); + return false; + } + ZERO_STRUCT(io); + io.in.desired_access = SEC_FILE_WRITE_DATA; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.create_options = 0; + io.in.fname = full_name; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + handle = io.out.file.handle; + + infobuf = torture_afpinfo_pack(mem_ctx, info); + if (infobuf == NULL) { + return false; + } + + status = smb2_util_write(tree, handle, infobuf, 0, AFP_INFO_SIZE); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, handle); + +done: + return ret; +} + +/** + * Read 'count' bytes at 'offset' from stream 'fname:sname' and + * compare against buffer 'value' + **/ +static bool check_stream(struct smb2_tree *tree, + const char *location, + struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + off_t read_offset, + size_t read_count, + off_t comp_offset, + size_t comp_count, + const char *value) +{ + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read r; + NTSTATUS status; + char *full_name; + bool ret = true; + + full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname); + if (full_name == NULL) { + torture_comment(tctx, "talloc_asprintf error\n"); + return false; + } + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_DATA; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = full_name; + + torture_comment(tctx, "Open stream %s\n", full_name); + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + if (value == NULL) { + TALLOC_FREE(full_name); + return true; + } + torture_comment(tctx, "Unable to open stream %s: %s\n", + full_name, nt_errstr(status)); + TALLOC_FREE(full_name); + return false; + } + + handle = create.out.file.handle; + if (value == NULL) { + TALLOC_FREE(full_name); + smb2_util_close(tree, handle); + return true; + } + + ZERO_STRUCT(r); + r.in.file.handle = handle; + r.in.length = read_count; + r.in.offset = read_offset; + + status = smb2_read(tree, tree, &r); + + torture_assert_ntstatus_ok_goto( + tctx, status, ret, done, + talloc_asprintf(tctx, "(%s) Failed to read %lu bytes from stream '%s'\n", + location, (long)strlen(value), full_name)); + + torture_assert_goto(tctx, r.out.data.length == read_count, ret, done, + talloc_asprintf(tctx, "smb2_read returned %jd bytes, expected %jd\n", + (intmax_t)r.out.data.length, (intmax_t)read_count)); + + torture_assert_goto( + tctx, memcmp(r.out.data.data + comp_offset, value, comp_count) == 0, + ret, done, + talloc_asprintf(tctx, "(%s) Bad data in stream\n", location)); + +done: + TALLOC_FREE(full_name); + smb2_util_close(tree, handle); + return ret; +} + +/** + * Read 'count' bytes at 'offset' from stream 'fname:sname' and + * compare against buffer 'value' + **/ +static ssize_t read_stream(struct smb2_tree *tree, + const char *location, + struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + off_t read_offset, + size_t read_count) +{ + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read r; + NTSTATUS status; + const char *full_name; + bool ret = true; + + full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname); + if (full_name == NULL) { + torture_comment(tctx, "talloc_asprintf error\n"); + return -1; + } + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_DATA; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = full_name; + + torture_comment(tctx, "Open stream %s\n", full_name); + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Unable to open stream %s: %s\n", + full_name, nt_errstr(status)); + return -1; + } + + handle = create.out.file.handle; + + ZERO_STRUCT(r); + r.in.file.handle = handle; + r.in.length = read_count; + r.in.offset = read_offset; + + status = smb2_read(tree, tree, &r); + if (!NT_STATUS_IS_OK(status)) { + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + } + + smb2_util_close(tree, handle); + +done: + if (ret == false) { + return -1; + } + return r.out.data.length; +} + +/** + * Read 'count' bytes at 'offset' from stream 'fname:sname' and + * compare against buffer 'value' + **/ +static bool write_stream(struct smb2_tree *tree, + const char *location, + struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + off_t offset, + size_t size, + const char *value) +{ + struct smb2_handle handle; + struct smb2_create create; + NTSTATUS status; + const char *full_name; + + full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname ? sname : ""); + if (full_name == NULL) { + torture_comment(tctx, "talloc_asprintf error\n"); + return false; + } + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_WRITE_DATA; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.fname = full_name; + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + if (value == NULL) { + return true; + } else { + torture_comment(tctx, "Unable to open stream %s: %s\n", + full_name, nt_errstr(status)); + return false; + } + } + + handle = create.out.file.handle; + if (value == NULL) { + return true; + } + + status = smb2_util_write(tree, handle, value, offset, size); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) Failed to write %lu bytes to " + "stream '%s'\n", location, (long)size, full_name); + return false; + } + + smb2_util_close(tree, handle); + return true; +} + +static bool torture_setup_local_xattr(struct torture_context *tctx, + const char *path_option, + const char *name, + const char *xattr, + const char *metadata, + size_t size) +{ + int ret = true; + int result; + const char *spath; + char *path; + + spath = torture_setting_string(tctx, path_option, NULL); + if (spath == NULL) { + printf("No sharepath for option %s\n", path_option); + return false; + } + + path = talloc_asprintf(tctx, "%s/%s", spath, name); + + result = setxattr(path, xattr, metadata, size, 0); + if (result != 0) { + ret = false; + } + + TALLOC_FREE(path); + + return ret; +} + +/** + * Create a file or directory + **/ +static bool torture_setup_file(TALLOC_CTX *mem_ctx, struct smb2_tree *tree, + const char *name, bool dir) +{ + struct smb2_create io; + NTSTATUS status; + + smb2_util_unlink(tree, name); + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = 0; + io.in.fname = name; + if (dir) { + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_CREATE; + } + + status = smb2_create(tree, mem_ctx, &io); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + status = smb2_util_close(tree, io.out.file.handle); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + return true; +} + +static bool enable_aapl(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + bool ret = true; + struct smb2_create io; + DATA_BLOB data; + struct smb2_create_blob *aapl = NULL; + uint32_t aapl_server_caps; + uint32_t expected_scaps = (SMB2_CRTCTX_AAPL_UNIX_BASED | + SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR | + SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE | + SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE); + bool is_osx_server = torture_setting_bool(tctx, "osx", false); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE); + io.in.fname = ""; + + /* + * Issuing an SMB2/CREATE with a suitably formed AAPL context, + * controls behaviour of Apple's SMB2 extensions for the whole + * session! + */ + + data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t)); + SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY); + SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS | + SMB2_CRTCTX_AAPL_VOLUME_CAPS | + SMB2_CRTCTX_AAPL_MODEL_INFO)); + SBVAL(data.data, 16, (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR | + SMB2_CRTCTX_AAPL_UNIX_BASED | + SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE)); + + status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_blob_add"); + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + + status = smb2_util_close(tree, io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close"); + + /* + * Now check returned AAPL context + */ + torture_comment(tctx, "Comparing returned AAPL capabilities\n"); + + aapl = smb2_create_blob_find(&io.out.blobs, + SMB2_CREATE_TAG_AAPL); + torture_assert_goto(tctx, aapl != NULL, ret, done, "missing AAPL context"); + + if (!is_osx_server) { + size_t expected_aapl_ctx_size; + + expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40; + + torture_assert_goto( + tctx, aapl->data.length == expected_aapl_ctx_size, + ret, done, "bad AAPL size"); + } + + aapl_server_caps = BVAL(aapl->data.data, 16); + torture_assert_goto(tctx, aapl_server_caps == expected_scaps, + ret, done, "bad AAPL caps"); + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool test_read_netatalk_metadata(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_read_metadata"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + ssize_t len; + const char *localdir = NULL; + + torture_comment(tctx, "Checking metadata access\n"); + + localdir = torture_setting_string(tctx, "localdir", NULL); + if (localdir == NULL) { + torture_skip(tctx, "Need localdir for test"); + } + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + ret = torture_setup_local_xattr(tctx, "localdir", + BASEDIR "/torture_read_metadata", + AFPINFO_EA_NETATALK, + metadata_xattr, sizeof(metadata_xattr)); + if (ret == false) { + goto done; + } + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 0, 4, "AFP"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, "BARRFOOO"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 16, 8, 0, 3, "AFP"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + /* Check reading offset and read size > sizeof(AFPINFO_STREAM) */ + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 0, 61); + CHECK_VALUE(len, 60); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 59, 2); + CHECK_VALUE(len, 2); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 60, 1); + CHECK_VALUE(len, 1); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 61, 1); + CHECK_VALUE(len, 0); + +done: + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_read_afpinfo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_read_metadata"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + ssize_t len; + AfpInfo *info; + const char *type_creator = "SMB,OLE!"; + + torture_comment(tctx, "Checking metadata access\n"); + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed"); + + info = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed"); + + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 0, 4, "AFP"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + /* + * OS X ignores offset <= 60 and treats the as + * offset=0. Reading from offsets > 60 returns EOF=0. + */ + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 16, 8, 0, 8, "AFP\0\0\0\001\0"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 0, 61); + torture_assert_goto(tctx, len == 60, ret, done, "read_stream failed"); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 59, 2); + torture_assert_goto(tctx, len == 2, ret, done, "read_stream failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 59, 2, 0, 2, "AF"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 60, 1); + torture_assert_goto(tctx, len == 1, ret, done, "read_stream failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 60, 1, 0, 1, "A"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + + len = read_stream(tree, __location__, tctx, mem_ctx, fname, + AFPINFO_STREAM, 61, 1); + torture_assert_goto(tctx, len == 0, ret, done, "read_stream failed"); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_write_atalk_metadata(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_write_metadata"; + const char *type_creator = "SMB,OLE!"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + AfpInfo *info; + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + info = torture_afpinfo_new(mem_ctx); + if (info == NULL) { + goto done; + } + + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + ret &= check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_write_atalk_rfork_io(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_write_rfork_io"; + const char *rfork = BASEDIR "\\torture_write_rfork_io" AFPRESOURCE_STREAM_NAME; + const char *rfork_content = "1234567890"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + + union smb_open io; + struct smb2_handle filehandle; + union smb_fileinfo finfo; + union smb_setfileinfo sinfo; + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) writing to resource fork\n", + __location__); + + ret &= write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 10, 10, rfork_content); + + ret &= check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 20, 10, 10, rfork_content); + + /* Check size after write */ + + ZERO_STRUCT(io); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE; + io.smb2.in.fname = rfork; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + filehandle = io.smb2.out.file.handle; + + torture_comment(tctx, "(%s) check resource fork size after write\n", + __location__); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = filehandle; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (finfo.all_info.out.size != 20) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect resource fork size\n", + __location__); + ret = false; + smb2_util_close(tree, filehandle); + goto done; + } + smb2_util_close(tree, filehandle); + + /* Write at large offset */ + + torture_comment(tctx, "(%s) writing to resource fork at large offset\n", + __location__); + + ret &= write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + (off_t)64*1024*1024, 10, rfork_content); + + /* Check size after write */ + + ZERO_STRUCT(io); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE; + io.smb2.in.fname = rfork; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + filehandle = io.smb2.out.file.handle; + + torture_comment(tctx, "(%s) check resource fork size after write\n", + __location__); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = filehandle; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (finfo.all_info.out.size != 64*1024*1024 + 10) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect resource fork size\n", + __location__); + ret = false; + smb2_util_close(tree, filehandle); + goto done; + } + smb2_util_close(tree, filehandle); + + ret &= check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + (off_t)64*1024*1024, 10, 0, 10, rfork_content); + + /* Truncate back to size of 1 byte */ + + torture_comment(tctx, "(%s) truncate resource fork and check size\n", + __location__); + + ZERO_STRUCT(io); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.fname = rfork; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + filehandle = io.smb2.out.file.handle; + + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = + RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = filehandle; + sinfo.end_of_file_info.in.size = 1; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, filehandle); + + /* Now check size */ + ZERO_STRUCT(io); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE; + io.smb2.in.fname = rfork; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + filehandle = io.smb2.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = filehandle; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + if (finfo.all_info.out.size != 1) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect resource fork size\n", + __location__); + ret = false; + smb2_util_close(tree, filehandle); + goto done; + } + smb2_util_close(tree, filehandle); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_rfork_truncate(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_truncate"; + const char *rfork = BASEDIR "\\torture_rfork_truncate" AFPRESOURCE_STREAM; + const char *rfork_content = "1234567890"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1, fh2, fh3; + union smb_setfileinfo sinfo; + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + ret &= write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM, + 10, 10, rfork_content); + + /* Truncate back to size 0, further access MUST return ENOENT */ + + torture_comment(tctx, "(%s) truncate resource fork to size 0\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = fname; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh2 = create.out.file.handle; + + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = fh2; + sinfo.end_of_file_info.in.size = 0; + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_setinfo_file"); + + /* + * Now check size, we should get OBJECT_NAME_NOT_FOUND (!) + */ + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + + /* + * Do another open on the rfork and write to the new handle. A + * naive server might unlink the AppleDouble resource fork + * file when its truncated to 0 bytes above, so in case both + * open handles share the same underlying fd, the unlink would + * cause the below write to be lost. + */ + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh3 = create.out.file.handle; + + status = smb2_util_write(tree, fh3, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write"); + + smb2_util_close(tree, fh3); + smb2_util_close(tree, fh2); + smb2_util_close(tree, fh1); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM, + 0, 3, 0, 3, "foo"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream"); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_rfork_create(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_create"; + const char *rfork = BASEDIR "\\torture_rfork_create" AFPRESOURCE_STREAM; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1; + const char *streams[] = { + "::$DATA" + }; + union smb_fileinfo finfo; + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) open rfork, should return ENOENT\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + + torture_comment(tctx, "(%s) create resource fork\n", __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + torture_comment(tctx, "(%s) getinfo on create handle\n", + __location__); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = fh1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file"); + if (finfo.all_info.out.size != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect resource fork size\n", + __location__); + ret = false; + smb2_util_close(tree, fh1); + goto done; + } + + torture_comment(tctx, "(%s) open rfork, should still return ENOENT\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + + ret = check_stream_list(tree, tctx, fname, 1, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + torture_comment(tctx, "(%s) close empty created rfork, open should return ENOENT\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +/* + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15182 + */ + +static bool test_rfork_fsync(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_fsync"; + const char *rfork = BASEDIR "\\torture_rfork_fsync" AFPRESOURCE_STREAM; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1; + struct smb2_flush f; + + ZERO_STRUCT(fh1); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, + status, + ret, + done, + "torture_smb2_testdir"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) create resource fork %s\n", + __location__, + rfork); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + torture_comment(tctx, "(%s) Write 10 bytes to resource fork %s\n", + __location__, + rfork); + + status = smb2_util_write(tree, fh1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + torture_comment(tctx, "(%s) fsync on resource fork %s\n", + __location__, + rfork); + + f.in.file.handle = fh1; + status = smb2_flush(tree, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_flush failed\n"); + +done: + + smb2_util_close(tree, fh1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_rfork_create_ro(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_create"; + const char *rfork = BASEDIR "\\torture_rfork_create" AFPRESOURCE_STREAM; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) Try opening read-only with " + "open_if create disposition, should work\n", + __location__); + + ZERO_STRUCT(create); + create.in.fname = rfork; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_FILE_READ_DATA | SEC_STD_READ_CONTROL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE; + status = smb2_create(tree, mem_ctx, &(create)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + smb2_util_close(tree, create.out.file.handle); + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_adouble_conversion(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_adouble_conversion"; + const char *adname = BASEDIR "/._test_adouble_conversion"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + const char data[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + size_t datalen = sizeof(data); + const char *streams[] = { + "::$DATA", + AFPINFO_STREAM, + AFPRESOURCE_STREAM, + ":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA", + ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ + }; + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = torture_setup_file(tctx, tree, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + adname, NULL, + 0, + sizeof(osx_adouble_non_empty_rfork_w_xattr), + osx_adouble_non_empty_rfork_w_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + torture_comment(tctx, "(%s) test OS X AppleDouble conversion\n", + __location__); + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM, + 16, datalen, 0, datalen, data); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPRESOURCE_STREAM failed\n"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPINFO_STREAM, + 0, 60, 16, 8, "TESTSLOW"); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPINFO_STREAM failed\n"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, + ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ + 0, 3, 0, 3, "baz"); + torture_assert_goto(tctx, ret == true, ret, done, + "check foo:bar stream failed\n"); + + ret = check_stream_list(tree, tctx, fname, 5, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + +done: + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +/* + * Test conversion of AppleDouble file without embedded xattr data + */ +static bool test_adouble_conversion_wo_xattr(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_adouble_conversion"; + const char *adname = BASEDIR "/._test_adouble_conversion"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + const char *streams[] = { + "::$DATA", + AFPINFO_STREAM, + AFPRESOURCE_STREAM + }; + struct smb2_create create; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + const char data[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + size_t datalen = sizeof(data); + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = torture_setup_file(tctx, tree, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + adname, NULL, 0, + sizeof(osx_adouble_without_xattr), + osx_adouble_without_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + /* + * Issue a smb2_find(), this triggers the server-side conversion + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + /* + * Check number of streams + */ + + ret = check_stream_list(tree, tctx, fname, 3, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + + /* + * Check Resourcefork data can be read. + */ + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM, + 16, datalen, 0, datalen, data); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPRESOURCE_STREAM failed\n"); + + /* + * Check FinderInfo data has been migrated to stream. + */ + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPINFO_STREAM, + 0, 60, 16, 8, "WAVEPTul"); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPINFO_STREAM failed\n"); + +done: + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_aapl(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_aapl"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create io; + DATA_BLOB data; + struct smb2_create_blob *aapl = NULL; + AfpInfo *info; + const char *type_creator = "SMB,OLE!"; + char type_creator_buf[9]; + uint32_t aapl_cmd; + uint32_t aapl_reply_bitmap; + uint32_t aapl_server_caps; + uint32_t aapl_vol_caps; + uint32_t expected_vol_caps = 0; + char *model; + struct smb2_find f; + unsigned int count; + union smb_search_data *d; + uint64_t rfork_len; + bool is_osx_server = torture_setting_bool(tctx, "osx", false); + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, testdirh); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE); + io.in.fname = fname; + + /* + * Issuing an SMB2/CREATE with a suitably formed AAPL context, + * controls behaviour of Apple's SMB2 extensions for the whole + * session! + */ + + data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t)); + SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY); + SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS | + SMB2_CRTCTX_AAPL_VOLUME_CAPS | + SMB2_CRTCTX_AAPL_MODEL_INFO)); + SBVAL(data.data, 16, (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR | + SMB2_CRTCTX_AAPL_UNIX_BASED | + SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE)); + + torture_comment(tctx, "Testing SMB2 create context AAPL\n"); + status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Now check returned AAPL context + */ + torture_comment(tctx, "Comparing returned AAPL capabilities\n"); + + aapl = smb2_create_blob_find(&io.out.blobs, + SMB2_CREATE_TAG_AAPL); + + if (aapl == NULL) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpectedly no AAPL capabilities were returned.", + __location__); + ret = false; + goto done; + } + + if (!is_osx_server) { + size_t expected_aapl_ctx_size; + bool size_ok; + + /* + * uint32_t CommandCode = kAAPL_SERVER_QUERY + * uint32_t Reserved = 0; + * uint64_t ReplyBitmap = kAAPL_SERVER_CAPS | + * kAAPL_VOLUME_CAPS | + * kAAPL_MODEL_INFO; + * uint64_t ServerCaps = kAAPL_SUPPORTS_READDIR_ATTR | + * kAAPL_SUPPORTS_OSX_COPYFILE; + * uint64_t VolumeCaps = kAAPL_SUPPORT_RESOLVE_ID | + * kAAPL_CASE_SENSITIVE; + * uint32_t Pad2 = 0; + * uint32_t ModelStringLen = 10; + * ucs2_t ModelString[5] = "MacSamba"; + */ + expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40; + + size_ok = aapl->data.length == expected_aapl_ctx_size; + torture_assert_goto(tctx, size_ok, ret, done, "bad AAPL size"); + } + + aapl_cmd = IVAL(aapl->data.data, 0); + if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected cmd: %d", + __location__, (int)aapl_cmd); + ret = false; + goto done; + } + + aapl_reply_bitmap = BVAL(aapl->data.data, 8); + if (aapl_reply_bitmap != (SMB2_CRTCTX_AAPL_SERVER_CAPS | + SMB2_CRTCTX_AAPL_VOLUME_CAPS | + SMB2_CRTCTX_AAPL_MODEL_INFO)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected reply_bitmap: %d", + __location__, (int)aapl_reply_bitmap); + ret = false; + goto done; + } + + aapl_server_caps = BVAL(aapl->data.data, 16); + if (aapl_server_caps != (SMB2_CRTCTX_AAPL_UNIX_BASED | + SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR | + SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE | + SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected server_caps: %d", + __location__, (int)aapl_server_caps); + ret = false; + goto done; + } + + if (is_osx_server) { + expected_vol_caps = 5; + } + aapl_vol_caps = BVAL(aapl->data.data, 24); + if (aapl_vol_caps != expected_vol_caps) { + /* this will fail on a case insensitive fs ... */ + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected vol_caps: %d", + __location__, (int)aapl_vol_caps); + } + + ret = convert_string_talloc(mem_ctx, + CH_UTF16LE, CH_UNIX, + aapl->data.data + 40, 10, + &model, NULL); + if (ret == false) { + torture_result(tctx, TORTURE_FAIL, + "(%s) convert_string_talloc() failed", + __location__); + goto done; + } + torture_comment(tctx, "Got server model: \"%s\"\n", model); + + /* + * Now that Requested AAPL extensions are enabled, setup some + * Mac files with metadata and resource fork + */ + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + torture_result(tctx, TORTURE_FAIL, + "(%s) torture_setup_file() failed", + __location__); + goto done; + } + + info = torture_afpinfo_new(mem_ctx); + if (info == NULL) { + torture_result(tctx, TORTURE_FAIL, + "(%s) torture_afpinfo_new() failed", + __location__); + ret = false; + goto done; + } + + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + if (ret == false) { + torture_result(tctx, TORTURE_FAIL, + "(%s) torture_write_afpinfo() failed", + __location__); + goto done; + } + + ret = write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 3, "foo"); + if (ret == false) { + torture_result(tctx, TORTURE_FAIL, + "(%s) write_stream() failed", + __location__); + goto done; + } + + /* + * Ok, file is prepared, now call smb2/find + */ + + ZERO_STRUCT(io); + io.in.desired_access = SEC_RIGHTS_DIR_READ; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.fname = BASEDIR; + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(f); + f.in.file.handle = io.out.file.handle; + f.in.pattern = "test_aapl"; + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + f.in.max_response_size = 0x1000; + f.in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO; + + status = smb2_find_level(tree, tree, &f, &count, &d); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + if (strcmp(d[0].id_both_directory_info.name.s, "test_aapl") != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s) write_stream() failed", + __location__); + ret = false; + goto done; + } + + if (d[0].id_both_directory_info.short_name.private_length != 24) { + torture_result(tctx, TORTURE_FAIL, + "(%s) bad short_name length %" PRIu32 ", expected 24", + __location__, d[0].id_both_directory_info.short_name.private_length); + ret = false; + goto done; + } + + torture_comment(tctx, "short_name buffer:\n"); + dump_data(0, d[0].id_both_directory_info.short_name_buf, 24); + + /* + * Extract data as specified by the AAPL extension: + * - ea_size contains max_access + * - short_name contains resource fork length + FinderInfo + * - reserved2 contains the unix mode + */ + torture_comment(tctx, "mac_access: %" PRIx32 "\n", + d[0].id_both_directory_info.ea_size); + + rfork_len = BVAL(d[0].id_both_directory_info.short_name_buf, 0); + if (rfork_len != 3) { + torture_result(tctx, TORTURE_FAIL, + "(%s) expected resource fork length 3, got: %" PRIu64, + __location__, rfork_len); + ret = false; + goto done; + } + + memcpy(type_creator_buf, d[0].id_both_directory_info.short_name_buf + 8, 8); + type_creator_buf[8] = 0; + if (strcmp(type_creator, type_creator_buf) != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s) expected type/creator \"%s\" , got: %s", + __location__, type_creator, type_creator_buf); + ret = false; + goto done; + } + +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static uint64_t patt_hash(uint64_t off) +{ + return off; +} + +static bool write_pattern(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len, + uint64_t patt_off) +{ + NTSTATUS status; + uint64_t i; + uint8_t *buf; + uint64_t io_sz = MIN(1024 * 64, len); + + if (len == 0) { + return true; + } + + torture_assert(torture, (len % 8) == 0, "invalid write len"); + + buf = talloc_zero_size(mem_ctx, io_sz); + torture_assert(torture, (buf != NULL), "no memory for file data buf"); + + while (len > 0) { + for (i = 0; i <= io_sz - 8; i += 8) { + SBVAL(buf, i, patt_hash(patt_off)); + patt_off += 8; + } + + status = smb2_util_write(tree, h, + buf, off, io_sz); + torture_assert_ntstatus_ok(torture, status, "file write"); + + len -= io_sz; + off += io_sz; + } + + talloc_free(buf); + + return true; +} + +static bool check_pattern(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len, + uint64_t patt_off) +{ + if (len == 0) { + return true; + } + + torture_assert(torture, (len % 8) == 0, "invalid read len"); + + while (len > 0) { + uint64_t i; + struct smb2_read r; + NTSTATUS status; + uint64_t io_sz = MIN(1024 * 64, len); + + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = io_sz; + r.in.offset = off; + status = smb2_read(tree, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "read"); + + torture_assert_u64_equal(torture, r.out.data.length, io_sz, + "read data len mismatch"); + + for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) { + uint64_t data = BVAL(r.out.data.data, i); + torture_assert_u64_equal(torture, data, patt_hash(patt_off), + talloc_asprintf(torture, "read data " + "pattern bad at %llu\n", + (unsigned long long)off + i)); + } + talloc_free(r.out.data.data); + len -= io_sz; + off += io_sz; + } + + return true; +} + +static bool test_setup_open(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + const char *fname, + struct smb2_handle *fh, + uint32_t desired_access, + uint32_t file_attributes) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.desired_access = desired_access; + io.in.file_attributes = file_attributes; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) { + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + } + io.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok(torture, status, "file create"); + + *fh = io.out.file.handle; + + return true; +} + +static bool test_setup_create_fill(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + const char *fname, + struct smb2_handle *fh, + uint64_t size, + uint32_t desired_access, + uint32_t file_attributes) +{ + bool ok; + + ok = test_setup_open(torture, tree, mem_ctx, + fname, + fh, + desired_access, + file_attributes); + torture_assert(torture, ok, "file open"); + + if (size > 0) { + ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0); + torture_assert(torture, ok, "write pattern"); + } + return true; +} + +static bool test_setup_copy_chunk(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + uint32_t nchunks, + const char *src_name, + struct smb2_handle *src_h, + uint64_t src_size, + uint32_t src_desired_access, + const char *dst_name, + struct smb2_handle *dest_h, + uint64_t dest_size, + uint32_t dest_desired_access, + struct srv_copychunk_copy *cc_copy, + union smb_ioctl *io) +{ + struct req_resume_key_rsp res_key; + bool ok; + NTSTATUS status; + enum ndr_err_code ndr_ret; + + ok = test_setup_create_fill(torture, tree, mem_ctx, src_name, + src_h, src_size, src_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "src file create fill"); + + ok = test_setup_create_fill(torture, tree, mem_ctx, dst_name, + dest_h, dest_size, dest_desired_access, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "dest file create fill"); + + ZERO_STRUCTPN(io); + io->smb2.level = RAW_IOCTL_SMB2; + io->smb2.in.file.handle = *src_h; + io->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; + /* Allow for Key + ContextLength + Context */ + io->smb2.in.max_output_response = 32; + io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + status = smb2_ioctl(tree, mem_ctx, &io->smb2); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_SRV_REQUEST_RESUME_KEY"); + + ndr_ret = ndr_pull_struct_blob(&io->smb2.out.out, mem_ctx, &res_key, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_req_resume_key_rsp"); + + ZERO_STRUCTPN(io); + io->smb2.level = RAW_IOCTL_SMB2; + io->smb2.in.file.handle = *dest_h; + io->smb2.in.function = FSCTL_SRV_COPYCHUNK; + io->smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp); + io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_STRUCTPN(cc_copy); + memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key)); + cc_copy->chunk_count = nchunks; + cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks); + torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks"); + + return true; +} + + +static bool check_copy_chunk_rsp(struct torture_context *torture, + struct srv_copychunk_rsp *cc_rsp, + uint32_t ex_chunks_written, + uint32_t ex_chunk_bytes_written, + uint32_t ex_total_bytes_written) +{ + torture_assert_int_equal(torture, cc_rsp->chunks_written, + ex_chunks_written, "num chunks"); + torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written, + ex_chunk_bytes_written, "chunk bytes written"); + torture_assert_int_equal(torture, cc_rsp->total_bytes_written, + ex_total_bytes_written, "chunk total bytes"); + return true; +} + +static bool neg_aapl_copyfile(struct torture_context *tctx, + struct smb2_tree *tree, + uint64_t flags) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = "aapl"; + NTSTATUS status; + struct smb2_create io; + DATA_BLOB data; + struct smb2_create_blob *aapl = NULL; + uint32_t aapl_cmd; + uint32_t aapl_reply_bitmap; + uint32_t aapl_server_caps; + bool ret = true; + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE); + io.in.fname = fname; + + data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t)); + SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY); + SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS)); + SBVAL(data.data, 16, flags); + + status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_create(tree, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + aapl = smb2_create_blob_find(&io.out.blobs, + SMB2_CREATE_TAG_AAPL); + if (aapl == NULL) { + ret = false; + goto done; + + } + if (aapl->data.length < 24) { + ret = false; + goto done; + } + + aapl_cmd = IVAL(aapl->data.data, 0); + if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected cmd: %d", + __location__, (int)aapl_cmd); + ret = false; + goto done; + } + + aapl_reply_bitmap = BVAL(aapl->data.data, 8); + if (!(aapl_reply_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected reply_bitmap: %d", + __location__, (int)aapl_reply_bitmap); + ret = false; + goto done; + } + + aapl_server_caps = BVAL(aapl->data.data, 16); + if (!(aapl_server_caps & flags)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) unexpected server_caps: %d", + __location__, (int)aapl_server_caps); + ret = false; + goto done; + } + +done: + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_unlink(tree, "aapl"); + talloc_free(mem_ctx); + return ret; +} + +static bool test_copyfile(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl io; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok; + const char *sname = ":foo" "\xef\x80\xa2" "bar:$DATA"; + + /* + * First test a copy_chunk with a 0 chunk count without having + * enabled this via AAPL. The request must not fail and the + * copied length in the response must be 0. This is verified + * against Windows 2008r2. + */ + + ok = test_setup_copy_chunk(torture, tree, tmp_ctx, + 0, /* 0 chunks, copyfile semantics */ + FNAME_CC_SRC, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + FNAME_CC_DST, + &dest_h, 0, /* 0 byte dest file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + &cc_copy, + &io); + if (!ok) { + torture_fail_goto(torture, done, "setup copy chunk error"); + } + + ndr_ret = ndr_push_struct_blob(&io.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &io.smb2); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&io.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 0, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 0); /* total bytes written */ + if (!ok) { + torture_fail_goto(torture, done, "bad copy chunk response data"); + } + + /* + * Now enable AAPL copyfile and test again, the file and the + * stream must be copied by the server. + */ + ok = neg_aapl_copyfile(torture, tree, + SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE); + if (!ok) { + torture_skip_goto(torture, done, "missing AAPL copyfile"); + goto done; + } + + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + smb2_util_unlink(tree, FNAME_CC_SRC); + smb2_util_unlink(tree, FNAME_CC_DST); + + ok = torture_setup_file(tmp_ctx, tree, FNAME_CC_SRC, false); + if (!ok) { + torture_fail(torture, "setup file error"); + } + ok = write_stream(tree, __location__, torture, tmp_ctx, + FNAME_CC_SRC, AFPRESOURCE_STREAM, + 10, 10, "1234567890"); + if (!ok) { + torture_fail(torture, "setup stream error"); + } + + ok = write_stream(tree, __location__, torture, tmp_ctx, + FNAME_CC_SRC, sname, + 10, 10, "abcdefghij"); + torture_assert_goto(torture, ok == true, ok, done, "write_stream failed\n"); + + ok = test_setup_copy_chunk(torture, tree, tmp_ctx, + 0, /* 0 chunks, copyfile semantics */ + FNAME_CC_SRC, + &src_h, 4096, /* fill 4096 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + FNAME_CC_DST, + &dest_h, 0, /* 0 byte dest file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + &cc_copy, + &io); + if (!ok) { + torture_fail_goto(torture, done, "setup copy chunk error"); + } + + ndr_ret = ndr_push_struct_blob(&io.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &io.smb2); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&io.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 0, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 4096); /* total bytes written */ + if (!ok) { + torture_fail_goto(torture, done, "bad copy chunk response data"); + } + + ok = test_setup_open(torture, tree, tmp_ctx, FNAME_CC_DST, &dest_h, + SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL); + if (!ok) { + torture_fail_goto(torture, done,"open failed"); + } + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); + if (!ok) { + torture_fail_goto(torture, done, "inconsistent file data"); + } + + ok = check_stream(tree, __location__, torture, tmp_ctx, + FNAME_CC_DST, AFPRESOURCE_STREAM, + 0, 20, 10, 10, "1234567890"); + if (!ok) { + torture_fail_goto(torture, done, "inconsistent stream data"); + } + + ok = check_stream(tree, __location__, torture, tmp_ctx, + FNAME_CC_DST, sname, + 0, 20, 10, 10, "abcdefghij"); + torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n"); + +done: + smb2_util_close(tree, src_h); + smb2_util_close(tree, dest_h); + smb2_util_unlink(tree, FNAME_CC_SRC); + smb2_util_unlink(tree, FNAME_CC_DST); + talloc_free(tmp_ctx); + return true; +} + +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + int num_exp, + const char **exp, + bool is_dir) +{ + bool ret = true; + union smb_fileinfo finfo; + NTSTATUS status; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char **exp_sort; + struct stream_struct *stream_sort; + struct smb2_create create; + struct smb2_handle h; + + ZERO_STRUCT(h); + torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "talloc_new failed"); + + ZERO_STRUCT(create); + create.in.fname = fname; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.create_options = is_dir ? NTCREATEX_OPTIONS_DIRECTORY : 0; + create.in.file_attributes = is_dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + status = smb2_create(tree, tmp_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + h = create.out.file.handle; + + finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION; + finfo.generic.in.file.handle = h; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "get stream info"); + + smb2_util_close(tree, h); + + torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams, num_exp, + ret, done, "stream count"); + + if (num_exp == 0) { + TALLOC_FREE(tmp_ctx); + goto done; + } + + exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__); + + TYPESAFE_QSORT(exp_sort, num_exp, qsort_string); + + stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + torture_assert_goto(tctx, stream_sort != NULL, ret, done, __location__); + + TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream); + + for (i=0; iafpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(tctx, ret == true, ret, done, "Bad type/creator in AFP_AfpInfo"); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Got unexpected AFP_AfpInfo stream"); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_setinfo_delete_on_close(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + union smb_setfileinfo sfinfo; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME; + const char *type_creator = "SMB,OLE!"; + AfpInfo *info = NULL; + const char *streams[] = { + AFPINFO_STREAM, + "::$DATA" + }; + const char *streams_basic[] = { + "::$DATA" + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + torture_comment(tctx, "Deleting AFP_AfpInfo via setinfo with delete-on-close\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + info = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed"); + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + /* Delete stream via setinfo delete-on-close */ + ZERO_STRUCT(sfinfo); + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set delete-on-close failed"); + + ret = check_stream_list(tree, tctx, fname, 2, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_DELETE_PENDING, + ret, done, "Got unexpected AFP_AfpInfo stream"); + + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Got unexpected AFP_AfpInfo stream"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_setinfo_eof(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + union smb_setfileinfo sfinfo; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME; + const char *type_creator = "SMB,OLE!"; + AfpInfo *info = NULL; + const char *streams_afpinfo[] = { + "::$DATA", + AFPINFO_STREAM + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + torture_comment(tctx, "Set AFP_AfpInfo EOF to 61, 1 and 0\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + info = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed"); + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + torture_comment(tctx, "Set AFP_AfpInfo EOF to 61\n"); + + /* Test setinfo end-of-file info */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 61; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ALLOTTED_SPACE_EXCEEDED, + ret, done, "set eof 61 failed"); + + torture_comment(tctx, "Set AFP_AfpInfo EOF to 1\n"); + + /* Truncation returns success, but has no effect */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, + ret, done, "set eof 1 failed"); + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(tctx, ret == true, ret, done, "FinderInfo changed"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + /* + * Delete stream via setinfo end-of-file info to 0, should + * return success but stream MUST NOT deleted + */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed"); + + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(tctx, ret == true, ret, done, "FinderInfo changed"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_afpinfo_all0(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + struct smb2_handle h1 = {{0}}; + struct smb2_handle baseh = {{0}}; + union smb_setfileinfo setfinfo; + union smb_fileinfo getfinfo; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPINFO_STREAM; + const char *type_creator = "SMB,OLE!"; + AfpInfo *info = NULL; + char *infobuf = NULL; + const char *streams_basic[] = { + "::$DATA" + }; + const char *streams_afpinfo[] = { + "::$DATA", + AFPINFO_STREAM + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + torture_comment(tctx, "Write all 0 to AFP_AfpInfo and see what happens\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + info = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed"); + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + /* Write all 0 to AFP_AfpInfo */ + memset(info->afpi_FinderInfo, 0, AFP_FinderSize); + infobuf = torture_afpinfo_pack(mem_ctx, info); + torture_assert_not_null_goto(tctx, infobuf, ret, done, + "torture_afpinfo_pack failed\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + baseh = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + create.in.fname = sname; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = create.out.file.handle; + + status = smb2_util_write(tree, h1, infobuf, 0, AFP_INFO_SIZE); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + /* + * Get stream information on open handle, must return only default + * stream, the AFP_AfpInfo stream must not be returned. + */ + + ZERO_STRUCT(getfinfo); + getfinfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION; + getfinfo.generic.in.file.handle = baseh; + + status = smb2_getinfo_file(tree, tctx, &getfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "get stream info\n"); + + torture_assert_int_equal_goto(tctx, getfinfo.stream_info.out.num_streams, + 1, ret, done, "stream count"); + + smb2_util_close(tree, baseh); + ZERO_STRUCT(baseh); + + /* + * Try to set some file-basic-info (time) on the stream. This catches + * naive implementation mistakes that simply deleted the backing store + * from the filesystem in the zero-out step. + */ + + ZERO_STRUCT(setfinfo); + unix_to_nt_time(&setfinfo.basic_info.in.write_time, time(NULL)); + setfinfo.basic_info.in.attrib = 0x20; + setfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + setfinfo.generic.in.file.handle = h1; + + status = smb2_setinfo_file(tree, &setfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + smb2_util_close(tree, h1); + ZERO_STRUCT(h1); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + +done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(tree, h1); + } + if (!smb2_util_handle_empty(baseh)) { + smb2_util_close(tree, baseh); + } + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_create_delete_on_close_resource(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME; + const char *streams_basic[] = { + "::$DATA" + }; + const char *streams_afpresource[] = { + "::$DATA", + AFPRESOURCE_STREAM + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + torture_comment(tctx, "Checking whether create with delete-on-close is ignored for AFP_AfpResource\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok(tctx, status, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + torture_comment(tctx, "Opening not existing AFP_AfpResource\n"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */ + create.in.fname = sname; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Got unexpected AFP_AfpResource stream"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Got unexpected AFP_AfpResource stream"); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + torture_comment(tctx, "Trying to delete AFP_AfpResource via create with delete-on-close\n"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 10, "1234567890"); + torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM_NAME, + 0, 10, 0, 10, "1234567890"); + torture_assert_goto(tctx, ret == true, ret, done, "Bad content from AFP_AfpResource"); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM_NAME, + 0, 10, 0, 10, "1234567890"); + torture_assert_goto(tctx, ret == true, ret, done, "Bad content from AFP_AfpResource"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_setinfo_delete_on_close_resource(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + union smb_setfileinfo sfinfo; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME; + const char *streams_afpresource[] = { + "::$DATA", + AFPRESOURCE_STREAM + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + torture_comment(tctx, "Trying to delete AFP_AfpResource via setinfo with delete-on-close\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 10, 10, "1234567890"); + torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + /* Try to delete stream via setinfo delete-on-close */ + ZERO_STRUCT(sfinfo); + sfinfo.disposition_info.in.delete_on_close = 1; + sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set delete-on-close failed"); + + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Got unexpected AFP_AfpResource stream"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +static bool test_setinfo_eof_resource(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + union smb_setfileinfo sfinfo; + union smb_fileinfo finfo; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME; + const char *streams_basic[] = { + "::$DATA" + }; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + torture_comment(tctx, "Set AFP_AfpResource EOF to 1 and 0\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree, h1); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 10, 10, "1234567890"); + torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + torture_comment(tctx, "Set AFP_AfpResource EOF to 1\n"); + + /* Test setinfo end-of-file info */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, + ret, done, "set eof 1 failed"); + + smb2_util_close(tree, h1); + + /* Check size == 1 */ + ZERO_STRUCT(create); + create.in.fname = sname; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed"); + + smb2_util_close(tree, h1); + + torture_assert_goto(tctx, finfo.all_info.out.size == 1, ret, done, "size != 1"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + /* + * Delete stream via setinfo end-of-file info to 0, this + * should delete the stream. + */ + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed"); + + smb2_util_close(tree, h1); + + ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "smb2_create failed"); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +/* + * This tests that right after creating the AFP_AfpInfo stream, + * reading from the stream returns an empty, default metadata blob of + * 60 bytes. + * + * NOTE: against OS X SMB server this only works if the read request + * is compounded with the create that created the stream, is fails + * otherwise. We don't care... + */ +static bool test_null_afpinfo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = "test_null_afpinfo"; + const char *sname = "test_null_afpinfo" AFPINFO_STREAM_NAME; + NTSTATUS status; + bool ret = true; + struct smb2_request *req[3]; + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read read; + AfpInfo *afpinfo = NULL; + char *afpinfo_buf = NULL; + const char *type_creator = "SMB,OLE!"; + struct smb2_handle handle2; + struct smb2_read r; + + torture_comment(tctx, "Checking create of AfpInfo stream\n"); + + smb2_util_unlink(tree, fname); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.fname = sname; + + smb2_transport_compound_start(tree->session->transport, 2); + + req[0] = smb2_create_send(tree, &create); + + handle.data[0] = UINT64_MAX; + handle.data[1] = UINT64_MAX; + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(read); + read.in.file.handle = handle; + read.in.length = AFP_INFO_SIZE; + req[1] = smb2_read_send(tree, &read); + + status = smb2_create_recv(req[0], tree, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_recv failed"); + + handle = create.out.file.handle; + + status = smb2_read_recv(req[1], tree, &read); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read_recv failed"); + + status = torture_smb2_testfile_access(tree, sname, &handle2, + SEC_FILE_READ_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + r = (struct smb2_read) { + .in.file.handle = handle2, + .in.length = AFP_INFO_SIZE, + }; + + status = smb2_read(tree, tree, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + smb2_util_close(tree, handle2); + + afpinfo = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed"); + + memcpy(afpinfo->afpi_FinderInfo, type_creator, 8); + + afpinfo_buf = torture_afpinfo_pack(tctx, afpinfo); + torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, "torture_afpinfo_new failed"); + + status = smb2_util_write(tree, handle, afpinfo_buf, 0, AFP_INFO_SIZE); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write failed"); + + smb2_util_close(tree, handle); + + ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + +done: + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_delete_file_with_rfork(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "torture_write_rfork_io"; + const char *rfork_content = "1234567890"; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(tree, fname); + + torture_comment(tctx, "Test deleting file with resource fork\n"); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed\n"); + + ret = write_stream(tree, __location__, tctx, tctx, + fname, AFPRESOURCE_STREAM_NAME, + 10, 10, rfork_content); + torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed\n"); + + ret = check_stream(tree, __location__, tctx, tctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 20, 10, 10, rfork_content); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed\n"); + + status = smb2_util_unlink(tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "check_stream failed\n"); + +done: + return ret; +} + +static bool test_rename_and_read_rsrc(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create, create2; + struct smb2_handle h1, h2; + const char *fname = "test_rename_openfile"; + const char *sname = "test_rename_openfile" AFPRESOURCE_STREAM_NAME; + const char *fname_renamed = "test_rename_openfile_renamed"; + const char *data = "1234567890"; + union smb_setfileinfo sinfo; + bool server_is_macos = torture_setting_bool(tctx, "osx", false); + NTSTATUS expected_status; + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + torture_comment(tctx, "Create file with resource fork\n"); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + ret = write_stream(tree, __location__, tctx, tctx, + fname, AFPRESOURCE_STREAM_NAME, 0, 10, data); + torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed"); + + torture_comment(tctx, "Open resource fork\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_ALL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h1 = create.out.file.handle; + + torture_comment(tctx, "Rename base file\n"); + + ZERO_STRUCT(create2); + create2.in.desired_access = SEC_FILE_ALL; + create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create2.in.create_disposition = NTCREATEX_DISP_OPEN; + create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create2.in.fname = fname; + + status = smb2_create(tree, tctx, &create2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + h2 = create2.out.file.handle; + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h2; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = fname_renamed; + + if (server_is_macos) { + expected_status = NT_STATUS_SHARING_VIOLATION; + } else { + expected_status = NT_STATUS_ACCESS_DENIED; + } + + status = smb2_setinfo_file(tree, &sinfo); + torture_assert_ntstatus_equal_goto( + tctx, status, expected_status, ret, done, + "smb2_setinfo_file failed"); + + smb2_util_close(tree, h2); + + status = smb2_util_write(tree, h1, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + smb2_util_close(tree, h1); + +done: + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname_renamed); + + return ret; +} + +static bool test_readdir_attr_illegal_ntfs(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *name = "test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */ + const char *fname = BASEDIR "\\test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */ + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create io; + AfpInfo *info; + const char *type_creator = "SMB,OLE!"; + struct smb2_find f; + unsigned int count; + union smb_search_data *d; + uint64_t rfork_len; + unsigned int i; + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed"); + smb2_util_close(tree, testdirh); + + torture_comment(tctx, "Enabling AAPL\n"); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + /* + * Now that Requested AAPL extensions are enabled, setup some + * Mac files with metadata and resource fork + */ + + torture_comment(tctx, "Preparing file\n"); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed"); + + info = torture_afpinfo_new(mem_ctx); + torture_assert_not_null_goto(tctx, info, ret, done, "torture_afpinfo_new failed"); + + memcpy(info->afpi_FinderInfo, type_creator, 8); + ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 3, "foo"); + torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed"); + + /* + * Ok, file is prepared, now call smb2/find + */ + + torture_comment(tctx, "Issue find\n"); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_RIGHTS_DIR_READ; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE); + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.fname = BASEDIR; + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); + + ZERO_STRUCT(f); + f.in.file.handle = io.out.file.handle; + f.in.pattern = "*"; + f.in.max_response_size = 0x1000; + f.in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO; + + status = smb2_find_level(tree, tree, &f, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_level failed"); + + status = smb2_util_close(tree, io.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed"); + + torture_comment(tctx, "Checking find response with enriched macOS metadata\n"); + + for (i = 0; i < count; i++) { + const char *found = d[i].id_both_directory_info.name.s; + + if (!strcmp(found, ".") || !strcmp(found, "..")) + continue; + if (strncmp(found, "._", 2) == 0) { + continue; + } + break; + } + + torture_assert_str_equal_goto(tctx, + d[i].id_both_directory_info.name.s, name, + ret, done, "bad name"); + + rfork_len = BVAL(d[i].id_both_directory_info.short_name_buf, 0); + torture_assert_int_equal_goto(tctx, rfork_len, 3, ret, done, "bad resource fork length"); + + torture_assert_mem_equal_goto(tctx, type_creator, + d[i].id_both_directory_info.short_name_buf + 8, + 8, ret, done, "Bad FinderInfo"); +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_invalid_afpinfo(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = "filtest_invalid_afpinfo"; + const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM_NAME; + struct smb2_create create; + const char *streams_basic[] = { + "::$DATA" + }; + const char *streams_afpinfo[] = { + "::$DATA", + AFPINFO_STREAM + }; + NTSTATUS status; + bool ret = true; + + if (tree2 == NULL) { + torture_skip_goto(tctx, done, "need second share without fruit\n"); + } + + torture_comment(tctx, "Testing invalid AFP_AfpInfo stream\n"); + + ret = torture_setup_file(tctx, tree2, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + ret = write_stream(tree2, __location__, tctx, tctx, + fname, AFPINFO_STREAM_NAME, + 0, 3, "foo"); + torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed"); + + ret = check_stream_list(tree2, tctx, fname, 2, streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + torture_comment(tctx, "Listing streams, bad AFPINFO stream must not be present\n"); + + ret = check_stream_list(tree1, tctx, fname, 1, streams_basic, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + torture_comment(tctx, "Try to open AFPINFO stream, must fail\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_ALL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; + create.in.fname = sname; + + status = smb2_create(tree1, tctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Stream still around?"); + +done: + smb2_util_unlink(tree1, fname); + return ret; +} + +static bool test_writing_afpinfo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "filtest_invalid_afpinfo"; + const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM; + const char *streams_afpinfo[] = { + "::$DATA", + AFPINFO_STREAM + }; + bool ret = true; + static AfpInfo *afpi = NULL; + char *buf = NULL; + char *afpi_buf = NULL; + char *zero_buf = NULL; + bool broken_osx = torture_setting_bool(tctx, "broken_osx_45759458", false); + off_t min_offset_for_2streams = 16; + int i; + NTSTATUS status; + struct test_sizes { + off_t offset; + size_t size; + bool expected_result; + } test_sizes[] = { + { 0, 1, false}, + { 0, 2, false}, + { 0, 3, true}, + { 0, 4, true}, + { 0, 14, true}, + { 0, 15, true}, + { 0, 16, true}, + { 0, 24, true}, + { 0, 34, true}, + { 0, 44, true}, + { 0, 54, true}, + { 0, 55, true}, + { 0, 56, true}, + { 0, 57, true}, + { 0, 58, true}, + { 0, 59, true}, + { 0, 60, true}, + { 0, 61, true}, + { 0, 64, true}, + { 0, 1024, true}, + { 0, 10064, true}, + + { 1, 1, false}, + { 1, 2, false}, + { 1, 3, false}, + { 1, 4, false}, + { 1, 14, false}, + { 1, 15, false}, + { 1, 16, false}, + { 1, 24, false}, + { 1, 34, false}, + { 1, 44, false}, + { 1, 54, false}, + { 1, 55, false}, + { 1, 56, false}, + { 1, 57, false}, + { 1, 58, false}, + { 1, 59, false}, + { 1, 60, true}, + { 1, 61, true}, + { 1, 1024, true}, + { 1, 10064, true}, + + { 30, 1, false}, + { 30, 2, false}, + { 30, 3, false}, + { 30, 4, false}, + { 30, 14, false}, + { 30, 15, false}, + { 30, 16, false}, + { 30, 24, false}, + { 30, 34, false}, + { 30, 44, false}, + { 30, 54, false}, + { 30, 55, false}, + { 30, 56, false}, + { 30, 57, false}, + { 30, 58, false}, + { 30, 59, false}, + { 30, 60, true}, + { 30, 61, true}, + { 30, 1024, true}, + { 30, 10064, true}, + + { 58, 1, false}, + { 58, 2, false}, + { 58, 3, false}, + { 58, 4, false}, + { 58, 14, false}, + { 58, 15, false}, + { 58, 16, false}, + { 58, 24, false}, + { 58, 34, false}, + { 58, 44, false}, + { 58, 54, false}, + { 58, 55, false}, + { 58, 56, false}, + { 58, 57, false}, + { 58, 58, false}, + { 58, 59, false}, + { 58, 60, true}, + { 58, 61, true}, + { 58, 1024, true}, + { 58, 10064, true}, + + { 59, 1, false}, + { 59, 2, false}, + { 59, 3, false}, + { 59, 4, false}, + { 59, 14, false}, + { 59, 15, false}, + { 59, 16, false}, + { 59, 24, false}, + { 59, 34, false}, + { 59, 44, false}, + { 59, 54, false}, + { 59, 55, false}, + { 59, 56, false}, + { 59, 57, false}, + { 59, 58, false}, + { 59, 59, false}, + { 59, 60, true}, + { 59, 61, true}, + { 59, 1024, true}, + { 59, 10064, true}, + + { 60, 1, false}, + { 60, 2, false}, + { 60, 3, false}, + { 60, 4, false}, + { 60, 14, false}, + { 60, 15, false}, + { 60, 16, false}, + { 60, 24, false}, + { 60, 34, false}, + { 60, 44, false}, + { 60, 54, false}, + { 60, 55, false}, + { 60, 56, false}, + { 60, 57, false}, + { 60, 58, false}, + { 60, 59, false}, + { 60, 60, true}, + { 60, 61, true}, + { 60, 1024, true}, + { 60, 10064, true}, + + { 61, 1, false}, + { 61, 2, false}, + { 61, 3, false}, + { 61, 4, false}, + { 61, 14, false}, + { 61, 15, false}, + { 61, 16, false}, + { 61, 24, false}, + { 61, 34, false}, + { 61, 44, false}, + { 61, 54, false}, + { 61, 55, false}, + { 61, 56, false}, + { 61, 57, false}, + { 61, 58, false}, + { 61, 59, false}, + { 61, 60, true}, + { 61, 61, true}, + { 61, 1024, true}, + { 61, 10064, true}, + + { 10000, 1, false}, + { 10000, 2, false}, + { 10000, 3, false}, + { 10000, 4, false}, + { 10000, 14, false}, + { 10000, 15, false}, + { 10000, 16, false}, + { 10000, 24, false}, + { 10000, 34, false}, + { 10000, 44, false}, + { 10000, 54, false}, + { 10000, 55, false}, + { 10000, 56, false}, + { 10000, 57, false}, + { 10000, 58, false}, + { 10000, 59, false}, + { 10000, 60, true}, + { 10000, 61, true}, + { 10000, 1024, true}, + { 10000, 10064, true}, + + { -1, 0, false}, + }; + + afpi = torture_afpinfo_new(tctx); + torture_assert_not_null_goto(tctx, afpi, ret, done, + "torture_afpinfo_new failed\n"); + + memcpy(afpi->afpi_FinderInfo, "FOO BAR ", 8); + + buf = torture_afpinfo_pack(afpi, afpi); + torture_assert_not_null_goto(tctx, buf, ret, done, + "torture_afpinfo_pack failed\n"); + + afpi_buf = talloc_zero_array(tctx, char, 10064); + torture_assert_not_null_goto(tctx, afpi_buf, ret, done, + "talloc_zero_array failed\n"); + memcpy(afpi_buf, buf, 60); + + zero_buf = talloc_zero_array(tctx, char, 10064); + torture_assert_not_null_goto(tctx, zero_buf, ret, done, + "talloc_zero_array failed\n"); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file\n"); + + for (i = 0; test_sizes[i].offset != -1; i++) { + struct smb2_handle h; + struct smb2_create c; + int expected_num_streams; + size_t fi_check_size; + + torture_comment(tctx, + "Test %d: offset=%jd size=%zu result=%s\n", + i, + (intmax_t)test_sizes[i].offset, + test_sizes[i].size, + test_sizes[i].expected_result ? "true":"false"); + + + c = (struct smb2_create) { + .in.desired_access = SEC_FILE_WRITE_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN_IF, + .in.fname = sname, + }; + + status = smb2_create(tree, tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h = c.out.file.handle; + + status = smb2_util_write(tree, + h, + zero_buf, + test_sizes[i].offset, + test_sizes[i].size); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_INVALID_PARAMETER, + ret, done, "smb2_util_write\n"); + + status = smb2_util_write(tree, + h, + afpi_buf, + test_sizes[i].offset, + test_sizes[i].size); + smb2_util_close(tree, h); + if (test_sizes[i].expected_result == true) { + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write\n"); + } else { + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_INVALID_PARAMETER, + ret, done, "smb2_util_write\n"); + } + + if (broken_osx) { + /* + * Currently macOS has a bug (Radar #45759458) where it + * writes more bytes then requested from uninitialized + * memory to the filesystem. That means it will likely + * write data to FinderInfo so the stream is not empty + * and thus listed when the number of streams is + * queried. + */ + min_offset_for_2streams = 2; + } + + if ((test_sizes[i].expected_result == true) && + (test_sizes[i].size > min_offset_for_2streams)) + { + expected_num_streams = 2; + } else { + expected_num_streams = 1; + } + + ret = check_stream_list(tree, tctx, fname, + expected_num_streams, + streams_afpinfo, false); + torture_assert_goto(tctx, ret == true, ret, done, + "Bad streams\n"); + + if (test_sizes[i].expected_result == false) { + continue; + } + + if (test_sizes[i].size <= 16) { + /* + * FinderInfo with the "FOO BAR " string we wrote above + * would start at offset 16. Check whether this test + * wrote 1 byte or more. + */ + goto next; + } + + fi_check_size = test_sizes[i].size - 16; + fi_check_size = MIN(fi_check_size, 8); + + ret = check_stream(tree, __location__, + tctx, tctx, + fname, AFPINFO_STREAM, + 0, 60, 16, fi_check_size, "FOO BAR "); + torture_assert_goto(tctx, ret == true, ret, done, + "Bad streams\n"); + +next: + status = smb2_util_unlink(tree, sname); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + bool missing_ok; + + missing_ok = test_sizes[i].expected_result == false; + missing_ok |= test_sizes[i].size <= 16; + + torture_assert_goto(tctx, missing_ok, + ret, done, "smb2_util_unlink\n"); + } + } + +done: + smb2_util_unlink(tree, fname); + return ret; +} + +static bool test_zero_file_id(struct torture_context *tctx, + struct smb2_tree *tree) +{ + const char *fname = "filtest_file_id"; + struct smb2_create create = {0}; + NTSTATUS status; + bool ret = true; + uint8_t zero_file_id[8] = {0}; + + torture_comment(tctx, "Testing zero file id\n"); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = fname; + create.in.query_on_disk_id = true; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, + done, + "test file could not be opened"); + torture_assert_mem_not_equal_goto(tctx, create.out.on_disk_id, + zero_file_id, 8, ret, done, + "unexpected zero file id"); + + smb2_util_close(tree, create.out.file.handle); + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = fname; + create.in.query_on_disk_id = true; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OK, ret, done, + "test file could not be opened with AAPL"); + torture_assert_mem_equal_goto(tctx, create.out.on_disk_id, zero_file_id, + 8, ret, done, "non-zero file id"); + + smb2_util_close(tree, create.out.file.handle); + +done: + smb2_util_unlink(tree, fname); + return ret; +} + +static bool copy_one_stream(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *tmp_ctx, + const char *src_sname, + const char *dst_sname) +{ + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + NTSTATUS status; + union smb_ioctl io; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + bool ok = false; + + ok = test_setup_copy_chunk(torture, tree, tmp_ctx, + 1, /* 1 chunk */ + src_sname, + &src_h, 256, /* fill 256 byte src file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + dst_sname, + &dest_h, 0, /* 0 byte dest file */ + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + &cc_copy, + &io); + torture_assert_goto(torture, ok == true, ok, done, + "setup copy chunk error\n"); + + /* copy all src file data (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 256; + + ndr_ret = ndr_push_struct_blob( + &io.smb2.in.out, tmp_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree, tmp_ctx, &io.smb2); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, + "FSCTL_SRV_COPYCHUNK\n"); + + ndr_ret = ndr_pull_struct_blob( + &io.smb2.out.out, tmp_ctx, &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_pull_srv_copychunk_rsp\n"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 256); /* total bytes written */ + torture_assert_goto(torture, ok == true, ok, done, + "bad copy chunk response data\n"); + + ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0); + if (!ok) { + torture_fail(torture, "inconsistent file data\n"); + } + +done: + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree, dest_h); + } + + return ok; +} + +static bool copy_finderinfo_stream(struct torture_context *torture, + struct smb2_tree *tree, + TALLOC_CTX *tmp_ctx, + const char *src_name, + const char *dst_name) +{ + struct smb2_handle src_h = {{0}}; + struct smb2_handle dest_h = {{0}}; + NTSTATUS status; + union smb_ioctl io; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + const char *type_creator = "SMB,OLE!"; + AfpInfo *info = NULL; + const char *src_name_afpinfo = NULL; + const char *dst_name_afpinfo = NULL; + bool ok = false; + + src_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", src_name, + AFPINFO_STREAM); + torture_assert_not_null_goto(torture, src_name_afpinfo, ok, done, + "talloc_asprintf failed\n"); + + dst_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", dst_name, + AFPINFO_STREAM); + torture_assert_not_null_goto(torture, dst_name_afpinfo, ok, done, + "talloc_asprintf failed\n"); + + info = torture_afpinfo_new(tmp_ctx); + torture_assert_not_null_goto(torture, info, ok, done, + "torture_afpinfo_new failed\n"); + + memcpy(info->afpi_FinderInfo, type_creator, 8); + ok = torture_write_afpinfo(tree, torture, tmp_ctx, src_name, info); + torture_assert_goto(torture, ok == true, ok, done, + "torture_write_afpinfo failed\n"); + + ok = test_setup_copy_chunk(torture, tree, tmp_ctx, + 1, /* 1 chunk */ + src_name_afpinfo, + &src_h, 0, + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + dst_name_afpinfo, + &dest_h, 0, + SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, + &cc_copy, + &io); + torture_assert_goto(torture, ok == true, ok, done, + "setup copy chunk error\n"); + + /* copy all src file data (via a single chunk desc) */ + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 60; + + ndr_ret = ndr_push_struct_blob( + &io.smb2.in.out, tmp_ctx, &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_push_srv_copychunk_copy\n"); + + status = smb2_ioctl(tree, tmp_ctx, &io.smb2); + torture_assert_ntstatus_ok_goto(torture, status, ok, done, + "FSCTL_SRV_COPYCHUNK\n"); + + ndr_ret = ndr_pull_struct_blob( + &io.smb2.out.out, tmp_ctx, &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + + torture_assert_ndr_success_goto(torture, ndr_ret, ok, done, + "ndr_pull_srv_copychunk_rsp\n"); + + smb2_util_close(tree, src_h); + ZERO_STRUCT(src_h); + smb2_util_close(tree, dest_h); + ZERO_STRUCT(dest_h); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + 60); /* total bytes written */ + torture_assert_goto(torture, ok == true, ok, done, + "bad copy chunk response data\n"); + + ok = check_stream(tree, __location__, torture, tmp_ctx, + dst_name, AFPINFO_STREAM, + 0, 60, 16, 8, type_creator); + torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n"); + +done: + if (!smb2_util_handle_empty(src_h)) { + smb2_util_close(tree, src_h); + } + if (!smb2_util_handle_empty(dest_h)) { + smb2_util_close(tree, dest_h); + } + + return ok; +} + +static bool test_copy_chunk_streams(struct torture_context *torture, + struct smb2_tree *tree) +{ + const char *src_name = "src"; + const char *dst_name = "dst"; + struct names { + const char *src_sname; + const char *dst_sname; + } names[] = { + { "src:foo", "dst:foo" }, + { "src" AFPRESOURCE_STREAM, "dst" AFPRESOURCE_STREAM } + }; + size_t i; + TALLOC_CTX *tmp_ctx = NULL; + bool ok = false; + + tmp_ctx = talloc_new(tree); + torture_assert_not_null_goto(torture, tmp_ctx, ok, done, + "torture_setup_file\n"); + + smb2_util_unlink(tree, src_name); + smb2_util_unlink(tree, dst_name); + + ok = torture_setup_file(torture, tree, src_name, false); + torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n"); + ok = torture_setup_file(torture, tree, dst_name, false); + torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n"); + + for (i = 0; i < ARRAY_SIZE(names); i++) { + ok = copy_one_stream(torture, tree, tmp_ctx, + names[i].src_sname, + names[i].dst_sname); + torture_assert_goto(torture, ok == true, ok, done, + "copy_one_stream failed\n"); + } + + ok = copy_finderinfo_stream(torture, tree, tmp_ctx, + src_name, dst_name); + torture_assert_goto(torture, ok == true, ok, done, + "copy_finderinfo_stream failed\n"); + +done: + smb2_util_unlink(tree, src_name); + smb2_util_unlink(tree, dst_name); + talloc_free(tmp_ctx); + return ok; +} + +/* + * Ensure this security descriptor has exactly one mode, uid + * and gid. + */ + +static NTSTATUS check_nfs_sd(const struct security_descriptor *psd) +{ + uint32_t i; + bool got_one_mode = false; + bool got_one_uid = false; + bool got_one_gid = false; + + if (psd->dacl == NULL) { + return NT_STATUS_INVALID_SECURITY_DESCR; + } + + for (i = 0; i < psd->dacl->num_aces; i++) { + if (dom_sid_compare_domain(&global_sid_Unix_NFS_Mode, + &psd->dacl->aces[i].trustee) == 0) { + if (got_one_mode == true) { + /* Can't have more than one. */ + return NT_STATUS_INVALID_SECURITY_DESCR; + } + got_one_mode = true; + } + } + for (i = 0; i < psd->dacl->num_aces; i++) { + if (dom_sid_compare_domain(&global_sid_Unix_NFS_Users, + &psd->dacl->aces[i].trustee) == 0) { + if (got_one_uid == true) { + /* Can't have more than one. */ + return NT_STATUS_INVALID_SECURITY_DESCR; + } + got_one_uid = true; + } + } + for (i = 0; i < psd->dacl->num_aces; i++) { + if (dom_sid_compare_domain(&global_sid_Unix_NFS_Groups, + &psd->dacl->aces[i].trustee) == 0) { + if (got_one_gid == true) { + /* Can't have more than one. */ + return NT_STATUS_INVALID_SECURITY_DESCR; + } + got_one_gid = true; + } + } + /* Must have at least one of each. */ + if (got_one_mode == false || + got_one_uid == false || + got_one_gid == false) { + return NT_STATUS_INVALID_SECURITY_DESCR; + } + return NT_STATUS_OK; +} + +static bool test_nfs_aces(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct security_ace ace; + struct dom_sid sid; + const char *fname = BASEDIR "\\nfs_aces.txt"; + struct smb2_handle h = {{0}}; + union smb_fileinfo finfo2; + union smb_setfileinfo set; + struct security_descriptor *psd = NULL; + NTSTATUS status; + bool ret = true; + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed"); + + /* clean slate ...*/ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + /* Create a test file. */ + status = torture_smb2_testfile_access(tree, + fname, + &h, + SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Get the ACL. */ + finfo2.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + finfo2.generic.level = RAW_FILEINFO_SEC_DESC; + finfo2.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, tctx, &finfo2); + CHECK_STATUS(status, NT_STATUS_OK); + + psd = finfo2.query_secdesc.out.sd; + + /* Ensure we have only single mode/uid/gid NFS entries. */ + status = check_nfs_sd(psd); + if (!NT_STATUS_IS_OK(status)) { + NDR_PRINT_DEBUG( + security_descriptor, + discard_const_p(struct security_descriptor, psd)); + } + CHECK_STATUS(status, NT_STATUS_OK); + + /* Add a couple of extra NFS uids and gids. */ + sid_compose(&sid, &global_sid_Unix_NFS_Users, 27); + init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0); + status = security_descriptor_dacl_add(psd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + status = security_descriptor_dacl_add(psd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + sid_compose(&sid, &global_sid_Unix_NFS_Groups, 300); + init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0); + status = security_descriptor_dacl_add(psd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + status = security_descriptor_dacl_add(psd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now set on the file handle. */ + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = h; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + set.set_secdesc.in.sd = psd; + status = smb2_setinfo_file(tree, &set); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Get the ACL again. */ + finfo2.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + finfo2.generic.level = RAW_FILEINFO_SEC_DESC; + finfo2.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, tctx, &finfo2); + CHECK_STATUS(status, NT_STATUS_OK); + + psd = finfo2.query_secdesc.out.sd; + + /* Ensure we have only single mode/uid/gid NFS entries. */ + status = check_nfs_sd(psd); + if (!NT_STATUS_IS_OK(status)) { + NDR_PRINT_DEBUG( + security_descriptor, + discard_const_p(struct security_descriptor, psd)); + } + CHECK_STATUS(status, NT_STATUS_OK); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_setinfo_stream_eof(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_create create; + union smb_setfileinfo sfinfo; + union smb_fileinfo finfo; + struct smb2_handle h1; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\file"; + const char *sname = BASEDIR "\\file:foo"; + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, + "talloc_new failed\n"); + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed"); + + torture_comment(tctx, "Test setting EOF on a stream\n"); + + smb2_deltree(tree, BASEDIR); + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + + status = torture_smb2_testfile(tree, fname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + smb2_util_close(tree, h1); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + status = smb2_util_write(tree, h1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + smb2_util_close(tree, h1); + + /* + * Test setting EOF to 21 + */ + + torture_comment(tctx, "Setting stream EOF to 21\n"); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 21; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, + ret, done, "set EOF 21 failed\n"); + + smb2_util_close(tree, h1); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + smb2_util_close(tree, h1); + + torture_assert_goto(tctx, finfo.standard_info.out.size == 21, + ret, done, "size != 21\n"); + + /* + * Test setting EOF to 0 + */ + + torture_comment(tctx, "Setting stream EOF to 0\n"); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + + smb2_util_close(tree, h1); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + smb2_util_close(tree, h1); + + torture_assert_goto(tctx, finfo.standard_info.out.size == 0, + ret, done, "size != 0\n"); + + /* + * Test setinfo end-of-file info to 1 + */ + + torture_comment(tctx, "Setting stream EOF to 1\n"); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set EOF 1 failed\n"); + + smb2_util_close(tree, h1); + + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + smb2_util_close(tree, h1); + + torture_assert_goto(tctx, finfo.standard_info.out.size == 1, + ret, done, "size != 1\n"); + + /* + * Test setting EOF to 0 with AAPL enabled, should delete stream + */ + + torture_comment(tctx, "Enabling AAPL extensions\n"); + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed\n"); + + torture_comment(tctx, "Setting stream EOF to 0\n"); + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + + smb2_util_close(tree, h1); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + + torture_comment( + tctx, "Setting main file EOF to 1 to force 0-truncate\n"); + + status = torture_smb2_testfile_access( + tree, + fname, + &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "set eof 1 failed\n"); + + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ret, + done, + "set eof 0 failed\n"); + + smb2_util_close(tree, h1); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = fname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + smb2_util_close(tree, h1); + + torture_comment(tctx, "Writing to stream after setting EOF to 0\n"); + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + status = smb2_util_write(tree, h1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + status = smb2_util_write(tree, h1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + smb2_util_close(tree, h1); + +done: + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, BASEDIR); + return ret; +} + +#define MAX_STREAMS 16 + +struct tcase { + const char *name; + uint32_t access; + const char *write_data; + size_t write_size; + struct tcase_results { + size_t size; + NTSTATUS initial_status; + NTSTATUS final_status; + int num_streams_open_handle; + const char *streams_open_handle[MAX_STREAMS]; + int num_streams_closed_handle; + const char *streams_closed_handle[MAX_STREAMS]; + } create, write, overwrite, eof, doc; +}; + +typedef enum {T_CREATE, T_WRITE, T_OVERWRITE, T_EOF, T_DOC} subtcase_t; + +static bool test_empty_stream_do_checks( + struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2, + struct tcase *tcase, + TALLOC_CTX *mem_ctx, + struct smb2_handle baseh, + struct smb2_handle streamh, + subtcase_t subcase) +{ + bool ret = false; + NTSTATUS status; + struct smb2_handle h1; + union smb_fileinfo finfo; + struct tcase_results *tcase_results = NULL; + + switch (subcase) { + case T_CREATE: + tcase_results = &tcase->create; + break; + case T_OVERWRITE: + tcase_results = &tcase->overwrite; + break; + case T_WRITE: + tcase_results = &tcase->write; + break; + case T_EOF: + tcase_results = &tcase->eof; + break; + case T_DOC: + tcase_results = &tcase->doc; + break; + } + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_STANDARD_INFORMATION, + .generic.in.file.handle = streamh, + }; + + /* + * Test: check size, same client + */ + + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size, + tcase_results->size, + ret, done, "Wrong size\n"); + + /* + * Test: open, same client + */ + + status = torture_smb2_open(tree, tcase->name, + SEC_FILE_READ_ATTRIBUTE, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->initial_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: check streams, same client + */ + + ret = check_stream_list_handle(tree, tctx, baseh, + tcase_results->num_streams_open_handle, + tcase_results->streams_open_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + /* + * Test: open, different client + */ + + status = torture_smb2_open(tree2, tcase->name, + SEC_FILE_READ_ATTRIBUTE, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->initial_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_STANDARD_INFORMATION, + .generic.in.file.handle = h1, + }; + + /* + * Test: check size, different client + */ + + status = smb2_getinfo_file(tree2, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size, + tcase_results->size, + ret, done, "Wrong size\n"); + + /* + * Test: check streams, different client + */ + + ret = check_stream_list(tree2, tctx, BASEDIR "\\file", + tcase_results->num_streams_open_handle, + tcase_results->streams_open_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + status = smb2_util_close(tree2, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + status = smb2_util_close(tree, streamh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + + /* + * Test: open after close, same client + */ + + status = torture_smb2_open(tree, tcase->name, + SEC_FILE_READ_DATA, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->final_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: open after close, different client + */ + + status = torture_smb2_open(tree2, tcase->name, + SEC_FILE_READ_DATA, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->final_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree2, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: check streams after close, same client + */ + + ret = check_stream_list_handle(tree, tctx, baseh, + tcase_results->num_streams_closed_handle, + tcase_results->streams_closed_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ret = true; + +done: + smb2_util_close(tree, streamh); + smb2_util_close(tree, baseh); + return ret; +} + +static bool test_empty_stream_do_one( + struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2, + struct tcase *tcase) +{ + bool ret = false; + NTSTATUS status; + struct smb2_handle baseh = {{0}}; + struct smb2_handle streamh; + struct smb2_create create; + union smb_setfileinfo sfinfo; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + torture_comment(tctx, "Testing stream [%s]\n", tcase->name); + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new\n"); + + /* + * Subtest: create + */ + torture_comment(tctx, "Subtest: T_CREATE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_CREATE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + if (!(tcase->access & SEC_FILE_WRITE_DATA)) { + /* + * All subsequent tests require write access + */ + ret = true; + goto done; + } + + /* + * Subtest: create and write + */ + torture_comment(tctx, "Subtest: T_WRITE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_WRITE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: overwrite + */ + torture_comment(tctx, "Subtest: T_OVERWRITE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF, + .in.fname = tcase->name, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + streamh = create.out.file.handle; + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_OVERWRITE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: setinfo EOF 0 + */ + torture_comment(tctx, "Subtest: T_EOF\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + sfinfo = (union smb_setfileinfo) { + .end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION, + .end_of_file_info.in.file.handle = streamh, + .end_of_file_info.in.size = 0, + }; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_EOF); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: delete-on-close + */ + torture_comment(tctx, "Subtest: T_DOC\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + sfinfo = (union smb_setfileinfo) { + .disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFORMATION, + .disposition_info.in.file.handle = streamh, + .disposition_info.in.delete_on_close = true, + }; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, + T_DOC); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + ret = true; + +done: + smb2_util_close(tree, baseh); + TALLOC_FREE(mem_ctx); + return ret; +} + +static bool test_empty_stream(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree *tree2 = NULL; + struct tcase *tcase = NULL; + const char *fname = BASEDIR "\\file"; + struct smb2_handle h1; + bool ret = true; + NTSTATUS status; + AfpInfo ai = (AfpInfo) { + .afpi_Signature = AFP_Signature, + .afpi_Version = AFP_Version, + .afpi_BackupTime = AFP_BackupTime, + .afpi_FinderInfo = "FOO BAR ", + }; + char *ai_blob = torture_afpinfo_pack(tctx, &ai); + struct tcase tcase_afpinfo_ro = (struct tcase) { + .name = BASEDIR "\\file" AFPINFO_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .create = { + .size = 60, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + }; + struct tcase tcase_afpinfo_rw = (struct tcase) { + .name = BASEDIR "\\file" AFPINFO_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = ai_blob, + .write_size = AFP_INFO_SIZE, + .create = { + .size = 60, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .write = { + .size = 60, + .initial_status = NT_STATUS_OK, + .final_status = NT_STATUS_OK, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 2, + .streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .streams_closed_handle = {"::$DATA", AFPINFO_STREAM}, + }, + .overwrite = { + .size = 60, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .eof = { + .size = 60, + .initial_status = NT_STATUS_OK, + .final_status = NT_STATUS_OK, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 2, + .streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .streams_closed_handle = {"::$DATA", AFPINFO_STREAM}, + }, + .doc = { + .size = 60, + .initial_status = NT_STATUS_DELETE_PENDING, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .streams_closed_handle = {"::$DATA"}, + }, + }; + + struct tcase tcase_afpresource_ro = (struct tcase) { + .name = BASEDIR "\\file" AFPRESOURCE_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .create = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + }; + struct tcase tcase_afpresource_rw = (struct tcase) { + .name = BASEDIR "\\file" AFPRESOURCE_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = "foo", + .write_size = 3, + .create = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .write = { + .size = 3, + .initial_status = NT_STATUS_OK, + .final_status = NT_STATUS_OK, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 2, + .streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM}, + .streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM}, + }, + .overwrite = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .eof = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .doc = { + .size = 3, + .initial_status = NT_STATUS_DELETE_PENDING, + .final_status = NT_STATUS_OK, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 2, + .streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM}, + .streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM}, + }, + }; + + struct tcase tcase_foo_ro = (struct tcase) { + .name = BASEDIR "\\file:foo", + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .write_data = "foo", + .write_size = 3, + .create = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + }; + + struct tcase tcase_foo_rw = (struct tcase) { + .name = BASEDIR "\\file:foo", + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = "foo", + .write_size = 3, + .create = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .write = { + .size = 3, + .initial_status = NT_STATUS_OK, + .final_status = NT_STATUS_OK, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 2, + .streams_open_handle = {"::$DATA", ":foo:$DATA"}, + .streams_closed_handle = {"::$DATA", ":foo:$DATA"}, + }, + .overwrite = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .eof = { + .size = 0, + .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 1, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + .doc = { + .size = 3, + .initial_status = NT_STATUS_DELETE_PENDING, + .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .num_streams_open_handle = 2, + .num_streams_closed_handle = 1, + .streams_open_handle = {"::$DATA", ":foo:$DATA"}, + .streams_closed_handle = {"::$DATA"}, + }, + }; + + struct tcase tcases[] = { + tcase_afpinfo_ro, + tcase_afpinfo_rw, + tcase_afpresource_ro, + tcase_afpresource_rw, + tcase_foo_ro, + tcase_foo_rw, + {0} + }; + + ret = torture_smb2_connection(tctx, &tree2); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_smb2_connection failed\n"); + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed\n"); + + ret = enable_aapl(tctx, tree2); + torture_assert(tctx, ret == true, "enable_aapl failed\n"); + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + + for (tcase = &tcases[0]; tcase->name != NULL; tcase++) { + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = test_empty_stream_do_one(tctx, tree, tree2, tcase); + torture_assert_goto(tctx, ret == true, ret, done, + "subtest failed\n"); + + status = smb2_util_unlink(tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + } + +done: + smb2_deltree(tree, BASEDIR); + TALLOC_FREE(tree2); + return ret; +} + +/* +------------------------------------------------------------------------------- +MagicNumber: 00051607 : AppleDouble +Version : 00020000 : Version 2 +Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X +Num. of ent: 0002 : 2 + +------------------------------------------------------------------------------- +Entry ID : 00000009 : Finder Info +Offset : 00000032 : 50 +Length : 00000EB0 : 3760 + +-DInfo-----: +Rect top : 0000 : 0 +Rect left : 0000 : 0 +Rect bottom: 0000 : 0 +Rect right : 0000 : 0 +isAlias : 0 +Invisible : 0 +hasBundle : 0 +nameLocked : 0 +Stationery : 0 +CustomIcon : 0 +Reserved : 0 +Inited : 1 +NoINITS : 0 +Shared : 0 +SwitchLaunc: 0 +Hidden Ext : 0 +color : 000 : none +isOnDesk : 0 +Location v : 0000 : 0 +Location h : 0000 : 0 +View : 0000 : .. + +-DXInfo----: +Scroll v : 0000 : 0 +Scroll h : 0000 : 0 +Rsvd|OpnChn: 00000000 : 0 +AreInvalid : 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +CustomBadge: 0 +ObjctIsBusy: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +unknown bit: 0 +RoutingInfo: 0 +unknown bit: 0 +unknown bit: 0 +Comment : 0000 : .. +PutAway : 00000000 : 0 + +-EA--------: +pad : 0000 : .. +magic : 41545452 : ATTR +debug_tag : 0081714C : 8483148 +total_size : 00000EE2 : 3810 +data_start : 00000098 : 152 +data_length: 00000039 : 57 +reserved[0]: 00000000 : .... +reserved[1]: 00000000 : .... +reserved[2]: 00000000 : .... +flags : 0000 : .. +num_attrs : 0001 : 1 +-EA ENTRY--: +offset : 00000098 : 152 +length : 00000039 : 57 +flags : 0000 : .. +namelen : 15 : 21 +-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 63 6F 6D 2E 61 70 70 6C 65 2E 71 75 61 72 61 6E : com.apple.quaran +00000010 : 74 69 6E 65 00 : tine. +-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 30 30 38 31 3B 36 32 65 61 33 37 66 64 3B 43 68 : 0081;62ea37fd;Ch +00000010 : 72 6F 6D 65 3B 42 35 39 46 42 39 45 44 2D 35 41 : rome;B59FB9ED-5A +00000020 : 32 39 2D 34 45 35 42 2D 38 35 36 43 2D 37 45 44 : 29-4E5B-856C-7ED +00000030 : 30 45 46 45 41 37 30 41 43 : 0EFEA70AC + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 : ................ +00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000020 : 00 00 41 54 54 52 00 81 71 4C 00 00 0E E2 00 00 : ..ATTR..qL...... +00000030 : 00 98 00 00 00 39 00 00 00 00 00 00 00 00 00 00 : .....9.......... +00000040 : 00 00 00 00 00 01 00 00 00 98 00 00 00 39 00 00 : .............9.. +00000050 : 15 63 6F 6D 2E 61 70 70 6C 65 2E 71 75 61 72 61 : .com.apple.quara +00000060 : 6E 74 69 6E 65 00 30 30 38 31 3B 36 32 65 61 33 : ntine.0081;62ea3 +00000070 : 37 66 64 3B 43 68 72 6F 6D 65 3B 42 35 39 46 42 : 7fd;Chrome;B59FB +00000080 : 39 45 44 2D 35 41 32 39 2D 34 45 35 42 2D 38 35 : 9ED-5A29-4E5B-85 +00000090 : 36 43 2D 37 45 44 30 45 46 45 41 37 30 41 43 00 : 6C-7ED0EFEA70AC. +000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000001F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000002F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000004F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000005F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000006F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000007F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000008F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000009F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + +------------------------------------------------------------------------------- +Entry ID : 00000002 : Resource Fork +Offset : 00000EE2 : 3810 +Length : 0000011E : 286 + +-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) +00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000010 : 54 68 69 73 20 72 65 73 6F 75 72 63 65 20 66 6F : This resource fo +00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally +00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. +00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ +00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ +00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. +*/ + +static char osx_adouble_dir_w_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x0e, 0xb0, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x0e, 0xe2, 0x00, 0x00, + 0x01, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x00, 0x81, 0x71, 0x4c, 0x00, 0x00, 0x0e, 0xe2, + 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x71, 0x75, 0x61, + 0x72, 0x61, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x00, + 0x30, 0x30, 0x38, 0x31, 0x3b, 0x36, 0x32, 0x65, + 0x61, 0x33, 0x37, 0x66, 0x64, 0x3b, 0x43, 0x68, + 0x72, 0x6f, 0x6d, 0x65, 0x3b, 0x42, 0x35, 0x39, + 0x46, 0x42, 0x39, 0x45, 0x44, 0x2d, 0x35, 0x41, + 0x32, 0x39, 0x2d, 0x34, 0x45, 0x35, 0x42, 0x2d, + 0x38, 0x35, 0x36, 0x43, 0x2d, 0x37, 0x45, 0x44, + 0x30, 0x45, 0x46, 0x45, 0x41, 0x37, 0x30, 0x41, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff +}; + +static bool test_delete_trigger_convert_sharing_violation( + struct torture_context *tctx, + struct smb2_tree *tree1) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *dirname = BASEDIR "\\dir"; + const char *adname = BASEDIR "\\._dir"; + struct smb2_handle testdirh; + struct smb2_create create; + AfpInfo *info = NULL; + bool ret = true; + NTSTATUS status; + + smb2_deltree(tree1, BASEDIR); + + status = torture_smb2_testdir(tree1, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree1, testdirh); + + status = torture_smb2_testdir(tree1, dirname, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree1, testdirh); + + ret = torture_setup_file(tctx, tree1, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree1, __location__, tctx, mem_ctx, + adname, NULL, 0, + sizeof(osx_adouble_dir_w_xattr), + osx_adouble_dir_w_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + /* + * 1) Create a non-empty AFP_AfpInfo stream + */ + + info = torture_afpinfo_new(mem_ctx); + torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed"); + + /* Set "Inited" flag (any other would do too) */ + info->afpi_FinderInfo[8] = 0x01; + + ret = torture_write_afpinfo(tree1, tctx, mem_ctx, dirname, info); + torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed"); + + ret = write_stream(tree1, __location__, tctx, mem_ctx, + adname, NULL, 0, + sizeof(osx_adouble_dir_w_xattr), + osx_adouble_dir_w_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + /* + * 2) Create a second stream + */ + + ret = write_stream(tree1, __location__, tctx, mem_ctx, + dirname, ":org.samba.boom", 0, + strlen("boom"), + "boom"); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_STD_DELETE, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = dirname, + }; + + status = smb2_create(tree1, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + status = smb2_util_close(tree1, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + +done: + smb2_deltree(tree1, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +/* + * Note: This test depends on "vfs objects = catia fruit streams_xattr". For + * some tests torture must be run on the host it tests and takes an additional + * argument with the local path to the share: + * "--option=torture:localdir=". + * + * When running against an OS X SMB server add "--option=torture:osx=true" + */ +struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "fruit"); + + suite->description = talloc_strdup(suite, "vfs_fruit tests"); + + torture_suite_add_1smb2_test(suite, "copyfile", test_copyfile); + torture_suite_add_1smb2_test(suite, "read metadata", test_read_afpinfo); + torture_suite_add_1smb2_test(suite, "write metadata", test_write_atalk_metadata); + torture_suite_add_1smb2_test(suite, "resource fork IO", test_write_atalk_rfork_io); + torture_suite_add_1smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl); + torture_suite_add_1smb2_test(suite, "stream names", test_stream_names); + torture_suite_add_1smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate); + torture_suite_add_1smb2_test(suite, "opening and creating resource fork", test_rfork_create); + torture_suite_add_1smb2_test(suite, "fsync_resource_fork", test_rfork_fsync); + torture_suite_add_1smb2_test(suite, "rename_dir_openfile", test_rename_dir_openfile); + torture_suite_add_1smb2_test(suite, "File without AFP_AfpInfo", test_afpinfo_enoent); + torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpInfo", test_create_delete_on_close); + torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpInfo", test_setinfo_delete_on_close); + torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpInfo", test_setinfo_eof); + torture_suite_add_1smb2_test(suite, "delete AFP_AfpInfo by writing all 0", test_afpinfo_all0); + torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpResource", test_create_delete_on_close_resource); + torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpResource", test_setinfo_delete_on_close_resource); + torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpResource", test_setinfo_eof_resource); + torture_suite_add_1smb2_test(suite, "setinfo eof stream", test_setinfo_stream_eof); + torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo); + torture_suite_add_1smb2_test(suite, "delete", test_delete_file_with_rfork); + torture_suite_add_1smb2_test(suite, "read open rsrc after rename", test_rename_and_read_rsrc); + torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs); + torture_suite_add_2ns_smb2_test(suite, "invalid AFP_AfpInfo", test_invalid_afpinfo); + torture_suite_add_1smb2_test(suite, "creating rsrc with read-only access", test_rfork_create_ro); + torture_suite_add_1smb2_test(suite, "copy-chunk streams", test_copy_chunk_streams); + torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion); + torture_suite_add_1smb2_test(suite, "NFS ACE entries", test_nfs_aces); + torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion without embedded xattr", test_adouble_conversion_wo_xattr); + torture_suite_add_1smb2_test(suite, "empty_stream", test_empty_stream); + torture_suite_add_1smb2_test(suite, "writing_afpinfo", test_writing_afpinfo); + torture_suite_add_1smb2_test(suite, "delete_trigger_convert_sharing_violation", test_delete_trigger_convert_sharing_violation); + + return suite; +} + +static bool test_stream_names_local(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + struct smb2_create create; + struct smb2_handle h; + const char *fname = BASEDIR "\\stream_names.txt"; + const char *sname1; + bool ret; + /* UTF8 private use are starts at 0xef 0x80 0x80 (0xf000) */ + const char *streams[] = { + ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ + ":bar" "\xef\x80\xa2" "baz:$DATA", /* "bar:baz:$DATA" */ + "::$DATA" + }; + const char *localdir = NULL; + + localdir = torture_setting_string(tctx, "localdir", NULL); + if (localdir == NULL) { + torture_skip(tctx, "Need localdir for test"); + } + + sname1 = talloc_asprintf(mem_ctx, "%s%s", fname, streams[0]); + + /* clean slate ...*/ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + torture_comment(tctx, "(%s) testing stream names\n", __location__); + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_WRITE_DATA; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create.in.create_disposition = NTCREATEX_DISP_CREATE; + create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + create.in.fname = sname1; + + status = smb2_create(tree, mem_ctx, &create); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, create.out.file.handle, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + smb2_util_close(tree, create.out.file.handle); + + ret = torture_setup_local_xattr(tctx, "localdir", BASEDIR "/stream_names.txt", + "user.DosStream.bar:baz:$DATA", + "data", strlen("data")); + CHECK_VALUE(ret, true); + + ret = check_stream_list(tree, tctx, fname, 3, streams, false); + CHECK_VALUE(ret, true); + +done: + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_fruit_locking_conflict(struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx; + struct smb2_create create; + struct smb2_handle h; + struct smb2_lock lck; + struct smb2_lock_element el; + const char *fname = BASEDIR "\\locking_conflict.txt"; + NTSTATUS status; + bool ret = false; + + mem_ctx = talloc_new(tctx); + torture_assert_not_null(tctx, mem_ctx, "talloc_new failed"); + + /* clean slate ...*/ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_READ, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + .in.create_disposition = NTCREATEX_DISP_CREATE, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + status = smb2_create(tree, mem_ctx, &create); + CHECK_STATUS(status, NT_STATUS_OK); + h = create.out.file.handle; + + /* Add AD_FILELOCK_RSRC_DENY_WR lock. */ + el = (struct smb2_lock_element) { + .offset = 0xfffffffffffffffc, + .length = 1, + .flags = SMB2_LOCK_FLAG_EXCLUSIVE, + }; + lck = (struct smb2_lock) { + .in.lock_count = 1, + .in.file.handle = h, + .in.locks = &el, + }; + + /* + * Lock up to and including: + * AD_FILELOCK_OPEN_WR + * AD_FILELOCK_OPEN_RD + * This is designed to cause a NetAtalk + * locking conflict on the next open, + * even though the share modes are + * compatible. + */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + el = (struct smb2_lock_element) { + .offset = 0, + .length = 0x7ffffffffffffff7, + .flags = SMB2_LOCK_FLAG_EXCLUSIVE, + }; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + create = (struct smb2_create) { + .in.desired_access = + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = fname, + }; + + /* + * Open on the second tree - ensure we are + * emulating trying to access with a NetATalk + * process with an existing open/deny mode. + */ + status = smb2_create(tree2, mem_ctx, &create); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + { + struct smb2_close cl = { + .level = RAW_CLOSE_SMB2, + .in.file.handle = h, + }; + smb2_close(tree, &cl); + } + + ret = true; +done: + return ret; +} + +struct torture_suite *torture_vfs_fruit_netatalk(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "fruit_netatalk"); + + suite->description = talloc_strdup(suite, "vfs_fruit tests for Netatalk interop that require fruit:metadata=netatalk"); + + torture_suite_add_1smb2_test(suite, "read netatalk metadata", test_read_netatalk_metadata); + torture_suite_add_1smb2_test(suite, "stream names with locally created xattr", test_stream_names_local); + torture_suite_add_2smb2_test( + suite, "locking conflict", test_fruit_locking_conflict); + + return suite; +} + +struct torture_suite *torture_vfs_fruit_file_id(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "fruit_file_id"); + + suite->description = + talloc_strdup(suite, "vfs_fruit tests for on-disk file ID that " + "require fruit:zero_file_id=yes"); + + torture_suite_add_1smb2_test(suite, "zero file id if AAPL negotiated", + test_zero_file_id); + + return suite; +} + +static bool test_timemachine_volsize(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h = {{0}}; + union smb_fsinfo fsinfo; + NTSTATUS status; + bool ok = true; + const char *info_plist = + "\n" + " band-size\n" + " 8192\n" + "\n"; + + smb2_deltree(tree, "test.sparsebundle"); + + ok = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ok, ok, done, "enable_aapl failed"); + + status = smb2_util_mkdir(tree, "test.sparsebundle"); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "smb2_util_mkdir\n"); + + ok = write_stream(tree, __location__, tctx, mem_ctx, + "test.sparsebundle/Info.plist", NULL, + 0, strlen(info_plist), info_plist); + torture_assert_goto(tctx, ok, ok, done, "write_stream failed\n"); + + status = smb2_util_mkdir(tree, "test.sparsebundle/bands"); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "smb2_util_mkdir\n"); + + ok = torture_setup_file(tctx, tree, "test.sparsebundle/bands/1", false); + torture_assert_goto(tctx, ok, ok, done, "torture_setup_file failed\n"); + + ok = torture_setup_file(tctx, tree, "test.sparsebundle/bands/2", false); + torture_assert_goto(tctx, ok, ok, done, "torture_setup_file failed\n"); + + status = smb2_util_roothandle(tree, &h); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle"); + + ZERO_STRUCT(fsinfo); + fsinfo.generic.level = RAW_QFS_SIZE_INFORMATION; + fsinfo.generic.handle = h; + + status = smb2_getinfo_fs(tree, tree, &fsinfo); + torture_assert_ntstatus_ok(tctx, status, "smb2_getinfo_fs failed"); + + torture_comment(tctx, "sectors_per_unit: %" PRIu32"\n" + "bytes_per_sector: %" PRIu32"\n" + "total_alloc_units: %" PRIu64"\n" + "avail_alloc_units: %" PRIu64"\n", + fsinfo.size_info.out.sectors_per_unit, + fsinfo.size_info.out.bytes_per_sector, + fsinfo.size_info.out.total_alloc_units, + fsinfo.size_info.out.avail_alloc_units); + + /* + * Let me explain the numbers: + * + * - the share is set to "fruit:time machine max size = 32K" + * - we've faked a bandsize of 8 K in the Info.plist file + * - we've created two bands files + * - one allocation unit is made of two sectors with 512 B each + * => we've consumed 16 allocation units, there should be 16 free + */ + + torture_assert_goto(tctx, fsinfo.size_info.out.sectors_per_unit == 2, + ok, done, "Bad sectors_per_unit"); + + torture_assert_goto(tctx, fsinfo.size_info.out.bytes_per_sector == 512, + ok, done, "Bad bytes_per_sector"); + + torture_assert_goto(tctx, fsinfo.size_info.out.total_alloc_units == 32, + ok, done, "Bad total_alloc_units"); + + torture_assert_goto(tctx, fsinfo.size_info.out.avail_alloc_units == 16, + ok, done, "Bad avail_alloc_units"); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_deltree(tree, "test.sparsebundle"); + talloc_free(mem_ctx); + return ok; +} + +struct torture_suite *torture_vfs_fruit_timemachine(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "fruit_timemachine"); + + suite->description = talloc_strdup( + suite, "vfs_fruit tests for TimeMachine"); + + torture_suite_add_1smb2_test(suite, "Timemachine-volsize", + test_timemachine_volsize); + + return suite; +} + +static bool test_convert_xattr_and_empty_rfork_then_delete( + struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_adouble_conversion"; + const char *adname = BASEDIR "/._test_adouble_conversion"; + const char *rfork = BASEDIR "\\test_adouble_conversion" AFPRESOURCE_STREAM_NAME; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + const char *streams[] = { + "::$DATA", + AFPINFO_STREAM, + ":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA", + ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ + }; + struct smb2_create create; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + bool delete_empty_adfiles; + int expected_num_files; + + delete_empty_adfiles = torture_setting_bool(tctx, + "delete_empty_adfiles", + false); + + smb2_deltree(tree1, BASEDIR); + + status = torture_smb2_testdir(tree1, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree1, testdirh); + + ret = torture_setup_file(tctx, tree1, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = torture_setup_file(tctx, tree1, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree1, __location__, tctx, mem_ctx, + adname, NULL, + 0, sizeof(osx_adouble_w_xattr), osx_adouble_w_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + ret = enable_aapl(tctx, tree2); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + /* + * Issue a smb2_find(), this triggers the server-side conversion + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree2, tree2, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree2, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + /* + * Check number of streams + */ + + ret = check_stream_list(tree2, tctx, fname, 4, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + /* + * Check Resource Fork is gone + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = rfork, + }; + + status = smb2_create(tree2, mem_ctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Bad smb2_create return\n"); + + /* + * Check xattr data has been migrated from the AppleDouble file to + * streams. + */ + + ret = check_stream(tree2, __location__, tctx, mem_ctx, + fname, AFPINFO_STREAM, + 0, 60, 16, 8, "TESTSLOW"); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPINFO_STREAM failed\n"); + + ret = check_stream(tree2, __location__, tctx, mem_ctx, + fname, ":foo" "\xef\x80\xa2" "bar", /* foo:bar */ + 0, 3, 0, 3, "baz"); + torture_assert_goto(tctx, ret == true, ret, done, + "check foo stream failed\n"); + + /* + * Now check number of files. If delete_empty_adfiles is set, the + * AppleDouble files should have been deleted. + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree2, tree2, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree2, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + if (delete_empty_adfiles) { + expected_num_files = 3; + } else { + expected_num_files = 4; + } + torture_assert_int_equal_goto(tctx, count, expected_num_files, ret, done, + "Wrong number of files\n"); + +done: + smb2_deltree(tree1, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +struct torture_suite *torture_vfs_fruit_conversion(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "fruit_conversion"); + + suite->description = talloc_strdup( + suite, "vfs_fruit conversion tests"); + + torture_suite_add_2ns_smb2_test( + suite, "convert_xattr_and_empty_rfork_then_delete", + test_convert_xattr_and_empty_rfork_then_delete); + + return suite; +} + +/* + * The buf below contains the following AppleDouble encoded data: + * + * ----------------------------------------------------------------------------- + * MagicNumber: 00051607 : AppleDouble + * Version : 00020000 : Version 2 + * Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X + * Num. of ent: 0002 : 2 + * + * ----------------------------------------------------------------------------- + * Entry ID : 00000002 : Resource Fork + * Offset : 0000009A : 154 + * Length : 00000004 : 4 + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 62 61 72 00 : bar. + * + * ----------------------------------------------------------------------------- + * Entry ID : 00000009 : Finder Info + * Offset : 00000032 : 50 + * Length : 00000068 : 104 + * + * -FInfo-----: + * Type : 464F4F20 : FOO + * Creator : 42415220 : BAR + * isAlias : 0 + * Invisible : 0 + * hasBundle : 0 + * nameLocked : 0 + * Stationery : 0 + * CustomIcon : 0 + * Reserved : 0 + * Inited : 0 + * NoINITS : 0 + * Shared : 0 + * SwitchLaunc: 0 + * Hidden Ext : 0 + * color : 000 : none + * isOnDesk : 0 + * Location v : 0000 : 0 + * Location h : 0000 : 0 + * Fldr : 0000 : .. + * + * -FXInfo----: + * Rsvd|IconID: 0000 : 0 + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * AreInvalid : 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * CustomBadge: 0 + * ObjctIsBusy: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * RoutingInfo: 0 + * unknown bit: 0 + * unknown bit: 0 + * Rsvd|commnt: 0000 : 0 + * PutAway : 00000000 : 0 + * + * -EA--------: + * pad : 0000 : + * magic : 41545452 : ATTR + * debug_tag : 00000000 : 0 + * total_size : 0000009A : 154 + * data_start : 00000096 : 150 + * data_length: 00000004 : 4 + * reserved[0]: 00000000 : .... + * reserved[1]: 00000000 : .... + * reserved[2]: 00000000 : .... + * flags : 0000 : .. + * num_attrs : 0001 : 1 + * -EA ENTRY--: + * offset : 00000096 : 150 + * length : 00000004 : 4 + * flags : 0000 : .. + * namelen : 13 : 19 + * -EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 6F 72 67 2E 73 61 6D 62 61 EF 80 A2 77 6F 6F 68 : org.samba...wooh + * 00000010 : 6F 6F 00 : oo. + * -EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 62 61 72 00 : bar. + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 46 4F 4F 20 42 41 52 20 00 00 00 00 00 00 00 00 : FOO BAR ........ + * 00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000020 : 00 00 41 54 54 52 00 00 00 00 00 00 00 9A 00 00 : baATTR.......... + * 00000030 : 00 96 00 00 00 04 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000040 : 00 00 00 00 00 01 00 00 00 96 00 00 00 04 00 00 : ................ + * 00000050 : 13 6F 72 67 2E 73 61 6D 62 61 EF 80 A2 77 6F 6F : .org.samba...woo + * 00000060 : 68 6F 6F 00 62 61 72 00 : hoo.bar. + * + * It was created with: + * + * $ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"' + */ +static char unconvert_adfile_data[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x98, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, + 0x00, 0x66, 0x46, 0x4f, 0x4f, 0x20, 0x42, 0x41, + 0x52, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x11, 0x6f, 0x72, 0x67, 0x2e, 0x73, + 0x61, 0x6d, 0x62, 0x61, 0x3a, 0x77, 0x6f, 0x6f, + 0x68, 0x6f, 0x6f, 0x00, 0x62, 0x61, 0x72, 0x00, + 0x62, 0x61, 0x72, 0x00 +}; + +static bool test_unconvert(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\unconvert"; + const char *adname = BASEDIR "\\._unconvert"; + const char *net = NULL; + const char *share = NULL; + AfpInfo *afpi = NULL; + char *cmd = NULL; + struct smb2_handle h1; + union smb_fileinfo finfo; + size_t adsize; + NTSTATUS status; + int result; + bool ret = true; + + torture_assert_not_null_goto(tctx, tree2, ret, done, + "Need a second share without fruit\n"); + + net = torture_setting_string(tctx, "net", NULL); + torture_assert_not_null_goto(tctx, net, ret, done, + "Need path to 'net'"); + + share = torture_setting_string(tctx, "sharename", NULL); + torture_assert_not_null_goto(tctx, share, ret, done, + "Need sharename"); + + torture_comment(tctx, "Testing unconvert\n"); + + smb2_deltree(tree1, BASEDIR); + + status = torture_smb2_testdir(tree1, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree1, h1); + + ret = torture_setup_file(tctx, tree1, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file"); + + afpi = torture_afpinfo_new(tctx); + torture_assert_not_null_goto(tctx, afpi, ret, done, + "torture_afpinfo_new failed\n"); + + memcpy(afpi->afpi_FinderInfo, "FOO BAR ", 8); + + ret = torture_write_afpinfo(tree1, tctx, tctx, fname, afpi); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_write_afpinfo failed\n"); + + ret = write_stream(tree1, __location__, tctx, tctx, + fname, + /* + * \xef\x80\xa2 is ':' mapped to Unicoe private range + */ + ":org.samba" "\xef\x80\xa2" "woohoo", + 0, 4, "bar"); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + ret = write_stream(tree1, __location__, tctx, tctx, + fname, AFPRESOURCE_STREAM_NAME, + 0, 4, "bar"); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + cmd = talloc_asprintf(tctx, + "%s --recursive vfs stream2adouble %s %s/", + net, + share, + BASEDIR); + torture_assert_not_null_goto(tctx, cmd, ret, done, + "talloc_asprintf failed\n"); + + torture_comment(tctx, "cmd: %s\n", cmd); + + result = system(cmd); + torture_assert_int_equal_goto(tctx, result, 0, ret, done, + "command failed\n"); + + status = torture_smb2_testfile(tree2, adname, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_ALL_INFORMATION, + .generic.in.file.handle = h1, + }; + + status = smb2_getinfo_file(tree2, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree2, h1); + + adsize = finfo.all_info.out.size; + torture_assert_int_equal_goto(tctx, adsize, + sizeof(unconvert_adfile_data), + ret, done, "wrong size\n"); + + ret = check_stream(tree2, __location__, tctx, tctx, + adname, "", 0, adsize, 0, adsize, + unconvert_adfile_data); + torture_assert_goto(tctx, ret == true, ret, done, + "check_stream failed\n"); + +done: +// smb2_deltree(tree1, BASEDIR); + return ret; +} + +struct torture_suite *torture_vfs_fruit_unfruit(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "unfruit"); + + suite->description = talloc_strdup( + suite, "test converting back to AppleDouble"); + + torture_suite_add_2ns_smb2_test(suite, + "unconvert", + test_unconvert); + + return suite; +} + +/* + * Write an invalid AFP_AfpInfo stream header + */ +bool test_fruit_validate_afpinfo(struct torture_context *tctx, + struct smb2_tree *tree) +{ + bool expect_invalid_param = torture_setting_bool(tctx, "validate_afpinfo", true); + const char *fname = "test_fruit_validate_afpinfo"; + const char *sname = "test_fruit_validate_afpinfo" AFPINFO_STREAM_NAME; + struct smb2_handle handle; + AfpInfo *afpinfo = NULL; + char *afpinfo_buf = NULL; + uint8_t valbuf[8]; + NTSTATUS status; + bool ret = true; + + torture_comment(tctx, "Checking create of AfpInfo stream\n"); + + smb2_util_unlink(tree, fname); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed"); + + afpinfo = torture_afpinfo_new(tctx); + torture_assert_not_null_goto(tctx, afpinfo, ret, done, + "torture_afpinfo_new failed\n"); + + memcpy(afpinfo->afpi_FinderInfo, "FOO BAR ", 8); + + ret = torture_write_afpinfo(tree, tctx, tctx, fname, afpinfo); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_write_afpinfo failed\n"); + + afpinfo_buf = talloc_zero_size(tctx, 60); + torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, + "torture_afpinfo_new failed"); + memcpy(afpinfo_buf + 16, "FOO ", 4); + + status = torture_smb2_testfile_access( + tree, sname, &handle, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + status = smb2_util_write(tree, handle, afpinfo_buf, 0, AFP_INFO_SIZE); + if (expect_invalid_param) { + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_INVALID_PARAMETER, ret, done, + "write didn't fail as expected\n"); + } else { + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed"); + } + + smb2_util_close(tree, handle); + + /* + * Verify the server fixed the header + */ + PUSH_BE_U32(valbuf, 0, AFP_Signature); + PUSH_BE_U32(valbuf + 4, 0, AFP_Version); + ret = check_stream(tree, __location__, tctx, tctx, fname, + AFPINFO_STREAM, 0, 60, 0, 8, (char *)valbuf); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed"); + +done: + smb2_util_unlink(tree, fname); + return ret; +} diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c new file mode 100644 index 0000000..3d402ee --- /dev/null +++ b/source4/torture/vfs/vfs.c @@ -0,0 +1,123 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Ralph Boehme 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libcli/libcli.h" +#include "../lib/util/dlinklist.h" + +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/cmdline/cmdline.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +#include "torture/util.h" +#include "torture/smbtorture.h" +#include "torture/vfs/proto.h" +#include "torture/smb2/proto.h" + +static bool wrap_2ns_smb2_test(struct torture_context *torture_ctx, + struct torture_tcase *tcase, + struct torture_test *test) +{ + bool (*fn) (struct torture_context *, struct smb2_tree *, struct smb2_tree *); + bool ok; + + struct smb2_tree *tree1 = NULL; + struct smb2_tree *tree2 = NULL; + TALLOC_CTX *mem_ctx = talloc_new(torture_ctx); + + if (!torture_smb2_connection(torture_ctx, &tree1)) { + torture_fail(torture_ctx, + "Establishing SMB2 connection failed\n"); + return false; + } + + /* + * This is a trick: + * The test might close the connection. If we steal the tree context + * before that and free the parent instead of tree directly, we avoid + * a double free error. + */ + talloc_steal(mem_ctx, tree1); + + ok = torture_smb2_con_sopt(torture_ctx, "share2", &tree2); + if (ok) { + talloc_steal(mem_ctx, tree2); + } + + fn = test->fn; + + ok = fn(torture_ctx, tree1, tree2); + + /* the test may already have closed some of the connections */ + talloc_free(mem_ctx); + + return ok; +} + +/* + * Run a test with 2 connected trees, the default share and another + * taken from option strings "torture:share2" + */ +struct torture_test *torture_suite_add_2ns_smb2_test(struct torture_suite *suite, + const char *name, + bool (*run)(struct torture_context *, + struct smb2_tree *, + struct smb2_tree *)) +{ + struct torture_test *test; + struct torture_tcase *tcase; + + tcase = torture_suite_add_tcase(suite, name); + + test = talloc(tcase, struct torture_test); + + test->name = talloc_strdup(test, name); + test->description = NULL; + test->run = wrap_2ns_smb2_test; + test->fn = run; + test->dangerous = false; + + DLIST_ADD_END(tcase->tests, test); + + return test; +} + +NTSTATUS torture_vfs_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "vfs"); + + suite->description = talloc_strdup(suite, "VFS modules tests"); + + torture_suite_add_suite(suite, torture_vfs_fruit(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_netatalk(suite)); + torture_suite_add_suite(suite, torture_acl_xattr(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_file_id(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_timemachine(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_conversion(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_unfruit(suite)); + torture_suite_add_1smb2_test(suite, "fruit_validate_afpinfo", test_fruit_validate_afpinfo); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/winbind/struct_based.c b/source4/torture/winbind/struct_based.c new file mode 100644 index 0000000..1c8751e --- /dev/null +++ b/source4/torture/winbind/struct_based.c @@ -0,0 +1,1190 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - winbind struct based protocol + Copyright (C) Stefan Metzmacher 2007 + Copyright (C) Michael Adam 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "nsswitch/winbind_nss_config.h" +#include "nsswitch/winbind_struct_protocol.h" +#include "nsswitch/libwbclient/wbclient_internal.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/netlogon.h" +#include "param/param.h" +#include "../libcli/auth/pam_errors.h" +#include "torture/winbind/proto.h" +#include "lib/util/string_wrappers.h" + +#define DO_STRUCT_REQ_REP_EXT(op,req,rep,expected,strict,warnaction,cmt) do { \ + const char *__cmt = (cmt); \ + wbcErr __wbc_status = WBC_ERR_UNKNOWN_FAILURE; \ + NSS_STATUS __got, __expected = (expected); \ + __wbc_status = wbcRequestResponse(NULL, op, req, rep); \ + switch (__wbc_status) { \ + case WBC_ERR_SUCCESS: \ + __got = NSS_STATUS_SUCCESS; \ + break; \ + case WBC_ERR_WINBIND_NOT_AVAILABLE: \ + __got = NSS_STATUS_UNAVAIL; \ + break; \ + case WBC_ERR_DOMAIN_NOT_FOUND: \ + __got = NSS_STATUS_NOTFOUND; \ + break; \ + default: \ + torture_result(torture, TORTURE_FAIL, \ + __location__ ": " __STRING(op) \ + " returned unmapped %s, expected nss %d%s%s", \ + wbcErrorString(__wbc_status), __expected, \ + (__cmt) ? ": " : "", \ + (__cmt) ? (__cmt) : ""); \ + return false; \ + } \ + if (__got != __expected) { \ + if (strict) { \ + torture_result(torture, TORTURE_FAIL, \ + __location__ ": " __STRING(op) \ + " returned %s, got %d , expected %d%s%s", \ + wbcErrorString(__wbc_status), __got, __expected, \ + (__cmt) ? ": " : "", \ + (__cmt) ? (__cmt) : ""); \ + return false; \ + } else { \ + torture_warning(torture, \ + __location__ ": " __STRING(op) \ + " returned %s, got %d , expected %d%s%s", \ + wbcErrorString(__wbc_status), __got, __expected, \ + (__cmt) ? ": " : "", \ + (__cmt) ? (__cmt) : ""); \ + warnaction; \ + } \ + } \ +} while(0) + +#undef _STRUCT_NOOP +#define _STRUCT_NOOP do {} while(0); +#define DO_STRUCT_REQ_REP(op,req,rep) do { \ + DO_STRUCT_REQ_REP_EXT(op,req,rep,NSS_STATUS_SUCCESS,true, _STRUCT_NOOP, NULL); \ +} while (0) + +static bool torture_winbind_struct_interface_version(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + torture_comment(torture, "Running WINBINDD_INTERFACE_VERSION (struct based)\n"); + + DO_STRUCT_REQ_REP(WINBINDD_INTERFACE_VERSION, &req, &rep); + + torture_assert_int_equal(torture, + rep.data.interface_version, + WINBIND_INTERFACE_VERSION, + "winbind server and client doesn't match"); + + return true; +} + +static bool torture_winbind_struct_ping(struct torture_context *torture) +{ + struct timeval tv = timeval_current(); + int timelimit = torture_setting_int(torture, "timelimit", 5); + uint32_t total = 0; + + torture_comment(torture, + "Running WINBINDD_PING (struct based) for %d seconds\n", + timelimit); + + while (timeval_elapsed(&tv) < timelimit) { + DO_STRUCT_REQ_REP(WINBINDD_PING, NULL, NULL); + total++; + } + + torture_comment(torture, + "%u (%.1f/s) WINBINDD_PING (struct based)\n", + total, total / timeval_elapsed(&tv)); + + return true; +} + + +static char winbind_separator(struct torture_context *torture) +{ + struct winbindd_response rep; + + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_INFO, NULL, &rep); + + return rep.data.info.winbind_separator; +} + +static bool torture_winbind_struct_info(struct torture_context *torture) +{ + struct winbindd_response rep; + const char *separator; + + ZERO_STRUCT(rep); + + torture_comment(torture, "Running WINBINDD_INFO (struct based)\n"); + + DO_STRUCT_REQ_REP(WINBINDD_INFO, NULL, &rep); + + separator = torture_setting_string(torture, + "winbindd_separator", + lpcfg_winbind_separator(torture->lp_ctx)); + + torture_assert_int_equal(torture, + rep.data.info.winbind_separator, + *separator, + "winbind separator doesn't match"); + + torture_comment(torture, "Samba Version '%s'\n", + rep.data.info.samba_version); + + return true; +} + +static bool torture_winbind_struct_priv_pipe_dir(struct torture_context *torture) +{ + struct winbindd_response rep; + const char *got_dir; + + ZERO_STRUCT(rep); + + torture_comment(torture, "Running WINBINDD_PRIV_PIPE_DIR (struct based)\n"); + + DO_STRUCT_REQ_REP(WINBINDD_PRIV_PIPE_DIR, NULL, &rep); + + got_dir = (const char *)rep.extra_data.data; + + torture_assert(torture, got_dir, "NULL WINBINDD_PRIV_PIPE_DIR\n"); + + SAFE_FREE(rep.extra_data.data); + return true; +} + +static bool torture_winbind_struct_netbios_name(struct torture_context *torture) +{ + struct winbindd_response rep; + const char *expected; + + ZERO_STRUCT(rep); + + torture_comment(torture, "Running WINBINDD_NETBIOS_NAME (struct based)\n"); + + DO_STRUCT_REQ_REP(WINBINDD_NETBIOS_NAME, NULL, &rep); + + expected = torture_setting_string(torture, + "winbindd_netbios_name", + lpcfg_netbios_name(torture->lp_ctx)); + expected = strupper_talloc(torture, expected); + + torture_assert_str_equal(torture, + rep.data.netbios_name, expected, + "winbindd's netbios name doesn't match"); + + return true; +} + +static bool get_winbind_domain(struct torture_context *torture, char **domain) +{ + struct winbindd_response rep; + + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_DOMAIN_NAME, NULL, &rep); + + *domain = talloc_strdup(torture, rep.data.domain_name); + torture_assert(torture, domain, "talloc error"); + + return true; +} + +static bool torture_winbind_struct_domain_name(struct torture_context *torture) +{ + const char *expected; + char *domain; + + torture_comment(torture, "Running WINBINDD_DOMAIN_NAME (struct based)\n"); + + expected = torture_setting_string(torture, + "winbindd_netbios_domain", + lpcfg_workgroup(torture->lp_ctx)); + + get_winbind_domain(torture, &domain); + + torture_assert_str_equal(torture, domain, expected, + "winbindd's netbios domain doesn't match"); + + return true; +} + +static bool torture_winbind_struct_check_machacc(struct torture_context *torture) +{ + bool ok; + bool strict = torture_setting_bool(torture, "strict mode", false); + struct winbindd_response rep; + + ZERO_STRUCT(rep); + + torture_comment(torture, "Running WINBINDD_CHECK_MACHACC (struct based)\n"); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_CHECK_MACHACC, NULL, &rep, + NSS_STATUS_SUCCESS, strict, ok = false, + "WINBINDD_CHECK_MACHACC"); + + if (!ok) { + torture_assert(torture, + strlen(rep.data.auth.nt_status_string)>0, + "Failed with empty nt_status_string"); + + torture_warning(torture,"%s:%s:%s:%d\n", + nt_errstr(NT_STATUS(rep.data.auth.nt_status)), + rep.data.auth.nt_status_string, + rep.data.auth.error_string, + rep.data.auth.pam_error); + return true; + } + + torture_assert_ntstatus_ok(torture, + NT_STATUS(rep.data.auth.nt_status), + "WINBINDD_CHECK_MACHACC ok: nt_status"); + + torture_assert_str_equal(torture, + rep.data.auth.nt_status_string, + nt_errstr(NT_STATUS_OK), + "WINBINDD_CHECK_MACHACC ok:nt_status_string"); + + torture_assert_str_equal(torture, + rep.data.auth.error_string, + get_friendly_nt_error_msg(NT_STATUS_OK), + "WINBINDD_CHECK_MACHACC ok: error_string"); + + torture_assert_int_equal(torture, + rep.data.auth.pam_error, + nt_status_to_pam(NT_STATUS_OK), + "WINBINDD_CHECK_MACHACC ok: pam_error"); + + return true; +} + +struct torture_trust_domain { + const char *netbios_name; + const char *dns_name; + struct dom_sid *sid; +}; + +static bool get_trusted_domains(struct torture_context *torture, + struct torture_trust_domain **_d) +{ + struct winbindd_request req; + struct winbindd_response rep; + struct torture_trust_domain *d = NULL; + uint32_t dcount = 0; + char line[256]; + const char *extra_data; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_LIST_TRUSTDOM, &req, &rep); + + extra_data = (char *)rep.extra_data.data; + torture_assert(torture, extra_data != NULL, + "Trust list was NULL: the list of trusted domain " + "should be returned, with at least 2 entries " + "(BUILTIN, and the local domain)"); + + while (next_token(&extra_data, line, "\n", sizeof(line))) { + char *p, *lp; + + d = talloc_realloc(torture, d, + struct torture_trust_domain, + dcount + 2); + ZERO_STRUCT(d[dcount+1]); + + lp = line; + p = strchr(lp, '\\'); + torture_assert(torture, p, "missing 1st '\\' in line"); + *p = 0; + d[dcount].netbios_name = talloc_strdup(d, lp); + torture_assert(torture, strlen(d[dcount].netbios_name) > 0, + "empty netbios_name"); + + lp = p+1; + p = strchr(lp, '\\'); + torture_assert(torture, p, "missing 2nd '\\' in line"); + *p = 0; + d[dcount].dns_name = talloc_strdup(d, lp); + /* it's ok to have an empty dns_name */ + + lp = p+1; + d[dcount].sid = dom_sid_parse_talloc(d, lp); + torture_assert(torture, d[dcount].sid, + "failed to parse sid"); + + dcount++; + } + SAFE_FREE(rep.extra_data.data); + + torture_assert(torture, dcount >= 2, + "The list of trusted domain should contain 2 entries " + "(BUILTIN, and the local domain)"); + + *_d = d; + return true; +} + +static bool torture_winbind_struct_list_trustdom(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + char *list1; + char *list2; + bool ok; + struct torture_trust_domain *listd = NULL; + uint32_t i; + + torture_comment(torture, "Running WINBINDD_LIST_TRUSTDOM (struct based)\n"); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + req.data.list_all_domains = false; + + DO_STRUCT_REQ_REP(WINBINDD_LIST_TRUSTDOM, &req, &rep); + + list1 = (char *)rep.extra_data.data; + + torture_comment(torture, "%s\n", list1); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + req.data.list_all_domains = true; + + DO_STRUCT_REQ_REP(WINBINDD_LIST_TRUSTDOM, &req, &rep); + + list2 = (char *)rep.extra_data.data; + + /* + * The list_all_domains parameter should be ignored + */ + torture_assert_str_equal(torture, list2, list1, "list_all_domains not ignored"); + + SAFE_FREE(list1); + SAFE_FREE(list2); + + ok = get_trusted_domains(torture, &listd); + torture_assert(torture, ok, "failed to get trust list"); + + for (i=0; listd && listd[i].netbios_name; i++) { + if (i == 0) { + struct dom_sid *builtin_sid; + + builtin_sid = dom_sid_parse_talloc(torture, SID_BUILTIN); + + torture_assert_str_equal(torture, + listd[i].netbios_name, + NAME_BUILTIN, + "first domain should be 'BUILTIN'"); + + torture_assert_str_equal(torture, + listd[i].dns_name, + "", + "BUILTIN domain should not have a dns name"); + + ok = dom_sid_equal(builtin_sid, + listd[i].sid); + torture_assert(torture, ok, "BUILTIN domain should have S-1-5-32"); + + continue; + } + + /* + * TODO: verify the content of the 2nd and 3rd (in member server mode) + * domain entries + */ + } + + return true; +} + +static bool torture_winbind_struct_domain_info(struct torture_context *torture) +{ + bool ok; + struct torture_trust_domain *listd = NULL; + uint32_t i; + + torture_comment(torture, "Running WINBINDD_DOMAIN_INFO (struct based)\n"); + + ok = get_trusted_domains(torture, &listd); + torture_assert(torture, ok, "failed to get trust list"); + + for (i=0; listd && listd[i].netbios_name; i++) { + torture_comment(torture, "LIST[%u] '%s' => '%s' [%s]\n", + (unsigned)i, + listd[i].netbios_name, + listd[i].dns_name, + dom_sid_string(torture, listd[i].sid)); + } + + for (i=0; listd && listd[i].netbios_name; i++) { + struct winbindd_request req; + struct winbindd_response rep; + struct dom_sid *sid; + char *flagstr = talloc_strdup(torture," "); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + fstrcpy(req.domain_name, listd[i].netbios_name); + + DO_STRUCT_REQ_REP(WINBINDD_DOMAIN_INFO, &req, &rep); + + if (rep.data.domain_info.primary) { + flagstr = talloc_strdup_append(flagstr, "PR "); + } + + if (rep.data.domain_info.active_directory) { + torture_assert(torture, + strlen(rep.data.domain_info.alt_name)>0, + "Active Directory without DNS name"); + flagstr = talloc_strdup_append(flagstr, "AD "); + } + + if (rep.data.domain_info.native_mode) { + torture_assert(torture, + rep.data.domain_info.active_directory, + "Native-Mode, but no Active Directory"); + flagstr = talloc_strdup_append(flagstr, "NA "); + } + + torture_comment(torture, "DOMAIN[%u] '%s' => '%s' [%s] [%s]\n", + (unsigned)i, + rep.data.domain_info.name, + rep.data.domain_info.alt_name, + flagstr, + rep.data.domain_info.sid); + + sid = dom_sid_parse_talloc(torture, rep.data.domain_info.sid); + torture_assert(torture, sid, "Failed to parse SID"); + + ok = dom_sid_equal(listd[i].sid, sid); + torture_assert(torture, ok, talloc_asprintf(torture, "SID's doesn't match [%s] != [%s]", + dom_sid_string(torture, listd[i].sid), + dom_sid_string(torture, sid))); + + torture_assert_str_equal(torture, + rep.data.domain_info.name, + listd[i].netbios_name, + "Netbios domain name doesn't match"); + + torture_assert_str_equal(torture, + rep.data.domain_info.alt_name, + listd[i].dns_name, + "DNS domain name doesn't match"); + } + + return true; +} + +static bool torture_winbind_struct_getdcname(struct torture_context *torture) +{ + bool ok; + bool strict = torture_setting_bool(torture, "strict mode", false); + const char *domain_name = torture_setting_string(torture, + "winbindd_netbios_domain", + lpcfg_workgroup(torture->lp_ctx)); + struct torture_trust_domain *listd = NULL; + uint32_t i, count = 0; + + torture_comment(torture, "Running WINBINDD_GETDCNAME (struct based)\n"); + + ok = get_trusted_domains(torture, &listd); + torture_assert(torture, ok, "failed to get trust list"); + + for (i=0; listd && listd[i].netbios_name; i++) { + struct winbindd_request req; + struct winbindd_response rep; + + /* getdcname is not expected to work on "BUILTIN" or our own + * domain */ + if (strequal(listd[i].netbios_name, "BUILTIN") || + strequal(listd[i].netbios_name, domain_name)) { + continue; + } + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + fstrcpy(req.domain_name, listd[i].netbios_name); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_GETDCNAME, &req, &rep, + NSS_STATUS_SUCCESS, + (i <2 || strict), ok = false, + talloc_asprintf(torture, "DOMAIN '%s'", + req.domain_name)); + if (!ok) continue; + + /* TODO: check rep.data.dc_name; */ + torture_comment(torture, "DOMAIN '%s' => DCNAME '%s'\n", + req.domain_name, rep.data.dc_name); + count++; + } + + if (strict) { + torture_assert(torture, count > 0, + "WiNBINDD_GETDCNAME was not tested"); + } + return true; +} + +static bool torture_winbind_struct_dsgetdcname(struct torture_context *torture) +{ + bool ok; + bool strict = torture_setting_bool(torture, "strict mode", false); + struct torture_trust_domain *listd = NULL; + uint32_t i; + uint32_t count = 0; + + torture_comment(torture, "Running WINBINDD_DSGETDCNAME (struct based)\n"); + + ok = get_trusted_domains(torture, &listd); + torture_assert(torture, ok, "failed to get trust list"); + + for (i=0; listd && listd[i].netbios_name; i++) { + struct winbindd_request req; + struct winbindd_response rep; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + if (strlen(listd[i].dns_name) == 0) continue; + + /* + * TODO: remove this and let winbindd give no dns name + * for NT4 domains + */ + if (strcmp(listd[i].dns_name, listd[i].netbios_name) == 0) { + continue; + } + + fstrcpy(req.domain_name, listd[i].dns_name); + + /* TODO: test more flag combinations */ + req.flags = DS_DIRECTORY_SERVICE_REQUIRED; + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_DSGETDCNAME, &req, &rep, + NSS_STATUS_SUCCESS, + strict, ok = false, + talloc_asprintf(torture, "DOMAIN '%s'", + req.domain_name)); + if (!ok) continue; + + /* TODO: check rep.data.dc_name; */ + torture_comment(torture, "DOMAIN '%s' => DCNAME '%s'\n", + req.domain_name, rep.data.dc_name); + + count++; + } + + if (count == 0) { + torture_warning(torture, "WINBINDD_DSGETDCNAME" + " was not tested with %d non-AD domains", + i); + } + + if (strict) { + torture_assert(torture, count > 0, + "WiNBINDD_DSGETDCNAME was not tested"); + } + + return true; +} + +static bool get_user_list(struct torture_context *torture, char ***users) +{ + struct winbindd_request req; + struct winbindd_response rep; + char **u = NULL; + uint32_t count; + char name[256]; + const char *extra_data; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_LIST_USERS, &req, &rep); + + extra_data = (char *)rep.extra_data.data; + torture_assert(torture, extra_data, "NULL extra data"); + + for(count = 0; + next_token(&extra_data, name, ",", sizeof(name)); + count++) + { + u = talloc_realloc(torture, u, char *, count + 2); + u[count+1] = NULL; + u[count] = talloc_strdup(u, name); + } + + SAFE_FREE(rep.extra_data.data); + + *users = u; + return true; +} + +static bool torture_winbind_struct_list_users(struct torture_context *torture) +{ + char **users; + uint32_t count; + bool ok; + + torture_comment(torture, "Running WINBINDD_LIST_USERS (struct based)\n"); + + ok = get_user_list(torture, &users); + torture_assert(torture, ok, "failed to get user list"); + + for (count = 0; users[count]; count++) { } + + torture_comment(torture, "got %d users\n", count); + + return true; +} + +static bool get_group_list(struct torture_context *torture, + unsigned int *num_entries, + char ***groups) +{ + struct winbindd_request req; + struct winbindd_response rep; + char **g = NULL; + uint32_t count; + char name[256]; + const char *extra_data; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_LIST_GROUPS, &req, &rep); + extra_data = (char *)rep.extra_data.data; + + *num_entries = rep.data.num_entries; + + if (*num_entries == 0) { + torture_assert(torture, extra_data == NULL, + "extra data is null for >0 reported entries\n"); + *groups = NULL; + return true; + } + + torture_assert(torture, extra_data, "NULL extra data"); + + for(count = 0; + next_token(&extra_data, name, ",", sizeof(name)); + count++) + { + g = talloc_realloc(torture, g, char *, count + 2); + g[count+1] = NULL; + g[count] = talloc_strdup(g, name); + } + + SAFE_FREE(rep.extra_data.data); + + torture_assert_int_equal(torture, *num_entries, count, + "Wrong number of group entries reported."); + + *groups = g; + return true; +} + +static bool torture_winbind_struct_list_groups(struct torture_context *torture) +{ + char **groups; + uint32_t count; + bool ok; + + torture_comment(torture, "Running WINBINDD_LIST_GROUPS (struct based)\n"); + + ok = get_group_list(torture, &count, &groups); + torture_assert(torture, ok, "failed to get group list"); + + torture_comment(torture, "got %d groups\n", count); + + return true; +} + +struct torture_domain_sequence { + const char *netbios_name; + uint32_t seq; +}; + +static bool get_sequence_numbers(struct torture_context *torture, + struct torture_domain_sequence **seqs) +{ + struct winbindd_request req; + struct winbindd_response rep; + const char *extra_data; + char line[256]; + uint32_t count = 0; + struct torture_domain_sequence *s = NULL; + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_SHOW_SEQUENCE, &req, &rep); + + extra_data = (char *)rep.extra_data.data; + torture_assert(torture, extra_data, "NULL sequence list"); + + while (next_token(&extra_data, line, "\n", sizeof(line))) { + char *p, *lp; + uint32_t seq; + + s = talloc_realloc(torture, s, struct torture_domain_sequence, + count + 2); + ZERO_STRUCT(s[count+1]); + + lp = line; + p = strchr(lp, ' '); + torture_assert(torture, p, "invalid line format"); + *p = 0; + s[count].netbios_name = talloc_strdup(s, lp); + + lp = p+1; + torture_assert(torture, strncmp(lp, ": ", 2) == 0, + "invalid line format"); + lp += 2; + if (strcmp(lp, "DISCONNECTED") == 0) { + seq = (uint32_t)-1; + } else { + seq = (uint32_t)strtol(lp, &p, 10); + torture_assert(torture, (*p == '\0'), + "invalid line format"); + torture_assert(torture, (seq != (uint32_t)-1), + "sequence number -1 encountered"); + } + s[count].seq = seq; + + count++; + } + SAFE_FREE(rep.extra_data.data); + + torture_assert(torture, count >= 2, "The list of domain sequence " + "numbers should contain 2 entries"); + + *seqs = s; + return true; +} + +static bool torture_winbind_struct_show_sequence(struct torture_context *torture) +{ + bool ok; + uint32_t i; + struct torture_trust_domain *domlist = NULL; + struct torture_domain_sequence *s = NULL; + + torture_comment(torture, "Running WINBINDD_SHOW_SEQUENCE (struct based)\n"); + + ok = get_sequence_numbers(torture, &s); + torture_assert(torture, ok, "failed to get list of sequence numbers"); + + ok = get_trusted_domains(torture, &domlist); + torture_assert(torture, ok, "failed to get trust list"); + + for (i=0; domlist[i].netbios_name; i++) { + struct winbindd_request req; + struct winbindd_response rep; + uint32_t seq; + + torture_assert(torture, s[i].netbios_name, + "more domains received in second run"); + torture_assert_str_equal(torture, domlist[i].netbios_name, + s[i].netbios_name, + "inconsistent order of domain lists"); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + fstrcpy(req.domain_name, domlist[i].netbios_name); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_SHOW_SEQUENCE, &req, &rep, + NSS_STATUS_SUCCESS, + false, ok = false, + "WINBINDD_SHOW_SEQUENCE"); + if (ok == false) { + torture_warning(torture, + "WINBINDD_SHOW_SEQUENCE on " + "domain %s failed\n", + req.domain_name); + + /* + * Only fail for the first two domain that we + * check specially below, otherwise we fail on + * trusts generated by the LSA torture test + * that do not really exist. + */ + if (i > 1) { + /* + * Do not confirm the sequence numbers + * below + */ + return true; + } + + torture_comment(torture, + "Full trust list for " + "WINBINDD_SHOW_SEQUENCE " + "test was:\n"); + for (i=0; domlist[i].netbios_name; i++) { + torture_comment(torture, + "%s\n", + domlist[i].netbios_name); + } + + return false; + } + + seq = rep.data.sequence_number; + + if (i == 0) { + torture_assert(torture, (seq != (uint32_t)-1), + "BUILTIN domain disconnected"); + } else if (i == 1) { + torture_assert(torture, (seq != (uint32_t)-1), + "local domain disconnected"); + } + + + if (seq == (uint32_t)-1) { + torture_comment(torture, " * %s : DISCONNECTED\n", + req.domain_name); + } else { + torture_comment(torture, " * %s : %d\n", + req.domain_name, seq); + } + torture_assert(torture, (seq >= s[i].seq), + "illegal sequence number encountered"); + } + + return true; +} + +static bool torture_winbind_struct_setpwent(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + + torture_comment(torture, "Running WINBINDD_SETPWENT (struct based)\n"); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_SETPWENT, &req, &rep); + + return true; +} + +static bool torture_winbind_struct_getpwent(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + struct winbindd_pw *pwent; + + torture_comment(torture, "Running WINBINDD_GETPWENT (struct based)\n"); + + torture_comment(torture, " - Running WINBINDD_SETPWENT first\n"); + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + DO_STRUCT_REQ_REP(WINBINDD_SETPWENT, &req, &rep); + + torture_comment(torture, " - Running WINBINDD_GETPWENT now\n"); + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + req.data.num_entries = 1; + if (torture_setting_bool(torture, "samba3", false)) { + DO_STRUCT_REQ_REP_EXT(WINBINDD_GETPWENT, &req, &rep, + NSS_STATUS_SUCCESS, false, _STRUCT_NOOP, + NULL); + } else { + DO_STRUCT_REQ_REP(WINBINDD_GETPWENT, &req, &rep); + } + pwent = (struct winbindd_pw *)rep.extra_data.data; + if (!torture_setting_bool(torture, "samba3", false)) { + torture_assert(torture, (pwent != NULL), "NULL pwent"); + } + if (pwent) { + torture_comment(torture, "name: %s, uid: %d, gid: %d, shell: %s\n", + pwent->pw_name, pwent->pw_uid, pwent->pw_gid, + pwent->pw_shell); + } + + return true; +} + +static bool torture_winbind_struct_endpwent(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + + torture_comment(torture, "Running WINBINDD_ENDPWENT (struct based)\n"); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + DO_STRUCT_REQ_REP(WINBINDD_ENDPWENT, &req, &rep); + + return true; +} + +/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the + form DOMAIN/user into a domain and a user */ + +static bool parse_domain_user(struct torture_context *torture, + const char *domuser, fstring domain, + fstring user) +{ + char *p = strchr(domuser, winbind_separator(torture)); + char *dom = NULL; + + if (!p) { + /* Maybe it was a UPN? */ + if ((p = strchr(domuser, '@')) != NULL) { + fstrcpy(domain, ""); + fstrcpy(user, domuser); + return true; + } + + fstrcpy(user, domuser); + get_winbind_domain(torture, &dom); + fstrcpy(domain, dom); + return true; + } + + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + + return true; +} + +static bool lookup_name_sid_list(struct torture_context *torture, char **list) +{ + uint32_t count; + + for (count = 0; list[count]; count++) { + struct winbindd_request req; + struct winbindd_response rep; + char *sid; + char *name; + const char *domain_name = torture_setting_string(torture, + "winbindd_domain_without_prefix", + NULL); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + parse_domain_user(torture, list[count], req.data.name.dom_name, + req.data.name.name); + + DO_STRUCT_REQ_REP(WINBINDD_LOOKUPNAME, &req, &rep); + + sid = talloc_strdup(torture, rep.data.sid.sid); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + fstrcpy(req.data.sid, sid); + + DO_STRUCT_REQ_REP(WINBINDD_LOOKUPSID, &req, &rep); + + if (domain_name != NULL && + strequal(rep.data.name.dom_name, domain_name)) + { + name = talloc_asprintf(torture, "%s", + rep.data.name.name); + } else { + name = talloc_asprintf(torture, "%s%c%s", + rep.data.name.dom_name, + winbind_separator(torture), + rep.data.name.name); + } + + torture_assert_casestr_equal(torture, list[count], name, + "LOOKUP_SID after LOOKUP_NAME != id"); + +#if 0 + torture_comment(torture, " %s -> %s -> %s\n", list[count], + sid, name); +#endif + + talloc_free(sid); + talloc_free(name); + } + + return true; +} + +static bool name_is_in_list(const char *name, char **list) +{ + uint32_t count; + + for (count = 0; list && list[count]; count++) { + if (strequal(name, list[count])) { + return true; + } + } + return false; +} + +static bool torture_winbind_struct_lookup_name_sid(struct torture_context *torture) +{ + struct winbindd_request req; + struct winbindd_response rep; + const char *invalid_sid = "S-0-0-7"; + char *domain = NULL; + const char *invalid_user = "no one"; + char *invalid_name; + bool strict = torture_setting_bool(torture, "strict mode", false); + char **users; + char **groups; + uint32_t count, num_groups; + bool ok; + + torture_comment(torture, "Running WINBINDD_LOOKUP_NAME_SID (struct based)\n"); + + ok = get_user_list(torture, &users); + torture_assert(torture, ok, "failed to retrieve list of users"); + lookup_name_sid_list(torture, users); + + ok = get_group_list(torture, &num_groups, &groups); + torture_assert(torture, ok, "failed to retrieve list of groups"); + if (num_groups > 0) { + lookup_name_sid_list(torture, groups); + } + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + fstrcpy(req.data.sid, invalid_sid); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_LOOKUPSID, &req, &rep, + NSS_STATUS_NOTFOUND, + strict, + ok=false, + talloc_asprintf(torture, + "invalid sid %s was resolved", + invalid_sid)); + + ZERO_STRUCT(req); + ZERO_STRUCT(rep); + + /* try to find an invalid name... */ + + count = 0; + get_winbind_domain(torture, &domain); + do { + count++; + invalid_name = talloc_asprintf(torture, "%s/%s%u", + domain, + invalid_user, count); + } while(name_is_in_list(invalid_name, users) || + name_is_in_list(invalid_name, groups)); + + fstrcpy(req.data.name.dom_name, domain); + fstrcpy(req.data.name.name, + talloc_asprintf(torture, "%s%u", invalid_user, + count)); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_LOOKUPNAME, &req, &rep, + NSS_STATUS_NOTFOUND, + strict, + ok=false, + talloc_asprintf(torture, + "invalid name %s was resolved", + invalid_name)); + + talloc_free(users); + talloc_free(groups); + + return true; +} + +static bool torture_winbind_struct_lookup_sids_invalid( + struct torture_context *torture) +{ + struct winbindd_request req = {0}; + struct winbindd_response rep = {0}; + bool strict = torture_setting_bool(torture, "strict mode", false); + bool ok; + + torture_comment(torture, + "Running WINBINDD_LOOKUP_SIDS (struct based)\n"); + + ok = true; + DO_STRUCT_REQ_REP_EXT(WINBINDD_LOOKUPSIDS, &req, &rep, + NSS_STATUS_NOTFOUND, + strict, + ok=false, + talloc_asprintf( + torture, + "invalid lookupsids succeeded")); + + return ok; +} + +struct torture_suite *torture_winbind_struct_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "struct"); + + torture_suite_add_simple_test(suite, "interface_version", torture_winbind_struct_interface_version); + torture_suite_add_simple_test(suite, "ping", torture_winbind_struct_ping); + torture_suite_add_simple_test(suite, "info", torture_winbind_struct_info); + torture_suite_add_simple_test(suite, "priv_pipe_dir", torture_winbind_struct_priv_pipe_dir); + torture_suite_add_simple_test(suite, "netbios_name", torture_winbind_struct_netbios_name); + torture_suite_add_simple_test(suite, "domain_name", torture_winbind_struct_domain_name); + torture_suite_add_simple_test(suite, "check_machacc", torture_winbind_struct_check_machacc); + torture_suite_add_simple_test(suite, "list_trustdom", torture_winbind_struct_list_trustdom); + torture_suite_add_simple_test(suite, "domain_info", torture_winbind_struct_domain_info); + torture_suite_add_simple_test(suite, "getdcname", torture_winbind_struct_getdcname); + torture_suite_add_simple_test(suite, "dsgetdcname", torture_winbind_struct_dsgetdcname); + torture_suite_add_simple_test(suite, "list_users", torture_winbind_struct_list_users); + torture_suite_add_simple_test(suite, "list_groups", torture_winbind_struct_list_groups); + torture_suite_add_simple_test(suite, "show_sequence", torture_winbind_struct_show_sequence); + torture_suite_add_simple_test(suite, "setpwent", torture_winbind_struct_setpwent); + torture_suite_add_simple_test(suite, "getpwent", torture_winbind_struct_getpwent); + torture_suite_add_simple_test(suite, "endpwent", torture_winbind_struct_endpwent); + torture_suite_add_simple_test(suite, "lookup_name_sid", torture_winbind_struct_lookup_name_sid); + torture_suite_add_simple_test( + suite, + "lookup_sids_invalid", + torture_winbind_struct_lookup_sids_invalid); + + suite->description = talloc_strdup(suite, "WINBIND - struct based protocol tests"); + + return suite; +} diff --git a/source4/torture/winbind/winbind.c b/source4/torture/winbind/winbind.c new file mode 100644 index 0000000..4acfd38 --- /dev/null +++ b/source4/torture/winbind/winbind.c @@ -0,0 +1,335 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Stefan Metzmacher 2007 + Copyright (C) Andrew Bartlett 2012 + Copyright (C) Christof Schmit 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/smbtorture.h" +#include "torture/winbind/proto.h" +#include "auth/auth.h" +#include "auth/auth_sam_reply.h" +#include "auth/gensec/gensec.h" +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" +#include "lib/cmdline/cmdline.h" +#include "auth/kerberos/pac_utils.h" +#include "wbclient.h" + +struct pac_data { + DATA_BLOB pac_blob; +}; + +/* A helper function which avoids touching the local databases to + * generate the session info, as we just want to verify the PAC + * details, not the full local token */ +static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *principal_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct auth_user_info_dc *user_info_dc; + TALLOC_CTX *tmp_ctx; + struct pac_data *pac_data; + + if (pac_blob == NULL) { + DBG_ERR("pac_blob missing\n"); + return NT_STATUS_NO_IMPERSONATION_TOKEN; + } + + tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data); + + pac_data->pac_blob = *pac_blob; + + talloc_steal(pac_data, pac_data->pac_blob.data); + nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx, + *pac_blob, + smb_krb5_context->krb5_context, + &user_info_dc, + NULL, NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + if (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + nt_status = auth_generate_session_info(mem_ctx, + NULL, + NULL, + user_info_dc, session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + talloc_free(tmp_ctx); + return nt_status; +} + +static bool torture_decode_compare_pac(struct torture_context *tctx, + DATA_BLOB pac) +{ + struct wbcAuthUserParams params; + struct wbcAuthUserInfo *info; + struct wbcAuthErrorInfo *error; + struct PAC_LOGON_INFO *logon_info; + struct netr_SamInfo3 *info3; + struct netr_SamBaseInfo *base; + struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups = NULL; + wbcErr wbc_err; + NTSTATUS status; + int sid_idx, i; + char sid_str[50]; + + /* Let winbind decode the PAC */ + memset(¶ms, 0, sizeof(params)); + params.level = WBC_AUTH_USER_LEVEL_PAC; + params.password.pac.data = pac.data; + params.password.pac.length = pac.length; + + wbc_err = wbcAuthenticateUserEx(¶ms, &info, &error); + torture_assert(tctx, WBC_ERROR_IS_OK(wbc_err), wbcErrorString(wbc_err)); + + /* Decode the PAC internally */ + status = kerberos_pac_logon_info(tctx, pac, NULL, NULL, NULL, NULL, 0, + &logon_info); + torture_assert(tctx, NT_STATUS_IS_OK(status), "pac_logon_info"); + info3 = &logon_info->info3; + base = &info3->base; + resource_groups = &logon_info->resource_groups; + + /* Compare the decoded data from winbind and from internal call */ + torture_assert(tctx, info->user_flags == base->user_flags, "user_flags"); + torture_assert_str_equal(tctx, info->account_name, base->account_name.string, "account_name"); + torture_assert_str_equal(tctx, info->full_name, base->full_name.string, "full_name"); + torture_assert_str_equal(tctx, info->domain_name, base->logon_domain.string, "domain_name"); + torture_assert(tctx, info->acct_flags == base->acct_flags, "acct_flags"); + torture_assert(tctx, info->logon_count == base->logon_count, "logon_count"); + torture_assert(tctx, info->bad_password_count == base->bad_password_count, "bad_password_count"); + torture_assert(tctx, info->logon_time == nt_time_to_unix(base->logon_time), "logon_time"); + torture_assert(tctx, info->logoff_time == nt_time_to_unix(base->logoff_time), "logoff_time"); + torture_assert(tctx, info->kickoff_time == nt_time_to_unix(base->kickoff_time), "kickoff_time"); + torture_assert(tctx, info->pass_last_set_time == nt_time_to_unix(base->last_password_change), "last_password_change"); + torture_assert(tctx, info->pass_can_change_time == nt_time_to_unix(base->allow_password_change), "allow_password_change"); + torture_assert(tctx, info->pass_must_change_time == nt_time_to_unix(base->force_password_change), "force_password_change"); + torture_assert(tctx, info->num_sids == 2 + base->groups.count + info3->sidcount + resource_groups->groups.count, "num_sids"); + + sid_idx = 0; + wbcSidToStringBuf(&info->sids[sid_idx].sid, sid_str, sizeof(sid_str)); + torture_assert(tctx, + dom_sid_equal(dom_sid_parse_talloc(tctx, sid_str), + dom_sid_add_rid(tctx, base->domain_sid, base->rid)), + sid_str); + + sid_idx++; + wbcSidToStringBuf(&info->sids[sid_idx].sid, sid_str, sizeof(sid_str)); + torture_assert(tctx, + dom_sid_equal(dom_sid_parse_talloc(tctx, sid_str), + dom_sid_add_rid(tctx, base->domain_sid, base->primary_gid)), + sid_str); + + for(i = 0; i < base->groups.count; i++ ) { + sid_idx++; + wbcSidToStringBuf(&info->sids[sid_idx].sid, + sid_str, sizeof(sid_str)); + torture_assert_sid_equal(tctx, + dom_sid_parse_talloc(tctx, sid_str), + dom_sid_add_rid(tctx, base->domain_sid, + base->groups.rids[i].rid), + "base SID mismatch"); + } + + for(i = 0; i < info3->sidcount; i++) { + sid_idx++; + wbcSidToStringBuf(&info->sids[sid_idx].sid, + sid_str, sizeof(sid_str)); + torture_assert_sid_equal(tctx, + dom_sid_parse_talloc(tctx, sid_str), + info3->sids[i].sid, + "extra SID mismatch"); + } + + for (i = 0; i < resource_groups->groups.count; i++) { + sid_idx++; + wbcSidToStringBuf(&info->sids[sid_idx].sid, + sid_str, sizeof(sid_str)); + torture_assert_sid_equal(tctx, + dom_sid_parse_talloc(tctx, sid_str), + dom_sid_add_rid(tctx, resource_groups->domain_sid, + resource_groups->groups.rids[i].rid), + "resource SID mismatch"); + } + + sid_idx++; + torture_assert_int_equal(tctx, sid_idx, info->num_sids, "some SIDs still unaccounted for"); + + return true; +} + +static bool torture_winbind_pac(struct torture_context *tctx, + const char *sasl_mech, + const char *mech) +{ + NTSTATUS status; + + struct gensec_security *gensec_client_context; + struct gensec_security *gensec_server_context; + struct cli_credentials *machine_credentials; + + DATA_BLOB client_to_server, server_to_client; + + struct auth4_context *auth_context; + struct auth_session_info *session_info; + struct pac_data *pac_data; + + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + machine_credentials = cli_credentials_init_server(tmp_ctx, + tctx->lp_ctx); + torture_assert(tctx, machine_credentials != NULL, "cli_credentials_init() failed"); + + auth_context = talloc_zero(tmp_ctx, struct auth4_context); + torture_assert(tctx, auth_context != NULL, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + status = gensec_set_target_hostname(gensec_client_context, cli_credentials_get_workstation(machine_credentials)); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + + status = gensec_set_credentials(gensec_client_context, + samba_cmdline_get_creds()); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + if (sasl_mech) { + status = gensec_start_mech_by_sasl_name(gensec_client_context, sasl_mech); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + } else { + status = gensec_start_mech_by_name(gensec_client_context, mech); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (client) failed"); + } + + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, machine_credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + if (sasl_mech) { + status = gensec_start_mech_by_sasl_name(gensec_server_context, sasl_mech); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); + } else { + status = gensec_start_mech_by_name(gensec_server_context, mech); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed"); + } + + server_to_client = data_blob(NULL, 0); + + do { + /* Do a client-server update dance */ + status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + } + + if (NT_STATUS_IS_OK(status)) { + break; + } + } while (1); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_data = talloc_get_type(auth_context->private_data, struct pac_data); + + torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context"); + torture_assert(tctx, pac_data->pac_blob.data != NULL, "pac_blob not present"); + torture_decode_compare_pac(tctx, pac_data->pac_blob); + + return true; +} + +static bool torture_winbind_pac_gssapi(struct torture_context *tctx) +{ + return torture_winbind_pac(tctx, "GSSAPI", NULL); +} + +static bool torture_winbind_pac_gss_spnego(struct torture_context *tctx) +{ + return torture_winbind_pac(tctx, "GSS-SPNEGO", NULL); +} + +static bool torture_winbind_pac_krb5(struct torture_context *tctx) +{ + return torture_winbind_pac(tctx, NULL, "krb5"); +} + +NTSTATUS torture_winbind_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "winbind"); + struct torture_suite *pac_suite; + torture_suite_add_suite(suite, torture_winbind_struct_init(suite)); + torture_suite_add_suite(suite, torture_wbclient(suite)); + + pac_suite = torture_suite_create(ctx, "pac"); + torture_suite_add_simple_test(pac_suite, + "GSSAPI", torture_winbind_pac_gssapi); + torture_suite_add_simple_test(pac_suite, + "GSS-SPNEGO", torture_winbind_pac_gss_spnego); + torture_suite_add_simple_test(pac_suite, + "krb5", torture_winbind_pac_krb5); + + pac_suite->description = talloc_strdup(suite, "Winbind Kerberos PAC tests"); + + torture_suite_add_suite(suite, pac_suite); + + suite->description = talloc_strdup(suite, "WINBIND tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/winbind/wscript_build b/source4/torture/winbind/wscript_build new file mode 100644 index 0000000..07b0b2b --- /dev/null +++ b/source4/torture/winbind/wscript_build @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +bld.SAMBA_MODULE('TORTURE_WINBIND', + source='winbind.c struct_based.c ../../../nsswitch/libwbclient/tests/wbclient.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_winbind_init', + deps='popt wbclient torture PAM_ERRORS winbindd-lib', + internal_module=True + ) diff --git a/source4/torture/wscript_build b/source4/torture/wscript_build new file mode 100644 index 0000000..1ddce71 --- /dev/null +++ b/source4/torture/wscript_build @@ -0,0 +1,361 @@ +#!/usr/bin/env python + +provision = bld.pyembed_libname('PROVISION') +samba_net = bld.pyembed_libname('samba-net') + +bld.SAMBA_SUBSYSTEM('TORTURE_UTIL', + source='util_smb.c', + public_deps='torture', + deps='smbclient-raw CMDLINE_S4 CREDENTIALS_KRB5' + ) + + +bld.SAMBA_MODULE('TORTURE_BASIC', + source='basic/base.c basic/misc.c basic/scanner.c basic/utable.c basic/charset.c basic/mangle_test.c basic/denytest.c basic/aliases.c basic/locking.c basic/secleak.c basic/rename.c basic/dir.c basic/delete.c basic/unlink.c basic/disconnect.c basic/delaywrite.c basic/attr.c basic/properties.c', + subsystem='smbtorture', + deps='LIBCLI_SMB TORTURE_UTIL smbclient-raw TORTURE_RAW', + internal_module=True, + autoproto='basic/proto.h', + init_function='torture_base_init', + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + + +bld.SAMBA_MODULE('TORTURE_RAW', + source='raw/qfsinfo.c raw/qfileinfo.c raw/setfileinfo.c raw/search.c raw/close.c raw/open.c raw/mkdir.c raw/oplock.c raw/notify.c raw/mux.c raw/ioctl.c raw/chkpath.c raw/unlink.c raw/read.c raw/context.c raw/session.c raw/write.c raw/lock.c raw/pingpong.c raw/lockbench.c raw/lookuprate.c raw/tconrate.c raw/openbench.c raw/rename.c raw/eas.c raw/streams.c raw/acls.c raw/seek.c raw/samba3hide.c raw/samba3misc.c raw/composite.c raw/raw.c raw/offline.c', + autoproto='raw/proto.h', + subsystem='smbtorture', + init_function='torture_raw_init', + deps='LIBCLI_SMB LIBCLI_LSA LIBCLI_SMB_COMPOSITE TORTURE_UTIL', + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.RECURSE('smb2') +bld.RECURSE('winbind') +bld.RECURSE('libnetapi') +bld.RECURSE('libsmbclient') +bld.RECURSE('gpo') + +ntvfs_specific = dict(source='', deps='') + +# Yes, the spoolss_notify test uses the NTVFS file server to run the SMB server expected +# to handle the RPC callback! +if bld.CONFIG_SET('WITH_NTVFS_FILESERVER'): + ntvfs_specific['source'] += ' rpc/spoolss_notify.c' + ntvfs_specific['deps'] += ' SMB_SERVER dcerpc_server ntvfs' + +bld.SAMBA_SUBSYSTEM('TORTURE_NDR', + source='''ndr/ndr.c + ndr/dcerpc.c + ndr/winreg.c + ndr/atsvc.c + ndr/lsa.c + ndr/epmap.c + ndr/dfs.c + ndr/netlogon.c + ndr/drsuapi.c + ndr/spoolss.c + ndr/ntprinting.c + ndr/samr.c + ndr/dfsblob.c + ndr/drsblobs.c + ndr/dnsp.c + ndr/nbt.c + ndr/ntlmssp.c + ndr/string.c + ndr/backupkey.c + ndr/witness.c + ndr/clusapi.c + ndr/negoex.c + ndr/krb5pac.c + ndr/winspool.c + ndr/cabinet.c + ndr/charset.c + ndr/svcctl.c + ndr/odj.c + ''', + autoproto='ndr/proto.h', + deps='torture krb5samba', + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.SAMBA_SUBSYSTEM('IREMOTEWINSPOOL_COMMON', + source='rpc/iremotewinspool_common.c', + deps='talloc', + enabled=bld.PYTHON_BUILD_IS_ENABLED()) + +bld.SAMBA_MODULE('torture_rpc', + source=''' + rpc/join.c + rpc/lsa.c + rpc/forest_trust.c + rpc/lsa_lookup.c + rpc/session_key.c + rpc/echo.c + rpc/dfs.c + rpc/drsuapi.c + rpc/drsuapi_w2k8.c + rpc/drsuapi_cracknames.c + rpc/dsgetinfo.c + rpc/spoolss.c + rpc/spoolss_win.c + rpc/spoolss_access.c + rpc/unixinfo.c + rpc/samr.c + rpc/samr_accessmask.c + rpc/samr_handletype.c + rpc/samr_priv.c + rpc/wkssvc.c + rpc/srvsvc.c + rpc/svcctl.c + rpc/atsvc.c + rpc/eventlog.c + rpc/epmapper.c + rpc/winreg.c + rpc/initshutdown.c + rpc/mgmt.c + rpc/scanner.c + rpc/countcalls.c + rpc/testjoin.c + rpc/schannel.c + rpc/netlogon.c + rpc/netlogon_crypto.c + rpc/remote_pac.c + rpc/samlogon.c + rpc/samsync.c + rpc/dssetup.c + rpc/alter_context.c + rpc/bench.c + rpc/samba3rpc.c + rpc/rpc.c + rpc/async_bind.c + rpc/handles.c + rpc/frsapi.c + rpc/object_uuid.c + rpc/ntsvcs.c + rpc/browser.c + rpc/bind.c + rpc/fsrvp.c + rpc/clusapi.c + rpc/witness.c + rpc/iremotewinspool.c + rpc/iremotewinspool_driver.c + rpc/mdssvc.c + rpc/backupkey.c''' + ntvfs_specific['source'], + autoproto='rpc/proto.h', + subsystem='smbtorture', + init_function='torture_rpc_init', + deps=''' + ndr-table + RPC_NDR_UNIXINFO + dcerpc-samr + RPC_NDR_WINREG + RPC_NDR_INITSHUTDOWN + RPC_NDR_EVENTLOG + RPC_NDR_ECHO + RPC_NDR_SVCCTL + RPC_NDR_NETLOGON + RPC_NDR_ATSVC + RPC_NDR_DRSUAPI + RPC_NDR_LSA + RPC_NDR_EPMAPPER + RPC_NDR_DFS + RPC_NDR_FRSAPI + RPC_NDR_SPOOLSS + RPC_NDR_SRVSVC + RPC_NDR_WKSSVC + RPC_NDR_DSSETUP + RPC_NDR_NTSVCS + %s + LIBCLI_AUTH + popt + CMDLINE_S4 + TORTURE_LDAP + TORTURE_UTIL + TORTURE_RAP + service + process_model + RPC_NDR_BROWSER + LIBCLI_DRSUAPI + TORTURE_DFS + RPC_NDR_FSRVP + RPC_NDR_CLUSAPI + RPC_NDR_WITNESS + RPC_NDR_BACKUPKEY + RPC_NDR_WINSPOOL + IREMOTEWINSPOOL_COMMON + printer_driver + RPC_NDR_MDSSVC + mdssvc + ''' % samba_net + ntvfs_specific['deps'], + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED()) + +bld.RECURSE('drs') +bld.RECURSE('dns') + +bld.SAMBA_MODULE('TORTURE_RAP', + source='rap/rap.c rap/rpc.c rap/printing.c rap/sam.c', + autoproto='rap/proto.h', + subsystem='smbtorture', + init_function='torture_rap_init', + deps='TORTURE_UTIL LIBCLI_SMB NDR_RAP LIBCLI_RAP', + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.SAMBA_MODULE('TORTURE_DFS', + source='dfs/domaindfs.c dfs/common.c', + autoproto='dfs/proto.h', + subsystem='smbtorture', + init_function='torture_dfs_init', + deps='TORTURE_UTIL LIBCLI_SMB', + internal_module=True + ) + + +bld.SAMBA_MODULE('TORTURE_AUTH', + source='auth/ntlmssp.c auth/pac.c auth/smbencrypt.c', + autoproto='auth/proto.h', + subsystem='smbtorture', + deps='LIBCLI_SMB gensec auth4 authkrb5 smbpasswdparser torture com_err gensec_ntlmssp CMDLINE_S4', + internal_module=True + ) + +bld.RECURSE('local') +bld.RECURSE('krb5') + +bld.SAMBA_MODULE('TORTURE_NBENCH', + source='nbench/nbio.c nbench/nbench.c', + autoproto='nbench/proto.h', + subsystem='smbtorture', + init_function='torture_nbench_init', + deps='TORTURE_UTIL', + internal_module=True + ) + + +bld.SAMBA_MODULE('TORTURE_UNIX', + source='unix/unix.c unix/whoami.c unix/unix_info2.c', + autoproto='unix/proto.h', + subsystem='smbtorture', + init_function='torture_unix_init', + deps='TORTURE_UTIL', + internal_module=True + ) + + +bld.SAMBA_MODULE('TORTURE_LDAP', + source=''' + ldap/common.c + ldap/basic.c + ldap/schema.c + ldap/uptodatevector.c + ldap/cldap.c + ldap/netlogon.c + ldap/cldapbench.c + ldap/ldap_sort.c + ldap/nested_search.c + ldap/session_expiry.c + ''', + subsystem='smbtorture', + deps='cli-ldap cli_cldap samdb torture ldbsamba CMDLINE_S4', + internal_module=True, + autoproto='ldap/proto.h', + init_function='torture_ldap_init' + ) + + +bld.SAMBA_MODULE('TORTURE_NBT', + source='nbt/query.c nbt/register.c nbt/wins.c nbt/winsbench.c nbt/winsreplication.c nbt/dgram.c nbt/nbt.c', + autoproto='nbt/proto.h', + subsystem='smbtorture', + init_function='torture_nbt_init', + deps='LIBCLI_SMB cli-nbt LIBCLI_DGRAM LIBCLI_WREPL torture_rpc', + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + + +bld.SAMBA_MODULE('TORTURE_NET', + source='libnet/libnet.c libnet/utils.c libnet/userinfo.c libnet/userman.c libnet/groupinfo.c libnet/groupman.c libnet/domain.c libnet/libnet_lookup.c libnet/libnet_user.c libnet/libnet_group.c libnet/libnet_share.c libnet/libnet_rpc.c libnet/libnet_domain.c libnet/libnet_BecomeDC.c', + autoproto='libnet/proto.h', + subsystem='smbtorture', + init_function='torture_net_init', + deps='%s torture_rpc %s' % (provision, samba_net), + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + + +bld.SAMBA_MODULE('TORTURE_NTP', + source='ntp/ntp_signd.c', + autoproto='ntp/proto.h', + subsystem='smbtorture', + init_function='torture_ntp_init', + deps='torture_rpc', + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.SAMBA_MODULE('TORTURE_VFS', + source='vfs/vfs.c vfs/fruit.c vfs/acl_xattr.c', + subsystem='smbtorture', + deps='LIBCLI_SMB TORTURE_UTIL smbclient-raw TORTURE_RAW', + internal_module=True, + autoproto='vfs/proto.h', + init_function='torture_vfs_init' + ) + +TORTURE_MODULES = 'TORTURE_BASIC TORTURE_RAW torture_rpc TORTURE_RAP TORTURE_AUTH TORTURE_NBENCH TORTURE_UNIX TORTURE_LDAP TORTURE_NBT TORTURE_NET TORTURE_NTP torture_registry TORTURE_VFS' + +bld.SAMBA_SUBSYSTEM('torturemain', + source='smbtorture.c torture.c shell.c', + subsystem_name='smbtorture', + deps='torture dcerpc LIBCLI_SMB SMBREADLINE ' + TORTURE_MODULES, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.SAMBA_BINARY('smbtorture', + source=[], + manpages='man/smbtorture.1', + private_headers='smbtorture.h', + deps='torturemain torture popt CMDLINE_S4 dcerpc LIBCLI_SMB SMBREADLINE ' + TORTURE_MODULES, + pyembed=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) + +bld.SAMBA_BINARY('gentest', + source='gentest.c', + manpages='man/gentest.1', + deps='samba-hostconfig samba-util popt CMDLINE_S4 LIBCLI_SMB smbclient-raw param_options' + ) + + +bld.SAMBA_BINARY('masktest', + source='masktest.c', + manpages='man/masktest.1', + deps='samba-hostconfig samba-util popt CMDLINE_S4 LIBCLI_SMB param_options' + ) + + +bld.SAMBA_BINARY('locktest', + source='locktest.c', + # COV_TARGET='test', + #ldflags='--coverage', + #cflags='--coverage', + # GCOV='1', + manpages='man/locktest.1', + deps='popt CMDLINE_S4 samba-util LIBCLI_SMB samba-hostconfig param_options', + ) + +bld.SAMBA_MODULE('TORTURE_DSDB', + source="../../source4/dsdb/common/tests/dsdb.c", + autoproto='dsdb_proto.h', + subsystem='smbtorture', + init_function='torture_dsdb_init', + deps="TORTURE_UTIL samba-util", + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + ) -- cgit v1.2.3